home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 21
/
CD_ASCQ_21_040595.iso
/
dos
/
prg
/
c
/
freedos3
/
source
/
xms
/
xms.asm
< prev
next >
Wrap
Assembly Source File
|
1994-12-28
|
31KB
|
1,329 lines
;==============================================================================
;==
;== Project : 286 XMS driver
;== Module name : HM286.ASM (HM286.SYS)
;== Author(s) : Mark E. Huss (meh@bis.adp.com)
;== Purpose : Simplified HIMEM.SYS replacement
;==
;== Created : 11/5/90
;==
;== Revision history
;== ================
;==
;==============================================================================
; Notes:
;
; * Some routines (e.g. xms_move_xmem) are NOT re-entrant.
;==============================================================================
page 65,132
.286
include xms.inc
;------------------------------------------------------------------------------
ABS0 segment at 0
org 4*15h
int_15_vec label dword
ABS0 ends
;------------------------------------------------------------------------------
CSEG segment
assume cs:CSEG, ds:nothing, es:nothing
segorg:
;
; stock device driver header
;
dd -1 ; pointer to next device
dw 8000h ; character device
dw offset strategy
dw offset interrupt
db 'XMSXXXX0' ; 8 character name
;
; program data
;
even
req_ptr dd ?
old_int_15 dd ?
old_int_2f dd ?
gate_a20 dw AT_gate_a20 ; else ps2_gate_a20
get_a20 dw test_a20 ; else ps2_get_a20
ftable label word
dw xms_get_version ; 0 get XMS version
dw xms_request_hma ; 1 request high mem area
dw xms_release_hma ; 2 release high mem area
dw xms_global_enable_A20 ; 3 global enable a20
dw xms_global_disable_A20 ; 4 global disable a20
dw xms_local_enable_A20 ; 5 local enable a20
dw xms_local_disable_A20 ; 6 local disable a20
dw xms_query_a20 ; 7 query a20
dw xms_query_xmem ; 8 query free extended mem
dw xms_alloc_xmem ; 9 alloc extended mem block (EMB)
dw xms_free_xmem ; A free EMB
dw xms_move_xmem ; B move EMB
dw xms_lock_xmem ; C lock EMB
dw xms_unlock_xmem ; D unlock EMB
dw xms_handle_info ; E get EMB handle info
;
; these are not yet supported
;
; dw xms_bad_fn ; F realloc EMB
; dw xms_bad_fn ; 10 request upper mem block (UMB)
; dw xms_bad_fn ; 11 release UMB
; dw xms_bad_fn ; 12 realloc UMB
ftable_end label word
; These fns won't go into the table (and are also not yet supported!)
; 88 Query any free extended memory
; 89 Allocate any Extended Memory Block
; 8E Get extended EMB handle
; 8F Reallocate any Extended Memory
;
; global descriptor table for int 15h block moves
;
gdt desc <0,0,0,0,0>
base_desc desc <>
src_desc desc <>
dest_desc desc <>
bios_cs desc <>
bios_ss desc <>
maxaddr dw 0,11h ; starts at 110000
_1024 dw 1024 ; for byte <-> kbyte conversions
move_length dw 0
hm_flags db 0
int_mask db 0
.8086 ; could be anything here
;------------------------------------------------------------------------------
strategy proc far
mov cs:req_ptr.lsw,bx
mov cs:req_ptr.msw,es
ret
strategy endp
;------------------------------------------------------------------------------
interrupt proc far
push ax
push cx
push dx
push bx
push si
push di
push ds
push es
mov ax,DD_CMD_ERR ; AX = error
sub bx,bx ; BX = 0 (assume the worst)
lds si,req_ptr
cmp [si].cmd,DD_INIT
jne int_exit
test hm_flags,INIT_DONE
jnz int_exit
call initialize ; only call once!
lds si,req_ptr
int_exit:
mov [si].stat,ax
mov [si].endseg,cs ; END offset = our CS:0000
mov [si].endofs,bx ; 0 or size to keep
pop es
pop ds
pop di
pop si
pop bx
pop dx
pop cx
pop ax
ret
interrupt endp
;------------------------------------------------------------------------------
.286 ; processor checked from here on
int_15 proc far
cmp ah,87h
je int_15_move
cmp ah,88h
je int_15_size
jmp old_int_15
int_15_move:
mov ah,3 ; 'gate a20 failed' error
push bp
mov bp,sp
or byte ptr [bp+6],CARRY_FLAG
jmp int_15_exit
int_15_size:
sub ax,ax ; indicate 0K free
push bp
mov bp,sp
and byte ptr [bp+6],not CARRY_FLAG
int_15_exit:
pop bp
popf_iret proc near ; popf (for B-step 286 bug)
iret
popf_iret endp
int_15 endp
;------------------------------------------------------------------------------
int_2f proc far
cmp ah,43h
je do_int2f
do_old_2f:
jmp old_int_2f
do_int2f:
cmp al,0
jne try_2f_10
mov al,80h
jmp short int_2f_exit
try_2f_10:
cmp al,10h
jne do_old_2f
push cs
pop es
mov bx,offset hm_main
int_2f_exit:
iret
int_2f endp
;------------------------------------------------------------------------------
assume cs:CSEG, ds:CSEG, es:nothing
;------------------------------------------------------------------------------
; hook_int_15 -
; hook int 15h and clear handle table on 1st non-version call
;
; input:
; output:
; modifies:
; AX, CX, DI, ES
;
; Note: currently MUST preserve BX, DX & SI
;------------------------------------------------------------------------------
hook_int_15 proc near
pushf
cli ; no ints now
sub ax,ax
mov es,ax
assume es:ABS0
mov ax,int_15_vec.lsw ; get old vector
mov old_int_15.lsw,ax
mov ax,int_15_vec.msw
mov old_int_15.msw,ax
; set our new int 15h vector
mov int_15_vec.lsw,offset int_15
mov int_15_vec.msw,cs
or hm_flags,INT15_HOOKED
@popf ; ints OK
sub ax,ax ; init handle table to 00
push cs
pop es
;
; hose all but 1st handle to 0
;
mov cx,((MAX_HANDLES-1) * type xms_handle) shr 1
mov di,(offset handle_table - offset segorg) + type xms_handle
cld
rep stosw
ret
hook_int_15 endp
;------------------------------------------------------------------------------
; test_a20 - see if A20 is enabled the hard way
;
; input:
; nil
; output:
; AX = 1 if A20 is enabled, ZF clear
; AX = 0 if A20 is disabled, ZF set
; modifies:
; AX
;------------------------------------------------------------------------------
test_a20 proc near
push si
push di
push ds
push es
mov ax,0FFFFh
mov es,ax ; ES = FFFF
inc ax
mov ds,ax ; DS = 0000
sub si,si
mov di,10h
mov cx,100h ; 0000:0000 vs. FFFF:0010
cld
rep cmpsw
jz ta_off ; ZF set means a20 is off
inc ax ; this will keep the ZF cleared
ta_off:
pop es
pop ds
pop di
pop si
ret
test_a20 endp
;------------------------------------------------------------------------------
; kbd_wait - wait for keyboard controller input ready
;
; input:
; nil
; output:
; nil
; modifies:
; AL
;------------------------------------------------------------------------------
kbd_wait proc near
push cx
sub cx,cx
kw_loop:
in al,KBD_STATUS
test al,KS_INPORT_FULL
loopnz kw_loop ; wait 'til input bit is clear
pop cx
ret
kbd_wait endp
;------------------------------------------------------------------------------
; AT_gate_a20
;
; input:
; AX = 0 --> disable
; else --> enable
; modifies:
; AX
;------------------------------------------------------------------------------
AT_gate_a20 proc near
pushf
cli
test ax,ax
; in al,KBD_DATA
mov al,0DDh
jz aga_disable
or al,KC_A20_BIT
jmp short aga_IO
aga_disable:
and al,not KC_A20_BIT
aga_IO:
or al,KC_RESET_BIT ; don't reset the box!
mov ah,al
call kbd_wait
jnz aga_err
mov al,KC_WR_OUTPORT
out KBD_COMMAND,al
call kbd_wait
jnz aga_err
mov al,ah
out KBD_DATA,al
call kbd_wait
aga_err:
@popf
ret
AT_gate_a20 endp
;------------------------------------------------------------------------------
; ps2_gate_a20
;
; input:
; AX = 0 --> disable
; else --> enable
;------------------------------------------------------------------------------
ps2_gate_a20 proc near
test ax,ax
in al,SYSTEM_CONTROL_PORT_A
jz pga_disable ; else fall thru to enable_a20
or al,A20_ENABLE_BIT
jmp short port_a_out
pga_disable:
and al,not A20_ENABLE_BIT
port_a_out:
out SYSTEM_CONTROL_PORT_A,al
ret
ps2_gate_a20 endp
;------------------------------------------------------------------------------
; enable_a20
; disable_a20
;
; input:
; nil
; output:
; nil
; modifies:
; AX
;------------------------------------------------------------------------------
enable_a20 proc near
mov al,1
jmp gate_a20
enable_a20 endp
disable_a20 proc near
sub ax,ax
jmp gate_a20
disable_a20 endp
;------------------------------------------------------------------------------
; ps2_get_a20
;
; input:
; nil
; output:
; AX = 1 if A20 is enabled, ZF clear
; AX = 0 if A20 is disabled, ZF set
; modifies:
; AX
;------------------------------------------------------------------------------
ps2_get_a20 proc near
sub ax,ax
in al,SYSTEM_CONTROL_PORT_A
and al,A20_ENABLE_BIT
shr ax,1 ; move bit to LSB & set/clear ZF
ret
ps2_get_a20 endp
;------------------------------------------------------------------------------
; segoff_to_physical
;
; input:
; DX:AX = seg:ofs
; output:
; DX:AX = physical 24 bit address (DH = 00)
; modifies:
; AX, CX, DX
;------------------------------------------------------------------------------
segoff_to_physical proc near
mov cx,dx
sub dx,dx
mov dl,ch
shr dx,4 ; DX = seg MSD 000x
shl cx,4 ; CX = seg 3 LSDs xxx0
add ax,cx ; AX = LSW
adc dx,0 ; DX = MSW
ret
segoff_to_physical endp
;------------------------------------------------------------------------------
; get_xmove_addr
;
; input:
; ES:BX -> xmem_sub_move struc
; output:
; CF = 0, DX:AX == physical addr
; CF = 1, error
; modifies:
; AX, (BL), DX
;------------------------------------------------------------------------------
get_xmove_addr proc near
mov dx,es:[bx].xhandle
test dx,dx
jnz gxa_do_handle
mov ax,es:[bx].xaddr_lo ; handle 0 means seg:ofs ptr
mov dx,es:[bx].xaddr_hi
call segoff_to_physical
jmp gxa_exit
gxa_do_handle:
call check_handle
jc gxa_exit
mov ax,[di].xbase
mul _1024 ; faster than 32 bit 10 place shift
add ax,es:[bx].xaddr_lo
adc dx,es:[bx].xaddr_hi
gxa_exit:
ret
get_xmove_addr endp
;------------------------------------------------------------------------------
; check_addr
;
; input:
; ES:SI -> xmem_move struc
; DX:AX = physical 24 bit address (DH = 00)
; if DEBUG
; CF = 1 -> fail all HIMEM addresses
; CF = 0 -> don't check for HIMEM
; output:
; CF = 0, addr ok
; CF = 1, bad addr
; modifies:
; flags only
;------------------------------------------------------------------------------
check_addr proc near
push ax
push dx
if DEBUG
jnc ca_ck_high_side
cmp dx,11h ; check for himem stomp
jae ca_ck_high_side
cmp dx,10h
jb ca_addr_ok
jae ca_addr_err
ca_ck_high_side:
endif
add ax,es:[si].length_lo
adc dx,es:[si].length_hi
cmp dx,maxaddr[2] ; check against maxaddr
jb ca_addr_ok
ja ca_addr_err
cmp ax,maxaddr
jb ca_addr_ok
ca_addr_err:
stc
jc ca_exit
ca_addr_ok:
clc
ca_exit:
pop dx
pop ax
ret
check_addr endp
;------------------------------------------------------------------------------
; find_free_handle - find an unused handle in the handle_table
;
; input:
; CF = 0 --> first time in
; CF = 1 --> re-entering, adjust DI & CX first
; CX = remaining handle count
; DS:DI = handle to look at
; output:
; CF = 0, DI = handle
; CF = 1, DI = ???
; modifies:
; CX, DI
;------------------------------------------------------------------------------
find_free_handle proc near
jcxz ffh_error_exit ; for safety's sake
jc ffh_keep_looking
ffh_loop:
test [di].xflags,XH_IN_USE ; in use?
jnz ffh_keep_looking
cmp [di].xbase,0 ; totally free handle?
jz ffh_exit ; if true, CF = 0
ffh_keep_looking:
add di,type xms_handle
loop ffh_loop
ffh_error_exit:
stc
ffh_exit:
ret
find_free_handle endp
;------------------------------------------------------------------------------
; find_free_memory - find an unused memory block in the handle table
;
; input:
; CF = 0 --> first time in
; CF = 1 --> re-entering, adjust SI & CX first
; DX = desired memory size
; CX = remaining handle count
; DS:SI = handle to look at
; output:
; CF = 0, SI = handle
; CF = 1, SI = ???
; modifies:
; CX, SI
;------------------------------------------------------------------------------
find_free_memory proc near
jcxz ffm_error_exit ; for safety's sake
jc ffm_keep_looking
ffm_loop:
test [si].xflags,XH_IN_USE
jnz ffm_keep_looking
cmp [si].xsize,dx
jae ffm_exit ; CF = 0
ffm_keep_looking:
add si,type xms_handle
loop ffm_loop
ffm_error_exit:
stc
ffm_exit:
ret
find_free_memory endp
;------------------------------------------------------------------------------
; check_handle
;
; input:
; DX = handle
; output:
; CF = 0, DI = handle ptr
; CF = 1, BL = error code
; modifies:
; DX, DI -- must preserve BX & SI --
;------------------------------------------------------------------------------
check_handle proc near
mov di,dx
sub dx,offset handle_table
js bad_handle
cmp dx,(MAX_HANDLES-1) * type xms_handle
ja bad_handle
cmp [di].xbase,0
je bad_handle
clc
ret
bad_handle:
mov bl,ERR_BAD_XHNDL
stc
ret
check_handle endp
;------------------------------------------------------------------------------
; ----- XMS functions -----
;------------------------------------------------------------------------------
; On entry, SS:[BP] -> register stack frame
;
; All functions return:
; CF=0 --> success, AX = returned AX, BL = returned BL (usually 00)
; CF=1 --> error, AX = don't care, BL = returned error code
;------------------------------------------------------------------------------
; get XMS version number (function 00h)
;------------------------------------------------------------------------------
xms_get_version proc near
mov [bp].r_dx,1 ; HMA exists
mov bx,OUR_VERSION
mov [bp].r_bx,bx ; BL = returned BL
mov ax,XMS_VERSION
clc
ret
xms_get_version endp
;------------------------------------------------------------------------------
; Request High Memory Area (function 01h)
; Release High Memory Area (function 02h)
;------------------------------------------------------------------------------
xms_request_hma proc near
mov bl,ERR_HMA_IN_USE
test hm_flags,HMA_IN_USE
jnz rhma_error_exit
or hm_flags,HMA_IN_USE ; & clc
jmp short rhma_good_exit
xms_request_hma endp
;----------------------------------
xms_release_hma proc near
mov bl,ERR_HMA_NOT_ALLOCATED
test hm_flags,HMA_IN_USE
jz rhma_error_exit
and hm_flags,not HMA_IN_USE ; & clc
rhma_good_exit:
mov ax,1
mov bl,0
ret
rhma_error_exit:
stc
ret
xms_release_hma endp
;------------------------------------------------------------------------------
; Global/Local Enable A20 (function 03h/05h)
; Global/Local Disable A20 (function 04h/06h)
;------------------------------------------------------------------------------
xms_global_enable_A20 proc near
mov bl,ERR_A20_GATE
test hm_flags,HMA_IN_USE ; 'global' checks for HMA user
jz fa_error_exit
xms_local_enable_A20:
call enable_a20
jmp short fa_good_exit
xms_global_enable_A20 endp
;----------------------------------
xms_global_disable_A20 proc near
mov bl,ERR_A20_STILL_ENABLED
test hm_flags,HMA_IN_USE
jz fa_error_exit
xms_local_disable_A20:
call disable_a20
fa_good_exit:
mov ax,1
mov bl,0
clc
ret
fa_error_exit:
stc
ret
xms_global_disable_A20 endp
;------------------------------------------------------------------------------
; Query A20 (function 07h)
;------------------------------------------------------------------------------
xms_query_a20 proc near
call get_a20
sub bl,bl ; & clc
ret
xms_query_a20 endp
;------------------------------------------------------------------------------
; Query Free Extended Memory (function 08h)
;------------------------------------------------------------------------------
xms_query_xmem proc near
sub ax,ax ; AX = largest found
sub dx,dx ; find even smallest free block
sub di,di ; DI = total ( & clc)
mov cx,MAX_HANDLES
mov si,offset handle_table
qxm_loop:
call find_free_memory ; SI = memory handle
jc qxm_done
add di,[si].xsize ; update total
cmp ax,[si].xsize ; update largest
ja qxm_mine_is_bigger ; if necessary
mov ax,[si].xsize
qxm_mine_is_bigger:
stc
jmp qxm_loop
qxm_done:
mov [bp].r_dx,di ; return DX = total
test ax,ax ; & clc
jz qxm_empty
mov bl,0 ; AX = largest found
ret
qxm_empty:
mov bl,ERR_OUT_OF_XMEM
stc
ret
xms_query_xmem endp
;------------------------------------------------------------------------------
; Allocate Extended Memory Block (function 09h)
; DX = block size
;------------------------------------------------------------------------------
xms_alloc_xmem proc near
mov cx,MAX_HANDLES
mov si,offset handle_table
clc ; CF = 0 --> first find_free
call find_free_memory ; SI = memory handle
jc axm_error_exit ; out of memory
mov cx,MAX_HANDLES
mov di,offset handle_table ; CF = 0 --> first find_free
axm_find_handle:
call find_free_handle ; DI = blank handle
;
; if memory handle is last unused handle, give
; requestor all remaining memory
;
jc axm_perfect_fit
;
; make sure we have two different handles!
;
cmp si,di
stc ; CF = 1 --> NOT first find_free
je axm_find_handle
;
; found two handles, adjust memory size of memory handle and
; use free handle for left over memory
;
axm_normal_alloc:
xchg [si].xsize,dx ; set size
sub dx,[si].xsize ; DX = left over size
jz axm_perfect_fit
mov ax,[si].xbase ; old xbase + xsize
add ax,[si].xsize ; = new block xbase
mov [di].xbase,ax
mov [di].xsize,dx ; new block size
axm_perfect_fit:
or [si].xflags,XH_IN_USE
mov [bp].r_dx,si ; handle table offset is handle
mov ax,1
sub bl,bl ; & clc
ret
axm_error_exit:
mov bl,ERR_OUT_OF_XHNDL ; this is what HIMEM returns
stc ; (even if really 'out of memory')
ret
xms_alloc_xmem endp
;------------------------------------------------------------------------------
; Free Extended Memory Block (function 0Ah)
;------------------------------------------------------------------------------
xms_free_xmem proc near
call check_handle
jc fxm_error_exit
cmp [di].xlocks,0 ; make sure its unlocked
jne fxm_locked
;
; check for 0 length handle
;
mov ax,[di].xsize
test ax,ax
jnz fxm_len_ok
mov [di].xbase,ax ; 0 len, just clear & exit
jmp fxm_done
;
; normal handle, check for and concatenate any contiguous free blocks
;
fxm_len_ok:
add ax,[di].xbase ; AX = freeing handle xbase+size
sub dx,dx ; any len 0 & up OK (& CF = 0)
;
; check the high side first
;
mov cx,MAX_HANDLES
mov si,offset handle_table
fxm_loop1:
call find_free_memory ; SI = memory handle
jc fxm_hi_done ; CF means no more memory handles
cmp ax,[si].xbase ; free mem right above us?
stc
jne fxm_loop1
;
; found one high, concat
;
mov bx,[si].xsize ; add his size
add [di].xsize,bx ; to ours
mov [si].xbase,dx ; and hose his
mov [si].xsize,dx ; fields to 0000
;
; check the low side next
;
fxm_hi_done:
mov cx,MAX_HANDLES
mov si,offset handle_table
clc
fxm_loop2:
call find_free_memory ; SI = memory handle
jc fxm_done ; CF means no more memory handles
mov bx,[si].xsize
add bx,[si].xbase
cmp bx,[di].xbase ; free mem right below us?
stc
jne fxm_loop2
;
; found one low, concat
;
mov bx,[di].xsize ; add our size
add [si].xsize,bx ; to his
mov [di].xbase,dx ; and hose our
mov [di].xsize,dx ; fields to 0000
fxm_done:
and [di].xflags,not XH_IN_USE
mov ax,1
sub bl,bl ; & clc
ret
fxm_locked:
mov bl,ERR_BLOCK_LOCKED
fxm_error_exit:
stc
ret
xms_free_xmem endp
;------------------------------------------------------------------------------
; Move Extended Memory Block (function 0Bh)
;------------------------------------------------------------------------------
xms_move_xmem proc near
test hm_flags,IN_MOVE ; almost guarantee no re-entrancy
jnz xms_move_xmem ; (tiny window of
or hm_flags,IN_MOVE ; mis-opportunity here)
;
; check length info
;
mov es,[bp].r_ds
mov si,[bp].r_si ; ES:SI -> xmove struc
mov dx,es:[si].length_hi
mov ax,es:[si].length_lo
shr dx,1 ; convert byte to word count
jnz mxm_len_err ; must be <64K
rcr ax,1
jc mxm_len_err ; len must be even
cmp ax,8000h ; word count in AX
jbe mxm_ok ; must be <= 64K bytes
mxm_len_err:
mov bl,ERR_BAD_LEN
jmp mxm_error_exit
mxm_ok:
mov move_length,ax
;
; process source info
;
lea bx,[si].source_handle
call get_xmove_addr
mov bl,ERR_BAD_SRC_XHNDL
jc mxm_error_exit
call check_addr
mov bl,ERR_BAD_SRC_OFS
jc mxm_error_exit
mov src_desc.basel,ax
mov src_desc.baseh,dl
;
; process destination info
;
lea bx,[si].dest_handle
call get_xmove_addr
mov bl,ERR_BAD_DEST_XHNDL
jc mxm_error_exit
if DEBUG
stc ; check HIMEM area too
endif
call check_addr
mov bl,ERR_BAD_DEST_OFS
jc mxm_error_exit
mov dest_desc.basel,ax
mov dest_desc.baseh,dl
;
; get machine critical
;
pushf
cli ; no ints here
in al,PIC_MASK ; REALLY no ints here!
Rest
mov int_mask,al ; save current mask
mov al,0FFh
out PIC_MASK,al ; mask ALL ints
;
; check A20 state, then do the move
;
call get_a20
push ax ; save A20 state
mov cx,move_length ; word count from above
push cs
pop es
mov si,offset gdt ; ES:SI --> GDT
mov ah,87h ; move block
pushf
call old_int_15 ; this will disable a20
rcl cl,1 ; save carry
pop ax
call gate_a20 ; restore a20
cli
mov al,int_mask
out PIC_MASK,al ; restore int mask
@popf ; re-enable ints
test cl,1 ; check for int 15 CF
jnz mxm_gate_exit
mov ax,1
mov bl,0
and hm_flags,not IN_MOVE ; & CLC
ret
mxm_gate_exit:
mov bl,ERR_A20_GATE
mxm_error_exit:
and hm_flags,not IN_MOVE
stc
ret
xms_move_xmem endp
;------------------------------------------------------------------------------
; Lock Extended Memory Block (function 0Ch)
;------------------------------------------------------------------------------
xms_lock_xmem proc near
call check_handle
jc lxm_error_exit
mov bl,ERR_OUT_OF_LOCKS
inc [di].xlocks
jz lxm_lock_wrap
mov ax,[di].xbase
mul _1024
mov [bp].r_bx,ax ; return base address
mov [bp].r_dx,dx ; in DX:BX
mov bl,al ; because BL gets returned
mov ax,1 ; in common exit routine
clc
ret
lxm_lock_wrap:
dec [di].xlocks
lxm_error_exit:
stc
ret
xms_lock_xmem endp
;------------------------------------------------------------------------------
; Unlock Extended Memory Block (function 0Dh)
;------------------------------------------------------------------------------
xms_unlock_xmem proc near
call check_handle
jc uxm_error_exit
mov bl,ERR_BLOCK_NOT_LOCKED
cmp [di].xlocks,0
je uxm_error_exit
dec [di].xlocks
mov ax,1
sub bl,bl ; & clc
ret
uxm_error_exit:
stc
ret
xms_unlock_xmem endp
;------------------------------------------------------------------------------
; Get EMB Handle Information (function 0Eh)
;------------------------------------------------------------------------------
xms_handle_info proc near
call check_handle
jc hi_exit
mov ax,[di].xsize
mov [bp].r_dx,ax
mov bh,[di].xlocks
mov cx,MAX_HANDLES
mov di,offset handle_table
sub bl,bl ; CF = 0 --> first find_free
hi_loop:
call find_free_handle ; DI = blank handle
jc hi_done
inc bl ; bump free handle count
stc
jmp hi_loop
hi_done: ; BL = # of free handles
mov [bp].r_bx,bx ; (BL = returned BL)
mov ax,1
clc
hi_exit:
ret
xms_handle_info endp
;------------------------------------------------------------------------------
; invalid/unsupported function handler
;------------------------------------------------------------------------------
if 0
xms_bad_fn proc near
mov bl,ERR_BAD_FN
stc
ret
xms_bad_fn endp
endif
;------------------------------------------------------------------------------
; main procedure - call far
;------------------------------------------------------------------------------
assume cs:CSEG, ds:nothing, es:nothing
hm_main proc far
jmp short silly
nop
nop
nop
silly: ; goofy recommended start for XMS drivers
pusha
push ds
push es
mov bp,sp ; [bp] -> regframe
push cs
pop ds
assume ds:CSEG
sub bx,bx
mov bl,ah
shl bx,1
test bx,bx
jz skip_int_15_hook ; init function does not hook int 15
cmp bx,ftable_end - ftable
jae hm_bad_fn
test hm_flags,INT15_HOOKED ; if not already hooked..
jnz skip_int_15_hook
call hook_int_15 ; ..hook int 15h
skip_int_15_hook:
call ftable[bx] ; dispatch function
jc fn_error
hm_exit:
mov [bp].r_bx.lsb,bl ; always return BL
mov [bp].r_ax,ax ; and AX
pop es
pop ds
popa
ret
;
; misc. exceptions/errors down here to keep 'normal' flow clean
;
hm_bad_fn:
mov bl,ERR_BAD_FN
fn_error:
sub ax,ax ; AX = 0000
jmp hm_exit
hm_main endp
even
handle_table label byte
;==============================================================================
; init code (this is discarded after initialization)
;
assume ds:CSEG, es:nothing
hello db 13,10,"XMS Driver v ",AVERSION
if DEBUG
db " (Debug) -- entry point $"
endif
crlf2 db 13,10,13,10,"$"
err_ db "XMS Fatal Error: $"
err_re db "An XMS driver is already installed$"
err_pta db "Unable to gain control of A20 line$"
err_mem db "Insufficent extended memory available$"
err_vd db "A VDISK device was detected$"
err_cpu db " -- Processor is not an 80286 or better --"
crlf db 13,10,"$"
vdisk_id db "VDISK"
VDISK_ID_LEN equ $ - vdisk_id
;------------------------------------------------------------------------------
if DEBUG
htoa4 proc near ; print AX as ASCII hex
push ax
xchg al,ah
call htoa2
pop ax
htoa2: push ax
REPT 4
shr al,1
ENDM
call htoa1
pop ax
htoa1: and al,0Fh
add al,90h
daa
adc al,40h
daa
mov dl,al
mov ah,2
int 21h
ret
htoa4 endp
endif
;------------------------------------------------------------------------------
determine_xmemsize proc near
mov ah,88h
int 15h ; use BIOS - why not
jc dx_exit
cmp ax,MIN_XMEM
jb dx_exit ; CF = 1
sub ax,64 ; reserve HIMEM area
;
; initialize first handle
;
mov handle_table.xsize,ax
mov handle_table.xbase,XMEM_BASE
mov handle_table.xlocks,0
mov handle_table.xflags,0
;
mul _1024
add maxaddr,ax ; max allowed ext addr
adc maxaddr[2],dx
clc
dx_exit:
ret
determine_xmemsize endp
;------------------------------------------------------------------------------
test_a20_port proc near
pushf
cli
call enable_a20
call test_a20 ; see if its ON
jz a20_port_failed
call disable_a20
call test_a20 ; see if its OFF
jnz a20_port_failed
@popf
clc
ret
a20_port_failed:
@popf
stc
ret
test_a20_port endp
;------------------------------------------------------------------------------
check_for_vdisk proc near
mov ax,3519h
int 21h ; get int 19h vector
mov di,VDISK_OFS
mov si,offset vdisk_id
mov cx,VDISK_ID_LEN
cld
rep cmpsb ; ZF=1 if found
jz cfd_found
mov ax,0FFFFh ; check FFFF:0013
mov es,ax
mov di,VDISK_OFS+1
mov si,offset vdisk_id
mov cx,VDISK_ID_LEN
cld
rep cmpsb ; ZF=1 if found
cfd_found:
ret
check_for_vdisk endp
;------------------------------------------------------------------------------
if 0 ; future expansion
get_cmd_line_args proc near
push ds
;
; find cmd line params
;
lds si,req_ptr
les di,dword ptr [si].bpbofs ; cmd line ptr
mov bx,di
mov cx,80
mov al,13 ; EOL
cld
repne scasb
; jne error_exit
not cx
add cx,80 ; CX = count to EOL
mov di,bx
mov al,'/'
repne scasb ; look for separator
jne no_params
mov al,es:[di]
or al,' ' ; tolower
cmp al,'x' ; whatever
jne no_params
; ( act on params )
no_params:
pop ds
ret
get_cmd_line_args endp
endif
;------------------------------------------------------------------------------
;
; Determine CPU type
;
; Returns:
; AX = 1 -> 8086 (flags MSD always F)
; AX = 2 -> 80286 (flags MSD always 0)
; AX = 3 -> 80386 (flags MSD changable)
;
get_cpu proc near
mov bx,1 ; assume 8086
pushf ; save original flags
pop ax
push ax ; AX = original flags
and ax,0FFFh ; clear bits 15..12
push ax
@popf ; put AX into flags reg
pushf
pop ax ; AX = modified flags
and ax,0F000h ; clear 3 LSDs
cmp ax,0F000h
je got_cpu_type
inc bx ; = 2 (try 286)
pop ax
push ax ; AX = original flags
or ax,0F000h ; try to set bits 15..12
push ax
@popf ; put AX into flags reg
pushf
pop ax ; AX = modified flags
and ax,0F000h ; are any MSD bits set?
jz got_cpu_type ; if not, its a 286
inc bx ; if so, we have a 386 ( = 3)
got_cpu_type:
@popf ; restore original flags
mov ax,bx
ret
get_cpu endp
;------------------------------------------------------------------------------
initialize proc near
push cs
pop ds
assume ds:CSEG
or hm_flags,INIT_DONE ; indicate init performed
@Print hello
if DEBUG
mov ax,cs
call htoa4
mov dl,':'
mov ah,2
int 21h
mov ax,offset hm_main
call htoa4
@Print crlf2
endif
mov ax,4300h
int 2Fh ; check for re-install
cmp al,80h
jne no_xms_yet
mov dx,offset err_re
jmp error_exit
no_xms_yet:
call check_for_vdisk ; check for VDISK type allocations
jnz no_vdisks
mov dx,offset err_vd
jmp error_exit
no_vdisks:
call get_cpu ; check for 80286
cmp ax,2
jae cpu_ok
mov dx,offset err_cpu
jmp warning_exit
cpu_ok:
call test_a20_port ; make sure our A20 control works
jnc port_ok
; AT failed, try ps2
mov gate_a20,offset ps2_gate_a20
mov get_a20,offset ps2_get_a20
call test_a20_port
jnc port_ok
; both failed, give up
mov dx,offset err_pta
jmp error_exit
port_ok:
call determine_xmemsize ; get available extended memory
jnc memsize_ok
mov dx,offset err_mem
jmp error_exit
memsize_ok:
; call get_cmd_line_args
mov dx,cs
mov ax,offset gdt
call segoff_to_physical
mov base_desc.basel,ax ; set up base_desc
mov base_desc.baseh,dl
mov ax,352Fh ; get old int 2f
int 21h
mov old_int_2f.lsw,bx
mov old_int_2f.msw,es
mov ax,252Fh ; set new int 2f
mov dx,offset int_2f
int 21h
mov bx,offset handle_table ; calculate size to keep
add bx,(MAX_HANDLES * type xms_handle)
mov ax,DD_DONE ; tell DOS we're OK
ret
error_exit:
push dx
@Print err_ ; print error message
pop dx
warning_exit:
@Print
@Print crlf
sub bx,bx ; keep offset = 0000
mov ax,DD_ERROR + DD_DONE ; return error
ret
initialize endp
;------------------------------------------------------------------------------
CSEG ends
end