386POWER PROGRAMMER GUIDELINES SMILEY WARNING: I use "smileys" (the faces drawn with punctuation marks and other chars you must roll your head to the left to see clearly) a lot because i like to write as i talk and i heavily use pronunciation inflection. (read: the things i say, if they are written as they are,sometimes look like an offence or a menace, the smiley correct this). ;-) or ;) == it's a joke, you know i don't mean exactly this. :-) or :) == happy :-} or :} == oops! :-( == i'm not happy of this PG-A386 WARNING: 386POWER HAS BEEN RATED Programmer Grade - Assembly 386 !!!!! ;-) I suppose you know enough of assembly and 386 programming. If you don't, bad luck. PMODE WARNING: Since 386Power is sort of PMODE stepbrother i based this sort of tech manual on the pmode.doc file included into pmode.zip by Thomas "Tran" Pytel, this way it will be easier to get the differences between the two extenders. You might say i mention Tran too many times (or too few times if you are Tran ;) ), fact is that i never read a book about 386 protected mode programming, i just looked into tran docs and played with pmode, then i found the DPMI docs and the Ralph Brown Interrupt List on the internet and decided to try to build 386Power. Anyway, Tran docs together with the pmode sources were the most complete thing i found about protected mode programming with some things you hardly find in the docs (like how to set the pages under VCPI). If you are familiar with pmode, read carefully, there are some things that are similar but a lot different. ------------------------------------------------------------------------------ Who did it: 386POWER (386P for short ) has been conceived and coded by Lorenzo Micheletto (that's me) but is not all "my stuff" (besides mostly is). It is based on the PMODE386 dos-extender by Tran (a.k.a. Thomas Pytel). I used modified algorithms found in PMODE 386 and in some situations i copied some chunks of code (i.e. the interrupt redirectors). I'm not the guy who reinvent the wheel every time (just NEARLY every time ;) ). And now a little fairy tale about rampaging mechs .... Lorenzo Micheletto (that's me), once an 8086 assembly programmer was building a game engine under MS-Dos in the free time to build a super beat 'em up game based on rampaging mechs. ( but sooner or later you will see MechaFighter IV hit the 'net after i make a 'test' game to see what's the limit of 386P ) After realizing 640k couldn't handle all the graph already produced.... ( immagine how much memory is needed to store all the sprites and composed-sprite data for the 150ton MCT-2S MechTerminator when it fires at close range a 500mm ShotCan [ binary barrel short range cannon, sort of grown-up shotgun ] charged with Depleted-Uranium swarm-bullets ) ... i tried PMODE (the release with RAW,XMS,VCPI and DPMI support). But after i discovered some quirks (gasp! my test code crashed in strange ways and i was closely following what Tran wrote in the docs) i first reversed engineered all PMODE to find out what was going wrong then digged in the VCPI and DPMI docs, then changed things here and ... ... restarted from zero. I was shaking the system upside-down a lot,so if something was going wrong, it was hard to me understand if was my fault, pmode fault or an interaction bug. Well, after two months of ripping up PMODE i gave up ( i couldn't read Tran mind, so some things looked like a bug to me but could be just arcane programming, not to mention i HATE code that self- modifies because it easily triggers heuristic antiviral routines) and decided to build a similar thing (i already had translated lots of code from real-mode-286 to protected-mode-386-with-pmode386, so the PMODE API was familiar to me, why change ? ) that i could understand and fix without problems. Starting from zero source lines i ended up with this strange thing: 386POWER, a dos extender interface a lot similar to PMODE (nearly the same API, and some portions of it are copied straight from pmode) with only VCPI and DPMI support. GASP! I started patching a program and ended up with another one! (sort of genetic programming technique ;-) ) 386Power checks if a VCPI or DPMI manager is present (read: it needs EMM386, QEMM or Windows running in 386 Enhanced Mode) and if it finds one it initialize the protected mode environment for 32bit FLAT protected mode. IRQs are active and redirected as expected. ALL interrupt vectors gets copied to the _OldInt table (and restored on program termination) so you don't need to save them. ------------------------------------------------------------------------------ First of all, the memory layout, as you can guess is nearly the same of PMODE: Your program will have at least 3 segments code16 A 16bit segment that holds all the real mode and 16bit protected mode init and exit code. It has to be the first segment of the EXE. code32 The huge 32bit segment. You can throw in as much code as will fit in low memory (no 64k fixup overflows). If you need more code space, you're gonna have to load it into extended memory at runtime and use it there. Under protected mode, addresses (code and data, they're the same memory space) are offset from the beginning of this segment. I'll explain later how to access things outside this segment. codeend This MUST be the last segment in the EXE. It is the base for the stack and low memory allocation. If you want to add other segments insert them between code32 and codeend two things seems to miss ... 1) The STACK... The stack is shared by your protected mode program and real mode calls. This means it can be at most 64k wide (don't worry it's wide enough). The stack region always begins at codeend, and goes on for STACKSIZE paragraphs. (a value declared in 386power.inc), but the stack you can use under a certain context (prot. or real mode) can be at most STACKSLOT paragraphs (more on this later). 2) The HEAP.... There are TWO HEAPS you can allocate memory from at run time. The "Low memory" heap, which covers all conventional memory below A0000h. The "Extended memory" heap, which covers all those nice bytes above 1M. There's a reason for keeping them separate (other than that big hole between A0000h and 100000h (which I did not want to fill in or rearrange with paging) : DOS can only see the low memory heap. So in calls where you have to pass buffer addresses, you must pass only buffers that you have in low memory. Low memory is the place where you would allocate any critical disk or TWIN DMA buffers (more on this later). ------------------------------------------------------------------------------ VIRTUAL REGISTERS You call real mode interrupts and far routines with "virtual registers". Memory images of the registers as they will be set for the real mode int. When the mode-switch occours the current values are pushed into the stack the mode switch occours & the new register values are loaded from the virtual registers (actually just a table of dwords used by int 32h and int 33h mode switch routines) ------------------------------------------------------------------------------ Details of runtime: After initialization of the protected mode environment 386POWER will call a label called _Main located in the code32 segment (so place _Main: where you want to start your code) When _Main is reached you can assume that: 1) The stack is set up in the first slot (more on this later). 2) The interrupts are disabled (and have been all the way from real mode) just in case theres something you want to do first. 3) CS points to the code segment you're running in (code32) thru a selector set up by 386POWER. 4) DS,ES,FS, and SS point to an alias of the code segment (same memory). (In case you don't remember, you can NEVER write to a CS: override in protected mode, only read or execute are allowed in a protectded mode code segment). N.B. Both selectors in 3) and 4) are set with a 4Gigabytes limit soyou can stuff anything you want into them. 5) GS is a segment that's 4Gigabyte wide but starts at absolute 0. This is of course useful for accessing the real mode dos data area, or the BIOS data area, or the PSP, etc... 6) The TYPEMATIC RATE is set to minimal values (this is very useful if you are gonna perform "raw keyboard" input) 7) ALL interrupt vectors are copied to the _OldInt table (they will be restored on interrupt termination) so you can change the dos interrupt vectors without having to be worried to save 'em. 8) If you run under DPMI, 386P tries to lock all memory allocated but doesn't care if it has been successful or not (i do this just to reduce disk swapping to the minimum). If you need locked memory (i.e. for IRQ handling) use the DPMI functions if you detect DPMI (check the _386Man var.). Selectors: There are three main selectors you have to know. _SelCode, _SelData, and _SelZero are 16bit word vars you can access to get the selector values for the code, data, and zero (GS) segments respectively. When 386power pass control to _main you can assume CS=_SelCode, DS=ES=FS=SS=_seldata, and GS=_selzero. You can change the segregs if you wish (for example to do a REP MOVS in the zero seg). But the 386POWER routines and ints expect the segregs to be these values (except for the special case of SS into an ISR). And these MUST be the values when you jump to '_Exit' to return to DOS. Another thing that is assumed by 386POWER is DF=0 (direction flag is clear (like the CLD instruction)). You can perform STD, but before calling 386POWER stuff you have to "re-set" the direction flag with CLD. Linear & relative addresses: All addresses of thing declared in code32 are RELATIVE to the beginning of the code32 segment (which could start anywhere in low memory). nearli all the system code expects addresses that are code32 relative. For this reason, you must adjust any physical memory pointers before you use them. That is, to access something at linear address A0000h (A000:0000, in 16 bit seg:ofs notation) you can use GS (it is loaded with the _SelZero selector) and write GS:0A0000h. If you want to write to A0000h using the default data segment it will not be at DS:A0000, but at DS:(A0000h - linear_address_of_the_beginning_of_Code32). And this linear_address_of_the_beginning_of_Code32 is stored in a variable called '_Code32Base'. So if the segment code32 is 1F43, the linear address will be 1F430h To get a pointer to A0000h, you would do something like: mov eax,0A0000h sub eax,_Code32Base This function is provided as a macro in 386power.inc (copied straight from pmode together with the protected mode ISR handlers :} ) together with the macro to go the other way, relative address to linear. Of course, if you address something with GS (assuming GS is _SelZero) you can use the actual linear address. The linear address for code16 is also provided in _Code16Base. As well as the linear address of the PSP in '_PSPBase'. The linear addresses of code16 and PSP will always be less than code32. To access them (memory pointed to by them, these vars are in code32), you will have to use one of two methods. One is easy enough, just use the GS segment. Or you could use negative indexes from the normal segment, causing a 4Gbyte wraparound (a lot like the 64kbyte wraparound under real mode). However it is rare to access things outside code32, so my advice is to use GS to make things clear and as a remainder of things "done outside code32". Memory where is my memory? As for the memory. You have two heaps, into low and high (extended) memory. Each of which is guaranteed to be at least as much as you specified in LOWMIN and EXTMIN in 386power.inc, the startup code will grab all low memory for you (because it's meant to run standalone), and it will attempt to grab all the high memory it can. Two dword vars hold information about each memory area. _LoMemBase and _LoMemTop specify the base and top of the low memory pool as relative addresses (ready to use, no adjustment needed). The total amount of low memory available in bytes is _LoMemTop - _LoMemBase (notice _LoMemTop points to one byte beyond the last available byte). The _GetLoMem routine is a very simple routine that takes a length in EAX and checks to see if there is enough low memory. If there is enough, it adds the length to _LoMemBase and returns a pointer (code32 relative, ready to use) in EAX to the low memory block along with the carry flag clear. If it finds not enough memory, it returns with the carry flag set. _HiMemBase, _HiMemTop, and _GetHiMem are the same thing for high memory. Then there is the _GetMem routine that first tries to allocate low memory and if this fails it tries high memory. Calling real (actually quite virtual) mode: You can call real mode, and back. This is only provided so that you can call real mode interrupts, and routines that you can't recode in protected mode. Keep these cross-mode calls to the minimum because they eat lots of cpu time and can raise some nasty interaction bugs. You can call real mode interrupts or procedures from protected mode through INT 32h (call real mode far proc), and INT 33h (call real mode int). These interrupts are only available to the PROTECTED MODE part of your program. In REAL MODE, there is a ANOTHER INT 32h that you can use to call a protected mode routine. Don't confuse the two INT32s with each other, though they do basically the same thing BUT THEY ARE NOT THE SAME THING AND THEY DO DIFFERENT THINGS. The "int 32h from real mode" doesn't use virtual registers, no register parameter passing is possible it is available just to implement sort of callback system. To pass register values from protected mode to real mode and back you use 'virtual registers'. These 'virtual registers' are merely memory images of EAX,EBX,ECX,EDX,ESI,EDI, EBP,DS,ES,FS,and GS. AL and AH and AX and BL ... etc ... are there too, and they share the appropriate memory space with each other so if you change the 'virtual' AH register, the 'virtual' AX and EAX registers will be changed accordingly. There are no SS,ESP,CS,EIP registers. CS:EIP is taken from the real mode int vector table for int calls, and passed in the real CX:DX registers for a procedure call. SS:ESP is set up by 386POWER together with the extended memory manager providing protected mode services. @ INT 33h from prot. mode: Do a real mode interrupt. AL=interrupt you want to do. All V86??? general and segment registers will be passed to the real mode handler. They will also be passed back as the return values into the virtual register. The carry, zero, aux, parity, sign, and overflow flags will be passed back as the actual CPU flags. The real mode interrupt will be called with interrupts disabled (as it is usually). Keep in mind, no CPU registers will be modified (except the flags mentioned). Only their V86??? images will be changed by the real mode int handler. @ INT 32h from prot. mode: Call a real mode far procedure. CX:DX=seg:off you want to call. The register passing works just like INT33. Except that the interrupt flag will be preserved across the call to real mode (but not back, the IF flag will be in the same state as it was before the int). @ INT 32h from real mode: Call a pmode procedure. EDX=off. A 32bit offset in the code32 segment. The register passing works just like for the other INT32 in pmode, except that register (real or virtual they are) ARE NOT PASSED TO OR FROM the prot. mode routine. Upon entry to the routine, the system standard things are set. That is: CS=_SelCode, DS=ES=FS=SS=_SelData, GS=_SelZero, DF=0 (CLD). And they must be that when the thing executes its RET (not RETF). But registers and flags won't be passed as happens on the int32 from prot. mode. (the real mode int32 is left as a quick way to implement callbacks and just these) DPMI and IRQs: Upon startup, all the interrupt vectors for IRQs point to routines that redirect the IRQs to their default real mode handlers. You can hook into any IRQ you want. There are two dword pointers that allow you to get and set IRQ vektorz. _GetIRQ and _SetIRQ point to routines to get and set the relative address of the handler for specific IRQs within the code32 segment. To get the address of a handler, just do a 'call _GetIRQ' with BL set to the IRQ num you want (0-15). EDX will be returned pointing to its current handler. To set an IRQ, pass BL again as the IRQ number, and EDX as the offset of the new handler. You can chain to the old handler if you want just by jumping to the old address when your handler is done processing. When your IRQ handler is called, you can be sure of ONLY TWO THINGS. The IF flag is clear and CS is loaded with _SelCode. All the general regs and segregs should be treated as undefined. Even SS cannot be trust, because under DPMI it is set to a little "interrupt stack" set up by DPMI. In other words ... DON'T CHANGE STACK!!!! Another consideration for DPMI is the IF flag. According to DPMI specs, only CLI, STI, and INT 31h functions AX=900h and AX=901h should be counted on to modify the interrupt flag (POPF(D) and IRET(D) should not). This is because certain DPMI systems might have to virtualize the interrupt flag, and keep the real flag enabled at all times (but don't worry, if the 'virtual' flag is clear, your program will not get any IRQs). In practice, certain DPMIs do allow IRET(D)s and POPF(D)s to modify the virtual interrupt flag. But this is inconsistent across them. So you should follow these rules: @ CLI and STI are allowed, and do their functions. @ Don't assume anything about POPF(D) and IRET(D) and the interrupt flag. @ Don't assume the interrupt flag PUSHF(D) stores on the stack is correct, it might be the real flag or the 'virtual' flag. @ These DPMI INT 31h functions are supported under VCPI too (by way of the 386POWER interface). ) AX=900h: Get state of IF and disable it. Returns AL set to the IF flag. ) AX=901h: Get state of IF and enable it. Returns AL set to the IF flag. ) AX=902h: Only returns AL set to the IF flag (0=disabled, 1=enabled). @ At the end of an IRQ handler, put a STI. When the handler is called, flags are automatically disabled. If you do not reenable them, and neither does the IRETD... Well... under some systems it will run and on other it will hang. The stack: 386POWER uses one major stack for both pmode and real mode. This stack is always located in low memory (always locked under DPMI). The size of the stack is set as STACKSIZE in 386power.inc . There is another value there called STACKSLOT, this is what must be explained. When a mode switch occurs, the new stack is the old stack base minus STACKSLOT paragraphs. The stack base is the stack location when your program starts. And it is only modified by mode switches. That is the stack uses STACKSIZE paragraphs and it is "sliced" into slots STACKSLOT wide. Your main program uses just one stack "slot" and when a mode switch occours another "slot" is used as stack, then returning from the mode switch the stack becomes the old "slot". Into each slot you can safely allocate local variables on the stack referencing them with esp ad ebp without having to worry of mode switches or reflected irqs triggering a mode switch. Every time you switch mode a new slot is used and when you "get back" to the caller, the caller "slot" becomes the stack. So if STACKSIZE = 4000 and STACKSLOT=1000 your main program cau use up to 1000 bytes If a mode switch occours, a news stack is set up and it is wide 1000 bytes too. Let's make an example: Immagine the following "chunks of code" A is the main program B is a real mode routine C is a real mode timer driven interrupt service routine D is a protected mode routine To describe the stack status we will use the following notation: XYZ 123--- a stack structure with six slots and slots 1,2,3 "in use" where 3 is the "active stack" and is in use by the Z subroutine 1] A is running A 1----- 2] A calls B (mode switch) AB 12---- 3] B calls D (mode switch) ABD 123--- 4] C triggered by timer interrupt (unexpected mode switch you cannot control) ABDC 1234-- 5] interrupt handler returns ABD 123--- 6] D returns AB 12---- 7] B returns A 1----- As you can guess, you'd better keep enough slots available because if too many mode switch "accumulates" the stack structure will break. Follow this rule of the thumb: slots = maximum_number_of_cross_mode_switches + maximum_number_of_irq_can_nest_between_mode_switches The best way to minimize IRQ problems is the use of TWIN IRQ handlers (two different IRQ ISR routines, one coded for real mode, the other coded for protected mode, this way you are sure no mode switches will happen because of interrupts triggered by not-deterministic events). If you really have to switch mode, keep the Interrupt flag disabled to prevent "unexpected irq stackings". Under DPMI things are slightly different but the rule "the less mode switches, the better" is still true. DPMI handles stack switching on its own, any IRQ causes a switch to a totally different stack provided by DPMI (argh!), this is the main reason i had to stick with the "slotted stack" model as in pmode and keep a var to point to the "next slot" to support int32h/33h in protected mode from within interrupts. Under real mode irqs and interrupts, if you want to call the "old" inteerupt handler, instead of having to store the address somewhere every time, just call the equivalent entry stored into the _OldInt table (it's the same table used by INT 33h from protected mode). To summarize: @ In an IRQ handler, DON'T switch off the stack it is entered with. Which is not guaranteed that SS=_seldata. @ Don't do more nested calls across modes than (STACKSIZE/STACKSLOT)-1. (-2 be totally safe). @ You CAN safely assume SS=_seldata in protected mode only in your main stream of execution, and in routines that are called with INT32 from real mode. @ Consider your maximum effective stack size to be STACKSLOT. @ You CAN call across modes using INT32/33 from an IRQ handler in both real and protected mode, but remember this takes time (giving IRQs enough time to stockpile if they are fast enough ). @ Under real mode IRQ, if you want to call directly the "old" interrupt just perform the following actions: pushf call dword ptr cs:[offset _OldInt + INT_NUM*4] Exceptions: Are handled entirely by the DPMI host in those systems. In VCPI, exceptions 0-5 and 7 are reflected to real mode just like IRQs would be. Exceptions 8 and 9-1fh cause immediate termination (maybe in future releases i will include some debug messages when terminating due to an exception). ------------------------------------------------------------------------------ Potential DMA problems: As you know, the DMA controllers in the PC use all physical addresses. Nothing but the processor itself knows how linear memory is arranged in the physical memory banks. When paging is disabled, the relationship is very simple. The linear address is always the same as the physical address. But when you enable paging, that could get all screwed up. Under VCPI and DPMI paging IS enabled. (bad luck, uh?) You can almost definately count on extended memory addresses not being consistent with their physical addresses. Low memory however, will usually map perfectly to its physical addresses. Usually i said, NOT always. The point is that you shouldn't use "raw" DMA under VCPI and DPMI. To handle DMA you can try to make tricky things or you can follow the Virtual DMA Specification (VDS). This is the recommended way of handling DMA under VCPI and DPMI. See the Ralph Brown interrupt list for INT 4Bh for more info. A future release of 386P will include VDS support thru standard routines as soon as i find the best way to handle some quirks. ------------------------------------------------------------------------------ And now to discuss some of the finer points of different protected mode environments: VCPI: VCPI is nearly "raw" mode. The CPL is 0, and there is nothing scrutinizing the execution of your code. Paging is enabled, but if you want you can mess directly with page physical addressed. The problem comes with the way VCPI compatibility works. To call a real mode interrupt or procedure, we have to pass protected mode control back to the VCPI server. This comes out to one thing. IRQs that occur in a real mode call will NOT make it to your protected mode handler. It's just the way VCPI works. Under VCPI the real mode interrupt table and the protected mode interrupt table belongs to different Task Segment Descriptors. @ Do the IRQ handler in real mode. That way, it will always be called no matter what is in control. But this seems to defeat the purpose of protected mode. And if this is a timing critical IRQ, you have a problem because passing control from a program (prot. mode) to the VCPI server to execute the real mode IRQ callback takes a bit of time. Not a terrible amount, but it is a delay. @ Do the IRQ handler in protected mode, and keep real mode calls to a minimum. For example, disable all but the critical IRQs to your program. And try to handle as many as you can in pmode. (You can read the keyboard direct from the hardware can't you. And you do know how to output FFh to A1h and FDh to 21h). But remember one thing... When you go to do a real mode call (DOS file call or something else you can't do yourself). Whatever the hardware cause of your IRQ will still be active. And if an IRQ occurs in real mode, and there is no real mode handler for it. Well, you know... So you either put in some valid real mode handler that may merely set a flag that you have an IRQ to service. Or disable the source of the IRQ (mask it off at 21h or A1h). @ Use TWO irq handlers (one in real mode and one in protected mode ) to handle the same data structure to pass data to/from the main program. It is harder to code but a lot faster that "switchy" stuff. ------------------------------------------------------------------------------ DPMI: DPMI is not that bad. It could be a little more consistent across its implementations and let you select CPL0 for "trusted" applications. I don't like the overhead imposed by CPL3 (in CPL3, certain instructions have to be emulated by software). Multitasking virtual machines in general are not that hot when you're trying to do a timing critical action game. One really annoying problem with DPMI is that current implementations are far from perfect. QDPMI 1.01 for example, dies when an IRQ occurs in a prot. mode call from a real mode IRQ. DPMI docs say this shouldn't happen, and it doesn't under Windows 3.1 DPMI implementation. The Windows 3.1 DPMI is a little better but has lethal quirks (try to make coexist paging and Virtual Dma Support if you don't believe me). While testing the 386power alpha release the DPMI startup used to go bonkers doing quite funny things, then i discovered that the "installation check" function wrongly reported DPMI 1.0 as the server while Windows 3.1 supports DPMI 0.9 ... Gasp! My code was optimized to take advantage of the extra function provided by DPMI 1.0 if present and so ... Hmm, another little problem is that I'm not sure how many DPMIs out there actually reflect IRQs to real mode if they occur in protected mode. Windows 3.1 seems to send them all over as it should. According to Tran QDPMI 1.01 sends IRQ1, but not IRQ0. And it also doesn't seem to pass IRQs that occur in real mode through their protected mode handlers, while Windows 3.1 does. If you code for VCPI, you must support interrupt reflection by yourself :-(. Under DPMI, the dpmi server should reflect interrupts for you (as happens under Win 3.1) but if it doesn't ... you can't bypass it to reflect things yourself (damn!). I tried to do it, but strange things happened, my antivirus started yelling like a maniac about unbeliavable viral attacks ( programs trying to become resident thru interrupt handling routines and tunneling from CPL3 to CPL0 ) and the dmpi server got nuked. ------------------------------------------------------------------------------ Some misc notes: @ Under VCPI, 386Power will map as much extended memory as it can, up to 64M without allowing the page tables to use up more memory than would leave LOWMIN. Allocating up to 64M means that extended memory under VCPI can be at most 63M (even if there is more available). @ Before exiting your program, you do NOT need to restore any vectors (protected or real mode) nor the typematic rate. And you do not have to restore the IRQ masks at 21h and A1h (PMODE stores them before jumping to _main, and restores them before exiting). BUT if you reprogrammed the irq8/int70h timer interval, set it back to its initial value (the irq0/int8 is automatically set back to 18.2 Hz by the _Exit routine). @ If you're gonna add other 16bit segments, put them in between code32 and codeend. @ Remember that upon reaching '_Main', interrupts are still disabled. Don't forget to do the STI. INTERSEGMENT ACCESS: Sometimes some variables declared in Code32 are accessed from Code16 (eech!) as bad as it can be, it saved me lots of segment swapping. I learned this trick looking into PMODE386 and decided to use it because even if it causes quite an headache to follow the code it saves from the problems you run into when you swap segment register and mode frequently, but remember the following rules: a) When you do this, it is better to use a segment override to indicate what segment you are using. i.e if in code16 you write DS:V86eax it means DS default segment ( == code16) and offset of V86eax (located into code32) CALCULATED FROM CODE16!!!! So be aware of where you declare data and from where you reference it. b) If you put too much stuff in code16 or code32 forcing the code accessed thru intersegment to use offsets beyound 64k you'll get a "hidden" protection violation (some assemblers don't detect it!!!!!) So if something strange happens, you know where to look at first. if you look into 386P remember: d16_ means DPMI stuff in code16 d32_ means DPMI stuff in code32 v16_ means VCPI stuff in code16 v32_ means VCPI stuff in code32 s16_ means MIXED stuff in code16 s32_ means MIXED stuff in code32 MIXED == shared between VCPI/DPMI xor "dos-exending" code ------------------------------------------------------------------------------ Heres a list of the vars provided by PMODE to your program: _LoMemBase:dword Low mem base for allocation (first free byte). _LoMemTop:dword Top of low mem (last free byte +1). _HiMemBase:dword High mem base for allocation (first free byte). _HiMemTop:dword Top of high mem (last free byte +1). _PSPBase:dword Absolute linear address of start of PSP. _Code16Base:dword Absolute linear address of start of code16. _Code32Base:dword Absolute linear address of start of code32 (32bit code offset from this). _SelCode:word Code segment selector. _SelData:word Data segment alias selector for code. _SelZero:word Data segment starting at absolute 0. _386man:byte 386 manager flags, they have the following meanings (in the future they may also contain information about unified DMA services, timer services and more). bits: 0: 0=VCPI, 1=DPMI 1-7: undefined _GetIRQ:dword A pointer to the get IRQ function appropriate for the mode. The function takes arguments as follows: In: BL == IRQ num (0-0fh) Out: EDX == offset of current IRQ handler _SetIRQ:dword A pointer to the set IRQ function. In: BL == IRQ num (0-0fh) EDX == offset of new IRQ handler to set _OldInt:table of 256 dwords LOCATED IN THE CODE16 SEGMENT It contains all the original real mode int vectors found at 386P startup. Instead of storing "old" real mode interrupts yourself 386P does it for you. To chain to an old int just jump or pushf&call to the equivalent entry in _OldInt. ------------------------------------------------------------------------------ And now some 'functions'. Remember, they ALL need: CS=_SelCode, DS=ES=FS=_Seldata, GS=_SelZero, DF=0 (CLD). _GetMem: Allocate any mem, (first checks low, then high) In: EAX == size requested Out: CF=0 memory allocated CF=1 not enough mem EAX == relative pointer to mem or undefined if not enough _GetLoMem: Allocate some low mem In: EAX == size requested Out: CF=0 memory allocated CF=1 not enough mem EAX == relative pointer to mem or undefined if not enough _GetHiMem: Allocate some high mem In: EAX == size requested Out: CF=0 memory allocated CF=1 not enough mem EAX == linear pointer to mem or undefined if not enough _GetIRQMask: Get status of IRQ mask bit (at port 21h or A1h) In: BL == IRQ num (0-15) Out: AL == status: 0=enabled, 1=disabled _SetIRQMask: Set status of IRQ mask bit In: BL == IRQ num (0-15) AL == status: 0=enabled, 1=disabled _Exit: Exit to real mode, restore initial IRQ mask restore timer 0 frequency to 18.2 Hz restore ALL interrupt vectors and get out setting video mode 03 (color text 80x25) ------------------------------------------------------------------------------ PROGRAM SUPPORT I'm not going to go commercial with 386Power and its companion code. Maybe i will go commercial with the games i will produce with it (i'm already working on a "Commander Keen like" game) Anyway i will distribuite the sources of this and ALL the future releases of 386power and its companion code (386video, and all the other mode-x stuff). If 386Power doesn't work on your system i'd like to know (so i can fix it) you can reach me at the addresses listed in the end. Call me if you have suggestions about new improvements and things like that. BUT REMEMBER, 386power is NOT TOTALLY FREE (gosh!) If you use 386P "as is",you are NOT allowed to remove the messages displayed by the 386power startup code AND if you use it remember to include me and Tran (i owe it to him) in the list of who devenloped the software. If you modify 386P (not just cosmetic adjustments of course) you should use a different name (to avoid confusion) BUT you have to include almost the following message "based on the 386Power dos-extender by Lorenzo Micheletto MCHLNZ67T19C890A and on the PMODE dos-extender by Thomas 'Tran' Pytel". What's more if you make a commercial thing with it .... ... send me a free copy :). I won't ask anything more from you. It is not a free lunch ... it is a nearly free lunch. The main reason i coded 386P was for pure fun and get skilled about protected mode programming before coding the real game (if i just wanted a dos-extender i would have bought one from FlashTek). I distribuite it because: (in priority order) a) Needed a wider test base (just my 386 and the 486s' of some friend is a too restricted test base) b) I owe it to all the people who distribuited their sources on the net and let me learn how to handle the pc hardware and more. c) Need to get a reputation as a programmer, sooner or later when i will try to publish/sell my games it could be useful. ------------------------------------------------------------------------------ Final things: 386Power has been designed with a restricted goal to reach (kick my code into 32bit protected mode :} and be sure that on program termination no bad things will happen). You are free to use it and change it any way you want, but please don't make protected mode viruses or trojan horse, i really hate those losers that think they are smart because they can destroy things. If you think you are smart, design and code a terrific piece of software. And as i said before 386P is limited to VCPI and DPMI (if you call it a limit). If you want a "real" dos-extender capable to handle anything contact the FlashTek guys and buy it or search the latest pmode release by Tran (Thomas Pytel) on internet. To reach FlashTek try to ask about them in the rec.games.programmer internet newsgroup. To find Tran, do the same or ... ....as far i know on may 1993 he could be reached on Creativity Demo Net or SBCnet as 'Tom Tran' or at the Sound Barrier: (718)979-6629, (718)979-9406 or on Internet: tran@phantom.com If you want to contact me (for bug reports or other things) you can reach me .... On the internet as: knight@arianna.dei.unipd.it or knight@maya.dei.unipd.it or knight@paola.dei.unipd.it (Yeah! Multiple logins! I'm moving from a server to another, so you'd better send to both ) By plain mail at the following address: Lorenzo Micheletto Via Piazza Miega 10/A Veronella (VERONA) ITALY 37040 N.B. Maybe in 1995 i will be reachable only by plain mail ( i will be in the army for a year ) but after that i will be back to Padova.