540 lines
10 KiB
Plaintext
540 lines
10 KiB
Plaintext
|
|
; 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
|