; flat assembler interface for DOS ; Copyright (c) 1999-2022, Tomasz Grysztar. ; All rights reserved. segment modes use16 real32: mov ax,7202h push ax popf pushf pop bx cmp ax,bx je processor_ok call init_error db 'required 80386 or better',24h processor_ok: mov eax,ds shl eax,4 mov [program_base],eax mov eax,buffer shl eax,4 sub eax,[program_base] mov [buffer_address],eax if UNREAL_ENABLED>0 smsw ax test al,1 jnz dpmi mov eax,cs ; calculate linear address of GDT shl eax,4 or dword [cs:real32_GDT+10],eax or dword [cs:real16_GDT+10],eax add [cs:real32_GDT_address],eax add [cs:real16_GDT_address],eax cli lgdt [cs:real32_GDTR] mov eax,cr0 or al,1 mov cr0,eax jmp 1 shl 3:test_pm32 no_rm32: sti jmp dpmi test_pm32: use32 mov eax,cr0 and al,not 1 mov cr0,eax mov ebx,0FFFFh jmp modes:test_rm32 test_rm32: inc ebx jz short no_rm32 lgdt [cs:real16_GDTR] mov eax,cr0 or al,1 mov cr0,eax jmp 1 shl 3:test_pm16 test_pm16: use16 mov eax,cr0 and al,not 1 mov cr0,eax jmp modes:test_rm16 test_rm16: sti mov bx,(400h+(100h*interrupt.size)) shr 4 mov ah,48h int 21h jc not_enough_memory push ds es mov es,ax push cs pop ds movzx eax,ax shl eax,4 mov [real32_IDT_base],eax mov dx,100h xor bx,bx mov di,400h init_interrupts: mov si,interrupt mov [si+interrupt.vector],bx mov word [es:bx],di mov word [es:bx+2],es mov cx,interrupt.size rep movsb add bx,4 dec dx jnz init_interrupts pop es ds call modes:switch_real32 use32 mov [mode],real32 retfw use16 switch_real32: pushfw push eax push word ds push word es push word fs push word gs cli mov eax,ss mov cr3,eax lgdt [cs:real32_GDTR] mov eax,cr0 ; switch to protected mode or al,1 mov cr0,eax jmp 1 shl 3:pm32_start pm32_start: use32 mov ax,2 shl 3 ; load 32-bit data descriptor mov ds,ax ; to all data segment registers mov es,ax mov fs,ax mov gs,ax mov ss,ax mov eax,cr0 ; switch back to real mode and al,not 1 mov cr0,eax jmp modes:pm32_end pm32_end: mov eax,cr3 mov ss,ax lidt [cs:real32_IDTR] pop word gs pop word fs pop word es pop word ds pop eax popfw retfw switch_real16: pushfw push eax cli lgdt [cs:real16_GDTR] mov eax,cr0 ; switch to protected mode or al,1 mov cr0,eax jmp 1 shl 3:pm16_start pm16_start: use16 mov eax,cr0 ; switch back to real mode and al,not 1 mov cr0,eax jmp modes:pm16_end pm16_end: lidt [cs:real16_IDTR] pop eax popfw retfd use32 interrupt: call modes:switch_real16 use16 movzx esp,sp push word [esp+4] push cs call .real16 pushfw pop word [esp+4] call modes:switch_real32 use32 iretw .real16: use16 push eax push ds xor ax,ax mov ds,ax mov eax,[word 0] label .vector word at $-2-interrupt pop ds xchg eax,[esp] retfw .size = $-interrupt label real32_GDTR pword real32_GDT_limit dw 3*8-1 ; limit of GDT real32_GDT_address dd real32_GDT ; linear address of GDT real32_GDT rw 4 ; null descriptor dw 0FFFFh,0,9A00h,0CFh ; 32-bit code descriptor dw 0FFFFh,0,9200h,08Fh ; 4 GB data descriptor label real16_GDTR pword real16_GDT_limit dw 2*8-1 ; limit of GDT real16_GDT_address dd real16_GDT ; linear address of GDT real16_GDT rw 4 ; null descriptor dw 0FFFFh,0,9A00h,0 ; 16-bit code descriptor label real32_IDTR pword real32_IDT_limit dw 3FFh real32_IDT_base dd ? label real16_IDTR pword real16_IDT_limit dw 3FFh real16_IDT_base dd 0 end if dpmi: mov ax,1687h int 2Fh or ax,ax ; DPMI installed? jnz no_dpmi test bl,1 ; 32-bit programs supported? jz no_dpmi mov word [cs:mode_switch],di mov word [cs:mode_switch+2],es mov bx,si ; allocate memory for DPMI data mov ah,48h int 21h jc not_enough_memory mov ds,[environment_segment] mov es,ax mov ax,1 call far [cs:mode_switch] ; switch to protected mode jc no_dpmi mov cx,1 xor ax,ax int 31h ; allocate descriptor for code mov si,ax xor ax,ax int 31h ; allocate descriptor for data mov di,ax mov dx,cs lar cx,dx shr cx,8 or cx,0C000h mov bx,si mov ax,9 int 31h ; set code descriptor access rights mov dx,ds lar cx,dx shr cx,8 or cx,0C000h mov bx,di int 31h ; set data descriptor access rights mov ecx,main shl ecx,4 mov dx,cx shr ecx,16 mov ax,7 int 31h ; set data descriptor base address movzx ecx,word [esp+2] shl ecx,4 mov dx,cx shr ecx,16 mov bx,si int 31h ; set code descriptor base address mov cx,0FFFFh mov dx,0FFFFh mov ax,8 ; set segment limit to 4 GB int 31h mov bx,di int 31h mov ax,ds mov ds,di mov [psp_segment],es mov [environment_segment],ax mov es,di mov [mode],dpmi pop ebx movzx ebx,bx push esi push ebx retfd init_error: push cs pop ds mov dx,init_error_prefix mov ah,9 int 21h pop dx int 21h mov dx,init_error_suffix int 21h mov ax,04CFFh int 21h init_error_prefix db 0Dh,0Ah,'error: ',24h init_error_suffix db '.',0Dh,0Ah,24h mode_switch dd ? not_enough_memory: call init_error db 'not enough conventional memory',24h if UNREAL_ENABLED>0 no_dpmi: smsw ax test al,1 jz no_real32 call init_error db 'system is in protected mode without 32-bit DPMI services',24h no_real32: call init_error db 'processor is not able to enter 32-bit real mode',24h else no_dpmi: call init_error db 'no 32-bit DPMI services are available',24h end if use32 if UNREAL_ENABLED>0 init_real32_memory: mov ax,4300h ; check for XMS int 2Fh cmp al,80h ; XMS present? je xms_init mov ax,0E801h ; check for large free extended memory int 15h jnc large_raw_memory mov ah,88h ; how much extended memory free? int 15h or ax,ax jz no_extended_memory movzx eax,ax ; convert AX kilobytes to pointer shl eax,10 jmp use_raw_memory large_raw_memory: movzx ecx,cx shl ecx,10 movzx edx,dx shl edx,16 mov eax,ecx add eax,edx use_raw_memory: add eax,100000h sub eax,[program_base] mov [memory_end],eax push ds push 0 ; DS := 0 pop ds call enable_a20 ; enable A20 call test_a20 ; is A20 enabled? jz a20_ok pop ds jmp no_extended_memory a20_ok: lds bx,dword [4*19h] mov eax,100000h ; initial free extended memory base cmp dword [bx+12h],'VDIS' ; VDISK memory allocation? jne short no_vdisk ; if present, get base of free memory mov eax,dword [bx+2Ch] ; get first free extended memory byte add eax,0Fh ; align on paragraph and eax,0FFFFF0h ; address is only 24bit no_vdisk: push 0FFFFh ; DS := FFFFh for ext mem addressing pop ds cmp dword [13h],'VDIS' ; VDISK memory allocation? jne short vdisk_ok ; if present, get base of free memory movzx ebx,word [2Eh] ; get first free kilobyte shl ebx,10 cmp eax,ebx ; pick larger of 2 addresses ja short vdisk_ok mov eax,ebx vdisk_ok: pop ds sub eax,[program_base] mov [memory_start],eax mov edx,[memory_setting] shl edx,10 jz extended_memory_ok mov eax,[memory_end] sub eax,[memory_start] sub eax,edx jbe extended_memory_ok sub [memory_end],eax jmp extended_memory_ok enable_a20: call test_a20 ; is A20 already enabled? jz a20_enabled ; if yes, done in al,92h ; PS/2 A20 enable or al,2 out 92h,al call test_a20 ; is A20 enabled? jz a20_enabled ; if yes, done call kb_wait ; AT A20 enable jnz a20_enabled mov al,0D1h out 64h,al call kb_wait jnz a20_enabled mov al,0DFh out 60h,al call kb_wait a20_enabled: retn kb_wait: ; wait for safe to write to 8042 xor cx,cx .loop: in al,64h ; read 8042 status test al,2 ; buffer full? loopnz .loop ; if yes, loop retn test_a20: ; test for enabled A20 mov al,[0] ; get byte from 0:0 mov ah,al ; preserve old byte not al ; modify byte xchg al,[100000h] ; put modified byte to 0FFFFh:10h cmp ah,[0] ; set zero if byte at 0:0 not modified mov [100000h],al ; restore byte at 0FFFFh:10h retn ; return, zero if A20 enabled xms_init: push es mov ax,4310h ; get XMS driver address int 2Fh mov word [cs:xms_proc],bx ; store XMS driver address mov word [cs:xms_proc+2],es pop es mov ah,3 ; enable A20 call call_xms cmp ax,1 ; error enabling A20? jne no_extended_memory mov ah,88h ; get free extended memory size (XMS 3.0) xor bl,bl call call_xms or bl,bl jz xms_large_init mov ah,8 ; get free extended memory size xor bl,bl call call_xms or bl,bl jnz no_extended_memory mov dx,ax movzx eax,ax shl eax,10 mov [memory_end],eax mov ah,9 ; allocate largest memory block xms_allocate: mov ecx,[memory_setting] shl ecx,10 jz xms_size_ok cmp ecx,[memory_end] jae xms_size_ok mov [memory_end],ecx mov edx,[memory_setting] xms_size_ok: call call_xms mov [cs:xms_handle],dx cmp ax,1 jne no_extended_memory mov ah,0Ch ; lock extended memory block call call_xms cmp ax,1 jne no_extended_memory shl edx,16 mov dx,bx sub edx,[program_base] mov [memory_start],edx ; store memory block address add [memory_end],edx jmp extended_memory_ok xms_large_init: mov edx,eax shl eax,10 mov [memory_end],eax mov ah,89h ; allocate largest memory block (XMS 3.0) jmp xms_allocate call_xms: call modes:switch_real16 use16 call far dword [cs:xms_proc] call modes:switch_real32 use32 retn no_extended_memory: xor eax,eax mov [memory_start],eax extended_memory_ok: mov ah,48h ; get free conventional memory size mov bx,-1 int 21h movzx ecx,bx shl ecx,4 mov ah,48h ; allocate all conventional memory int 21h movzx edi,ax shl edi,4 sub edi,[program_base] mov [additional_memory],edi mov [additional_memory_end],edi add [additional_memory_end],ecx cmp [memory_start],0 je only_conventional_memory mov eax,[memory_end] sub eax,[memory_start] shr eax,2 cmp eax,ecx jbe real32_memory_ok mov eax,[memory_end] mov ebx,[memory_start] sub eax,ebx shr eax,2 mov [additional_memory],ebx add ebx,eax mov [additional_memory_end],ebx mov [memory_start],ebx real32_memory_ok: retf only_conventional_memory: shr ecx,2 ; use part of conventional memory add edi,ecx ; as a substitute for extended memory mov [memory_start],edi xchg [additional_memory_end],edi mov [memory_end],edi retf free_real32_memory: cmp [cs:xms_handle],0 je no_xms mov ah,0Dh ; unlock extended memory block mov dx,[cs:xms_handle] call call_xms mov ah,0Ah ; free extended memory block call call_xms no_xms: retf xms_proc dd ? ; XMS driver pointer xms_handle dw ? ; handle of XMS memory block end if