Hi, I wrote a small include file which allows one to write 32 bit .com programs with 32mb of pre-allocated memory under windows XP. Not sure if it will work under vista or windows 7. The code is minimalistic and DPMI is outdated but comments and advice are welcome. Many thanks.
; minimalistic include file for DPMI under windows XP
; include at start of .com program for 32 bit com file with 32mb memory allocated
; to exit use ret to exit, or call exit
;
; on entry
; esi/edi points to ~32mb of free memory
;
; e.g.
; include 'dpmi.inc'
; mov edx,hello
; mov ah,9
; int 21h
; ret
; hello: db 'Hello from Protected mode!$'
org 100h
;----------- Routine to enter protected mode ---------------------------
free_unused_memory:
; mov bx,sp
; shr bx,4
; inc bx
; mov ah, 4Ah ;INT 21,4A Modify allocated ES memory block to BX new size in paragraphs
; int 21h ;On error: CF set, AX error code, BX maximum possible size if AX=8
; mov bp,bx
; sub bp,4 ;allocate 4 paragraphs in stack for DPMI host
; sub sp,64
mov bp,(65536-256)/16
get_pmode_entry: ;INT 2F,1687h return protected mode entry point in ES:DI
mov ax,1687h ;AX 0 if success, BX bit 0 set if 32-bit supported
int 2Fh ;CL CPU type (02 = 286, 03 = 386, 04 = 486...)
or ax,ax ;DH DPMI major version, DL minor ver
jnz error ;SI memory paragraphs needed by DPMI host
; test bl,1
; jz error
has_dpmi_host:
push es
push di ;DPMI switch is called with
mov es,bp ; ES allocated segment for DPMI host
mov bx,sp
inc ax ; AX bit 0 = 1 if 32 bit program
call far [bx]
jc error ;On error CF set, program in real mode.
;Else we are in protected mode
;CS: base of real mode CS, 64K limit
;SS: base of real mode SS, 64K limit
;DS: base of real mode DS, 64K limit
;ES: base of PSP, 256 byte limit (NOT setup to its real mode segment!)
;FS and GS = 0 (if 386+)
;High word of ESP 0, all other registers preserved
entered_pmode:
allocate_memory:
mov ax,0501h
mov bx,(32*1024*1024) shr 16 ;allocate 32mb (default maximum that Vista will allocate for DPMI)
sub cx,cx
int 31h ;INT 31/0501h allocates BX:CX bytes of memory
mov [mem_handle_si],si ;return SI:DI memory handle for resizing and freeing block
mov [mem_handle_di],di ; BX:CX = linear address
jc error ;CF set if error
set_base_address:
mov dx,cx
mov cx,bx
mov bx,es
mov ax,7 ;INT 31/0007 set CX:DX segment base address for BX selector
int 31h ;CF set on error AX = error code
set_segment_limit:
mov dx,-1
mov cx,-1
inc ax ;INT 31/0008 set CX:DX segment limit for BX selector
int 31h
set_granularity:
mov cx,1100111111110011b
inc ax ;INT 31/0009 Set CX access rights for BX selector
int 31h
duplicate:
sub di,di ;copy program to memory block
sub si,si
mov cx,sp
rep movsb ;edi, esi = after stack
mov ds,bx ;single segment for DS, SS, ES
mov ss,bx
mov cx,1100111111111011b
mov bx,cs ;switch on 32 bit code
int 31h
;Descriptor details
;byte bit
;7 BASE 31..24
;6 7 (G) 1 = page granular (segment limit = LIMIT*4096)
; 6 (D) 1 = default is 32 bit code/esp
; 5 (L) 1 = 64 bit segment (long mode)
; 4 AVaiLable for use by operating system = 0
; 3..0 LIMIT 19..16
;5 7 (P) 1 = present in memory.
; OS sets P=0 when it caches the segment to disk, so access
; sends an exception; OS then loads the segment and sets P=1.
; 6..5 Descriptor Privilege Level. Highest=ring 0. Lowest=ring 3.
; 4 0 = system descriptor, 1 = code/data descriptor
; TYPE Data descriptor Code descriptor
; 3 0 Data 1 Executable
; 2 0 Expand up, 1 down 0 Non-conforming, 1 conforming
; 1 0 Read-only, 1 R/W 0 Execute only, 1 readable
; 0 (A) Set upon segment access so OS can determine the frequency
; of use and if it can be cached to disk.
;4..2 BASE 23..0
;1..0 LIMIT 15..0
use32
call main
exit: mov ah,1 ;ask for user input to flush previous DOS calls (otherwise won't display previous output)
int 21h
mov si,0 ;INT 31/0502h free SI:DI memory handle (otherwise will crash NTVDM next time we run pgm)
mem_handle_si = $-2
mov di,0
mem_handle_di = $-2
mov ax,0502h
int 31h
error: mov ah,4Ch ;quit
int 21h
;-------------- 32 bit user code goes here -------------------
main: