Hi folks, this is Tran... This text explains START32, my 386 protected mode header... I'm assuming youre an assembler programmer. If you are not, this thing will do you no good. I'm also assuming you have some experience, and knowledge of the 386 wouldn't hurt either... If you don't know the 386, I suggest you get a good book if you really want to code it... First of all: ------------- I coded START32 for myself, it is designed and optimized for my needs. The greatest of which is the need for speed... That is the reason I do not implement paging in this thing, too slow... Also, since the only real mode INTs I usually need are DOS file functions, the V86 handler is not perfect, but it does DOS and BIOS just fine. What it does: ------------- START32 is a piece of code that handles most of the things associated with using and getting into and out of 32bit protected mode. It also allows your protected mode programs to access the regular real-mode DOS and BIOS functions. START32 will check mundane little things like the processor and memory, then if the system is 386+ and not already in protected mode, it will switch the system into 32bit protected mode. In protected mode the system will run in flat memory, that is you won't need to bother with segment registers, and large data arrays will be much easier to handle... How the system works: --------------------- After START32 initializes protected mode, it will jump to a label '_main', which mush be declared public in your code. This is the starting point of your program. All of the code and data in your program will reside in one large segment called 'code32' ... this is a 4gig segment. You do regular code using the 386 instruction set. To access a real-mode interrupt function, you load up 'virtual registers' with the values you want passed to the interrupt, then you load up AL with the number of the interrupt you wanna call, then you call INT 30h. The system will use the 386's v86 mode to call the real mode int. After which, the values in the 'virtual regs' will be what the interrupt handler passed back (All the real registers are preserved). Selectors: ---------- Instead of physical segment pointers, in protected mode, the 386 uses selectors. Selectors are indexes into a table of data that describe the memory location the specific index points to. But you need not concern yourself with these tables (GDT, IDT, LDTs)... This thing takes care of them. All you need to know, is that there are 3 basic selectors available to you. 8, 10h, and 18h... 8 is the selector for the code segment, this is the 4gig segment which you put all your flat code into. 10h is the selector for the data segment, the memory is physically the same as the code segment, but it must be done this way. That is 8 and 10h point to the same large segment, but CS must use 8, and the data segregs must use 10h... 18h is a data selector that points to the beginning of memory. 8 and 10h both start at the beginning of the segment 'code32' in memory, wherever it may be. But 18h always starts at absolute physical adress 0000:0000... This is in case you want to access something that resides before the beginning of the 32bit code, like the BIOS data area or the PSP... Upon arrival at '_main', CS ofcourse is 8. DS, ES, FS, and SS are 10h. And GS is 18h. And you should keep them this way because START32, whose job is by far not done after it passes control to your code, expects it this way. IRQs: ----- START32 handles IRQs for both the real system, and your protected mode code. By default, all the IRQ functions point to a piece of code in START32 which redirects them to their real-mode handler. You can set your own protected mode IRQ handlers, and control will pass DIRECTLY to them when they are called. There are four possible scenarios between the type of IRQ handler and the mode the IRQ was called in. The handling procedure can be the original real mode handler, or your own protected mode handler. And the IRQ can occur in protected mode, or when the system is servicing a V86 real mode INT. For this reason, your protected mode IRQ handler should make no assumption about the state of the system. That is, upon entry your handler, the only things you can be sure of, is CS=8, and SS=10h... You must set the other segregs manually if you wish to use them. Also, you must remember to terminate your IRQ routine with an 'IRETD' instead of an 'IRET'. If you wish to call the original real mode handler from your protected mode handler, simply call the real mode int associated with that IRQ from your handler. BTW: No real mode interrupt vektor table entries are modified by START32, all the IRQs go through the IDT. Also: START32 will mask off IRQ 0 and 1 when it first starts. The system timer and keyboard IRQs, because there is really no use for them untill you put them to use. Memory: ------- Like I said, the whole system uses flat memory when you use START32. There are 2 types, low memory - below 640k, and high memory - above 1M... START32 does some very basic 'memory management', if you could call it that. You can request blocks of a certain size, and START32 will either give ya a pointer to the block, or tell ya theres not enough of that type of memory. The blocks are allocated from the bottom up. You cannot free a memory below an area that you allocated later. You CAN free memory though, simply by decreasing the 'base of memory' pointer START32 uses to track the current base of usable memory. This pointer (along with a few others) is made available to your program. One good way of using this in a function, is along with any registers you PUSH, you also PUSH the pointer to the type of memory you wanna use. After the function is done and there is no more use for the memory it has allocated, you POP the pointer. If you need any memory area permanently throught the program, you should allocate it at the beginning, and ofcourse never decrease the pointer beyond this area. Crashes: -------- One thing about coding protected mode, you do not have the use of a debugger in finding bugz... Youre gonna have to make due with your own traps and internal debug aids... But a crash in protected mode is not as bad as in real mode. START32 handles the 386 exceptions. If any type of exception occurs, START32 will stop the program, do a debug dump of the exception number, registers, and stack ... and quit to DOS... The system will very rarely lock up, and the DOS environment will hardly ever be screwed up... Some reasons are that like I said, no real mode interrupt vektors are modified, and any area below the beginning of your program is usually only accessible through GS. This means that when your code goes astray, it cant overwrite DOS of the int vect table, or any TSRs below it, or anything. And there is hardly ever anything that can be screwed up above a program. An exception will usually occur very soon after a screwup in your code. Two of the most common exceptions are: 6 - an invalid opcode. Either something overwrote your code, or you code jumped off into limbo... 0dh - a general protection violation. Many things can cause this. The system itself: ------------------ First of all, START32 expects: CS=8, DS=ES=SS=FS=10h, GS=18h, and the direction flag cleared when you call any of its functions. In the included source code, at the top, the values are: LOWMIN - the minimum amount of free low memory (minus the code) that has to be present for START32 to allow the program to run. EXTMIN - the minimum amount of extended memory that has to be present for START32 to allow the program to run. (START32 will also quit if it finds less than 638k total low memory). TSTAK - the total stack space for START32 to give. The default value of 400h bytes is usually enough, unless you plan to do recursion. ISTAK - the amount of stack space to be given to a protected mode IRQ handler when an IRQ comes from V86 mode. (This also includes START32's exception handlers). START32 detects and will not run if a VGA is not present. This is not some system critical thing, but like I said, I wrote START32 for myself, and since I dont code anything that doesnt require a VGA right now, this is technically part of the system for me. START32 makes these variables available to your program: _totalextmem:word - The total amount of extended memory in the system (in K). _code16a:dword - Offset of the beginning of the program from absolute 0. This starts 100h bytes above the PSP. _code32a:dword - Offset of the beginning of protected mode code from absolute 0. This is useful for creating realative pointers to physical locations. That is if you wanted a pointer to the beginning of the VGA grafix area, realative to DS, move 0A0000h into a reg, then subtract _code32a from it. theres a macro provided to create such pointers, @rlp. _hextbl:bytes - Simply '0123456789ABCDEF'. It is used during an exception dump, but since it's there, might as well make it public since its so useful in printing hex numbers. _lomembase:dword - Current base of low memory (as a realative pointer, like all the other regular pointers in your code)... this is the beginning of free low memory that has not been allocated. _lomemtop:dword - Top of low memory as a realative pointer. _himembase:dword - Base of high memory, just like _lomembase. _himemtop:dword - Guess... v86r_ax, v86r_bx, v86r_cx, v86r_dx, v86r_si, v86r_di, v86r_bp, v86r_ds, v86r_es, v86r_ah, v86r_al, v86r_bh, v86r_bl, v86r_ch, v86r_cl, v86r_dh, v86r_dl, v86r_flags These are the 'virtual registers' you use to communicate with real-mode ints... v86r_flags though is used only to pass BACK flags, flags are not set to it before invoking the V86 int... Here are some 'functions': ------------------------------------------------------------------------------ _exit - Exit to real mode. ------------------------------------------------------------------------------ _ret - Simply points to a RET instruction. ------------------------------------------------------------------------------ _getvect - Get interrupt vektor, vektors 0-1fh and 30h are reserved, 31h+ dont exist, but 20h-2fh are IRQ's 0-0fh. In: BL - interrupt number Out: EAX - 32 bit offset in code ------------------------------------------------------------------------------ _setvect - Set interrupt vektor. In: BL - interrupt number EAX - 32 bit offset in code ------------------------------------------------------------------------------ _getlomem - Get a low memory block. In: EAX - size requested Out: CF=1 - not enough mem (carry flag set) EAX - ? CF=0 - memory allocated (carry flag clear) EAX - linear pointer to mem ------------------------------------------------------------------------------ _gethimem - Get a high memory block. In: EAX - size requested Out: CF=1 - not enough mem EAX - ? CF=0 - memory allocated EAX - linear pointer to mem ------------------------------------------------------------------------------ _getmem - Allocate any mem (first cheks low, then high). In: EAX - size requested Out: CF=1 - not enough mem EAX - ? CF=0 - memory allocated EAX - linear pointer to mem ------------------------------------------------------------------------------ _lomemsize - Get amount of low mem free. Out: EAX - number of bytes free ------------------------------------------------------------------------------ _himemsize - Get amount of high mem free. Out: EAX - number of bytes free ------------------------------------------------------------------------------ _putdosmsg - Put a '$' terminated DOS string. In: EDX -> message (MUST be in low mem). How to use it: -------------- It's very simple, you compile START32. Compile with multiple passes to resolve forward references. Then you link it with your other 32bit modules. START32 must be the first program in the link list. Try compiling and linking the example programs, then run them to make sure it runs on your system. The thing will just color the screen and pop up some numbers, press ESC to return to DOS. Some quickie tips on the 386 instruction set: --------------------------------------------- (This isn't anything new to those of you already familiar with it) First the extended registers: EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP,EIP are all 32bit registers. (No you can't access EIP directly)... The bit shift instructions, SHL, SHR, RCL, ect... accept immediate values greater than 1 now. This means 'SHL AX,5' is legal. The addressing mode has been extended. You can use a combination of ANY 2 of the 8 general purpose instructions and an immediate value in addressing. Also one of the registers can be multiplied by 2,4, or 8 right in the address field of the instruction youre using. So 'MOV BX,[EAX+ECX*4+78]' is legal. BTW: This is really good because it allows you to use the 'LEA' instruction to perform quick multiplies. Example: 'LEA EAX,[EAX*4+EAX]' is EAX=EAX*5. A version of the 'IMUL' instruction doesn't require preloading registers other than the one(s) you wanna multiply... examples: IMUL EAX,71 ; EAX=EAX*71 IMUL EAX,ECX,33 ; EAX=ECX*33 You can 'PUSH' immediates... 'PUSH 17983' I don't feel like explaining the double shift SHLD/SHRD instructions or any of the BT instructions, so if you don't know em, get a book... ------------------------------------------------------------------------------ Anyways, play with this thing. If you like the flat memory and stuff then youre free to use this thing in whatever code you do, PD, shareware, commercial, or whatever... Any questions or anything, you can contact me on the Sound Barrier BBS (718)979-6629. L8r...