.model small .386 .stack .data dpmistartaddr dd 0 thegdt LABEL BYTE GDTENTRY MACRO base,limit,type dw limit AND 0ffffh dw base AND 0ffffh db (base SHR 16) AND 0ffh dw type OR ((limit SHR 8) AND 0f00h) db base SHR 24 ENDM GDTENTRY 0,00ffffh,0C09Ah ; 386 code (1M limit) GDTENTRY 0,00ffffh,0C092H ; 386 data (1M limit) msg db "Hello, we are in protected mode now!",10,13,'$' d386 segment public para use32 'DATA' msg2 db "hello, this is a 32-bit segment",10,13,'$' d386 ends c386 segment public para use32 'CODE' assume cs:c386 assume ds:c386 start386: mov edx,offset msg2 mov ah,9 int 21h retf c386 ends .code assume ds:_DATA start: mov ax,DGROUP mov ds,ax call resizearena ; reset the memory arena to minimum size jc nodpmi mov ax,1687h ; function for detecting DPMI int 2fh or ax,ax jnz nodpmi ; exit if AX !=0 (no DPMI) mov [dpmistartaddr+2],es mov word ptr [dpmistartaddr],di ; save the DPMI entry point ; at this point the low bit of bx should be 1 ; if you intend to use a 32-bit segment ; else 32-bit isn't supported. You probably don't ; need to check unless you are running a VERY old ; computer and OS. test bl,1 jz nodpmi ; now we may have to allocate some space ; for DPMI to use as a scratch area. SI will ; be the size in paragraphs mov bx,si or bx,bx jz nohostdata mov ah,48h int 21h jc nodpmi mov es,ax ; we have allocated successfully, put seg in ES nohostdata: mov ax,1 ; tell it we are going to use 32-bit segments ; even though we aren't in this example call [dpmistartaddr] ; see if DPMI system will initialize jc nodpmi ; failed, still in real/v86 mode so exit ; at this point we are in a 16-bit PMODE code segment which ; has been aliased to our original CS ; the stack has been aliased to pmode (64K), ; DS has been aliased to pmode (64K) ; ES holds a pmode descriptor for the PSP (100h bytes) ; at this point we would create whatever PMODE segments we ; need (e.g. 32-bit flat segments) by calling appropriate ; DPMI functions and branch to a 32-bit ; code segment. Note that this is a protected environment ; and you still do not have complete access to the x86 ; pmode system; in particular to change the GDT or IDT ; you should go through appropriate DPMI functions. DPMI ; does not allow you to fiddle with paging at all. ; I'm just going to print a message and exit mov dx,offset msg movzx edx,dx mov ah,9 int 21h ; note that many int 21h functions may be reused directly ; from any DPMI segment. However it is NOT universal; ; there are some int 21h functions that require extra work. ; basically as long as the function does not deal with pointers ; you are safe; if it does deal with pointers then it may ; or may not be supported. The rule is, the more use it ; has in general the more likely to be supported... thus ; most file handle functions are supported but some directory ; functions are not. Don't expect memory allocation to ; work at all... ; If you run across an int 21h ; function that does not work, there is a DPMI function that ; will allow you to get around that. ; the windows version of DPMI translation libraries seems ; to use the whole of EDX for this function, so, zero ; the upper half of the pointer offsets when in 16-bit ; segments. ; now create the 32 bit CS and DS sub ax,ax ; function to get selectors mov cx,2 ; we want two descriptors int 31h ; DPMI int jc theerr ; get out if can't do it ; now we have CS and DS, but have to change the attribs, ; base and limit. ; now patch our GDT entries with the base addresses we need ; mov bx,ax mov ax,SEG c386 movzx eax,ax shl eax,4 mov word ptr [thegdt + 2],ax shr eax,16 mov [thegdt + 4],al mov ax,SEG d386 movzx eax,ax shl eax,4 mov word ptr [thegdt + 2 + 8],ax shr eax,16 mov [thegdt + 4 + 8],al ; now set the DPL bits to our current DPL ; mov ax,cs and ax,3 and bx,0fffch or bx,ax push bx ; shl ax,5 or byte ptr [thegdt + 5],al or byte ptr [thegdt + 5 + 8],al ; ; now blit the contents of the code descriptor into the LDT push ds pop es mov di,offset thegdt movzx edi,di mov ax,0ch int 31h pop bx jc theerr push bx ; the CS selector ; now point at the data segment selecotr mov ax,3 ; get selector increment value int 31h add bx,ax push bx ; the DS selector ; now blit the contents of the data descriptor into the LDT mov di,offset thegdt + 8 movzx edi,di mov ax,0ch int 31h pop bx ; DS selector pop cx ; CS selector jc theerr ; now put the return address for coming back here on the ; stack push ds ; save DS for later mov ax,offset returnpoint movzx eax,ax push 0 ; will be returning from 32-bit seg push cs push eax ; will be returning from 32-bit seg ; now put the address we want to jump to on the stack mov edx,offset start386 push cx push dx ; the far ret is from a 16-bit segment ; so the offset must be clear in the upper 16 bits or ; this method won't work. ; now load up the 32-bit data seg mov ds,bx ; and branch to the 32-bit code retf ; we come here when we exit the 32-bit segment returnpoint: pop ds theerr: mov ah,4ch int 21h ; the above exit happened while in PMODE... this particular ; dos-like function does not go straight to dos but goes ; to the DPMI cleanup routines which clean up the ; DPMI context and then exit you back to DOS. nodpmi: ; here we are exiting in real mode because something failed. mov ah,4ch int 21h resizearena PROC ; we have to resize the arena in case our pmode program wants ; to execute another DOS program or allocate memory. ; ; we are going to take advantage of the fact that most linkers will ; put the stack last if there is no BSS. mov bx,ss mov ax,es sub bx,ax mov ax,sp add ax,15 shr ax,4 add bx,ax mov ah,4ah int 21h ret resizearena ENDP end start