mirror of
https://github.com/SEPPDROID/DR-DOS-OpenDOS.git
synced 2025-10-22 16:04:20 +00:00
3608 lines
89 KiB
Plaintext
3608 lines
89 KiB
Plaintext
; File : $GENERCFG.A86$
|
|
;
|
|
; Description :
|
|
;
|
|
; Original Author : DIGITAL RESEARCH
|
|
;
|
|
; Last Edited By : $CALDERA$
|
|
;
|
|
;-----------------------------------------------------------------------;
|
|
; Copyright Work of Caldera, Inc. All Rights Reserved.
|
|
;
|
|
; THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
|
|
; PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
|
|
; ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
|
|
; WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
|
|
; THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
|
|
; HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
|
|
; AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
|
|
; AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
|
|
; COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
|
|
; CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
|
|
; TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
|
|
; CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
|
|
; AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
|
|
; CIVIL LIABILITY.
|
|
;-----------------------------------------------------------------------;
|
|
;
|
|
; *** Current Edit History ***
|
|
; *** End of Current Edit History ***
|
|
;
|
|
; $Log$
|
|
;
|
|
; GENERCFG.A86 1.51 93/12/02 00:15:06
|
|
; When auto-sizing DEVICEHIGH requirements use EXE header info if present
|
|
; GENERCFG.A86 1.49 93/11/29 14:10:29
|
|
; Add NUMLOCK=ON/OFF support
|
|
; GENERCFG.A86 1.48 93/11/28 15:32:22
|
|
; Support HIBUFFERS in UMB's
|
|
; GENERCFG.A86 1.47 93/11/22 15:45:11
|
|
; Ignore [COMMON] statements
|
|
; GENERCFG.A86 1.46 93/11/18 16:20:16
|
|
; Add primitive multi-master checking
|
|
; GENERCFG.A86 1.45 93/11/17 20:07:17
|
|
; change history defaults
|
|
; GENERCFG.A86 1.44 93/11/16 14:10:21
|
|
; F5 has precedence over ?
|
|
; GENERCFG.A86 1.42 93/11/08 17:37:06
|
|
; Move INSTALLHIGH to before INSTALL, so it's now recognised
|
|
; GENERCFG.A86 1.41 93/11/04 16:28:08
|
|
; Cosmetic change - the preload name field now strips off "C:\" properly
|
|
; GENERCFG.A86 1.40 93/11/03 22:52:14
|
|
; Replace chardev test with one for zero units
|
|
; GENERCFG.A86 1.39 93/11/03 18:21:16
|
|
; disable CHARDEV test until it gets fixed
|
|
; GENERCFG.A86 1.38 93/11/03 17:11:05
|
|
; Preloaded compression drivers maybe loaded from C: when booting
|
|
; from a diskette.
|
|
; GENERCFG.A86 1.37 93/10/29 19:42:51
|
|
; DOS=UMB turns HIDOS on
|
|
; GENERCFG.A86 1.36 93/09/28 19:54:52
|
|
; Support "DEVICE?=" syntax, and "SWITCHES=/N"
|
|
; GENERCFG.A86 1.35 93/09/02 22:35:16
|
|
; Add header to system allocations
|
|
; GENERCFG.A86 1.32 93/08/02 14:46:21
|
|
; hide preload drives from func_device
|
|
; support INSTALLHIGH
|
|
; GENERCFG.A86 1.30 93/07/29 14:47:15
|
|
; Change SETVER method
|
|
; GENERCFG.A86 1.28 93/07/20 22:32:07
|
|
; default upper memory link = FFFF
|
|
; ENDLOG
|
|
|
|
|
|
include config.equ
|
|
include i:msdos.equ
|
|
include i:char.def
|
|
include i:reqhdr.equ
|
|
include i:driver.equ
|
|
include i:fdos.equ
|
|
include i:f52data.def ; Function 52 DOS Data Area
|
|
include i:doshndl.def ; DOS Handle Structure Definition
|
|
include i:country.def
|
|
|
|
TRUE equ 0FFFFh ; value of TRUE
|
|
FALSE equ 0 ; value of FALSE
|
|
|
|
F5KEY equ 3F00h ; keys returned by BIOS
|
|
F8KEY equ 4200h ; in boot_options
|
|
|
|
CGROUP group INITCODE, INITDATA, INITENV
|
|
|
|
INITCODE CSEG PARA 'INITCODE'
|
|
|
|
extrn whitespace:near
|
|
extrn build_cmd_tail:near
|
|
extrn device_init:near
|
|
extrn init_static_request:near
|
|
extrn block_device:near
|
|
extrn device_insert:near
|
|
if not ADDDRV
|
|
extrn nls_hook:near
|
|
extrn nls_unhook:near
|
|
endif
|
|
|
|
CONFIG_ERRLVL equ 400h ; special 'CONFIG' error level
|
|
|
|
; config_process returns CF set if an error occurred during command file
|
|
; processing for ADDDRV.EXE
|
|
|
|
Public config_process
|
|
config_process: ; Process CONFIG.SYS
|
|
|
|
mov save_sp,sp ; save SP for unstructured GOSUB's
|
|
|
|
if ADDDRV
|
|
mov error_flag,0
|
|
else
|
|
mov ax,offset envstart ; env buffer is para aligned
|
|
mov cl,4
|
|
shr ax,cl ; convert offset to paras
|
|
mov cx,ds
|
|
add ax,cx ; segment of env variables
|
|
les bx,drdos_ptr
|
|
mov es:DRDOS_ENVSEG[bx],ax ; tell COMMAND.COM where we are
|
|
push ds ! pop es
|
|
endif
|
|
|
|
mov ah,MS_DRV_GET ; check whether we are loading from
|
|
int DOS_INT ; drive C:
|
|
cmp al,2
|
|
je preload_security
|
|
push ds
|
|
mov ah,32h ; get DOS parameter block
|
|
mov dl,3 ; ensure drive C: is valid (no critical
|
|
int DOS_INT ; errors)
|
|
pop ds
|
|
cmp al,0ffh ; skip if the drive is not valid
|
|
je preload_stacker
|
|
mov alt_drive,1 ; drive C: should be used also
|
|
jmp preload_stacker
|
|
|
|
preload_security:
|
|
call preload_device ; preload the SECURITY device driver
|
|
|
|
preload_stacker:
|
|
mov preload_file,offset stacker_file+2
|
|
call preload_device ; preload STACKER from default drive
|
|
jnc preload_done
|
|
|
|
mov preload_file,offset dblspace_file+2
|
|
call preload_device ; STACKER failed, try DBLSPACE from default
|
|
jnc preload_done
|
|
|
|
cmp alt_drive,0 ; STACKER and DBLSPACE failed on A:,
|
|
je preload_done ; should we look on drive C:?
|
|
|
|
mov preload_file,offset stacker_file
|
|
call preload_device ; preload STACKER from drive C:
|
|
jnc preload_done
|
|
|
|
mov preload_file,offset dblspace_file
|
|
call preload_device ; STACKER failed, try DBLSPACE from C:
|
|
|
|
preload_done:
|
|
mov ax,(MS_X_OPEN*256)+80h ; Open the configuration file
|
|
mov dx,offset cfg_file ; Try Opening the file DCONFIG.SYS
|
|
int DOS_INT ; if this fails then open CONFIG.SYS
|
|
jnc cfg_20 ; Found DCONFIG.SYS
|
|
|
|
if ADDDRV
|
|
mov si,dx
|
|
mov dx,offset err_no_command_file
|
|
xor al,al
|
|
call config_error
|
|
jmps cfg_exit
|
|
else
|
|
mov si,offset cfg_file+1
|
|
mov di,offset cfg_file
|
|
call copy_asciiz ; make DCONFIG.SYS = CONFIG.SYS
|
|
|
|
mov ax,(MS_X_OPEN*256)+80h ; Open the configuration file
|
|
int DOS_INT ; CONFIG.SYS
|
|
jc cfg_exit ; Return on error without complaint
|
|
endif
|
|
|
|
cfg_20:
|
|
mov bx,ax ; get the CONFIG handle
|
|
mov ah,MS_X_CLOSE ; Close the CONFIG file
|
|
int DOS_INT
|
|
|
|
cfg_nextline: ; Read the next command line
|
|
call readline ; If Carry then Terminal Error
|
|
jc cfg_exit ; so Exit
|
|
mov ax,boot_options
|
|
cfg_continue:
|
|
push ax ; save query options
|
|
call scan ; Now Scan the command List
|
|
pop ax ; recover query options
|
|
jc cfg_error ; for a matching command name
|
|
call cfg_query ; prompt to see if we want it
|
|
jc cfg_nextline
|
|
call CFG_FUNC[di] ; Execute the request command
|
|
jmps cfg_nextline ; and loop till the end of the file
|
|
|
|
cfg_error:
|
|
mov dx,offset bad_command ; Display a Bad command error msg
|
|
mov al,CR ; and the CR terminated string
|
|
call config_error ; then continue processing
|
|
jmps cfg_nextline ; Carry Returned On Error
|
|
|
|
cfg_exit:
|
|
mov sp,save_sp ; get back the real stack
|
|
call preload_complete ; move transcient code to final position
|
|
if ADDDRV
|
|
cmp error_flag,1 ;Set CF if error flag is zero
|
|
cmc ;Set CF if error flag is 1
|
|
endif
|
|
ret
|
|
|
|
|
|
cfg_query:
|
|
; On Entry:
|
|
; AX = boot_options key
|
|
; SI -> command tail
|
|
; DI -> CFG_
|
|
; On Exit:
|
|
; CY set if we should skip command
|
|
; SI/DI preserved
|
|
;
|
|
push si
|
|
push di
|
|
test CFG_FLAGS[di],CF_NOF ; are Function Keys allowed ?
|
|
; clc ; if not process the command
|
|
jz $+5 ! jmp cfg_query90
|
|
cmp ax,F5KEY ; F5 bypasses CONFIG processing
|
|
stc ; so bypass this line
|
|
je cfg_query90
|
|
test CFG_FLAGS[di],CF_QUERY ; specific QUERY request ?
|
|
jnz cfg_query10
|
|
cmp ax,F8KEY ; should we prompt for everything ?
|
|
clc ; if not process the command
|
|
jne cfg_query90
|
|
cfg_query10:
|
|
push si
|
|
mov si,CFG_NAME[di] ; DS:SI -> command name we matched
|
|
cfg_query20:
|
|
lodsb ; get character
|
|
test al,al ; zero terminated
|
|
jz cfg_query30
|
|
xchg ax,dx ; DL= character
|
|
mov ah,MS_C_WRITE
|
|
int DOS_INT ; output the character
|
|
jmps cfg_query20 ; "DEVICE"
|
|
cfg_query30:
|
|
mov dl,'='
|
|
mov ah,MS_C_WRITE
|
|
int DOS_INT ; "DEVICE="
|
|
pop si ; rest of command line
|
|
cfg_query40:
|
|
lodsb
|
|
cmp al,CR ; is it the end of the line ?
|
|
je cfg_query50 ; no, do another character
|
|
xchg ax,dx ; DL= character
|
|
mov ah,MS_C_WRITE
|
|
int DOS_INT ; output the character
|
|
jmps cfg_query40 ; "DEVICE=FILENAME.SYS"
|
|
cfg_query50:
|
|
mov ah,MS_C_WRITESTR ; Output msg of form " (Y/N) ? "
|
|
mov dx,offset confirm_msg1
|
|
int DOS_INT ; do " ("
|
|
mov ah,MS_C_WRITE
|
|
mov dl,yes_char
|
|
int DOS_INT ; do "Y"
|
|
mov dl,'/'
|
|
int DOS_INT ; do "/"
|
|
mov dl,no_char
|
|
int DOS_INT ; do "N"
|
|
mov ah,MS_C_WRITESTR
|
|
mov dx,offset confirm_msg2
|
|
int DOS_INT ; do ") ? "
|
|
cfg_query60:
|
|
call wait_for_key ; wait until a key is pressed
|
|
mov al,default_query_char ; if we timeout default
|
|
jc cfg_query70
|
|
mov ah,MS_C_RAWIN
|
|
int DOS_INT ; read a char
|
|
test al,al ; is it a function key ?
|
|
jnz cfg_query70
|
|
mov ah,MS_C_RAWIN
|
|
int DOS_INT ; throw away function keys
|
|
jmps cfg_query60
|
|
cfg_query70:
|
|
push ax ; save response
|
|
mov ah,MS_C_WRITE
|
|
mov dl,al
|
|
int DOS_INT ; echo the char
|
|
mov ah,MS_C_WRITESTR
|
|
mov dx,offset confirm_msg3 ; "DEVICE=FILENAME.SYS (Y/N) ?"
|
|
int DOS_INT ; now do CR/LF to tidy up
|
|
pop ax
|
|
call toupper ; make response upper case
|
|
cmp al,yes_char ; is it yes ?
|
|
je cfg_query90
|
|
stc ; return CY set, skip this line
|
|
cfg_query90:
|
|
pop di
|
|
pop si
|
|
ret
|
|
|
|
|
|
call_preload_entry: ; call the preload device driver back door
|
|
push ds ; all DBLSPACE calls destroy DS register
|
|
callf preload_entry
|
|
pop ds
|
|
ret
|
|
|
|
preload_complete: ; CONFIG processing complete
|
|
push ds
|
|
push es
|
|
mov ax,4a11h ; DBLSPACE presence check
|
|
xor bx,bx
|
|
int 2fh
|
|
or ax,ax
|
|
jnz reloc_end
|
|
test dx,8000h ; is relocation already complete (may
|
|
jz reloc_end ; be done by HIDEVICE statement)
|
|
mov bx,0ffffh ; query the size of the transcient
|
|
mov ax,4a11h ; portion
|
|
int 2fh
|
|
|
|
mov cx,mem_current ; pull transcient portion down to low
|
|
mov es,cx ; memory
|
|
inc cx
|
|
mov es:DMD_ID,'D' ; control block type is 'D' (device
|
|
mov es:DMD_PSP,cx ; driver), owner is self
|
|
mov es:DMD_LEN,ax
|
|
inc ax ; control block overhead
|
|
add mem_current,ax
|
|
mov di,8 ; copy the name to the control block
|
|
mov si,preload_file ; device filename
|
|
preload_complete10:
|
|
lodsb ; get a character
|
|
cmp al,'\' ; have we found the '\' yet ?
|
|
jne preload_complete10 ; no, go swallow another character
|
|
movsw ! movsw ! movsw ! movsw ; copy the 8 bytes
|
|
mov ax,es
|
|
inc ax ; segment address of free memory
|
|
mov es,ax
|
|
mov bx,0fffeh ; move transcient portion down
|
|
mov ax,4a11h
|
|
int 2fh
|
|
reloc_end:
|
|
pop es
|
|
pop ds
|
|
ret
|
|
|
|
PreloadFixup:
|
|
;------------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; None
|
|
; All regs preserved
|
|
;
|
|
push es
|
|
push bx
|
|
push ax
|
|
mov al,preload_drv ; get number of preload drives
|
|
les bx,func52_ptr
|
|
sub es:F52_PHYDRV[bx],al
|
|
sub es:F52_LASTDRV[bx],al
|
|
pop ax
|
|
pop bx
|
|
pop es
|
|
ret
|
|
|
|
PreloadCleanup:
|
|
;--------------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; None
|
|
;
|
|
; inform DBLSPACE about each device driver
|
|
push ds
|
|
push es
|
|
xor ax,ax
|
|
xchg al,preload_drv ; get number of preload drives
|
|
les bx,func52_ptr
|
|
add es:F52_PHYDRV[bx],al
|
|
add es:F52_LASTDRV[bx],al
|
|
mov ax,4a11h ; DBLSPACE installation check
|
|
xor bx,bx
|
|
int 2fh
|
|
or ax,ax
|
|
jnz broadcast_exit
|
|
test dx,8000h ; initialisation complete?
|
|
jz broadcast_exit
|
|
mov preload_drv,ch ; save # preload drives
|
|
mov dx,mem_max ; top of available memory
|
|
mov cx,mem_current ; base of available memory
|
|
sub dx,cx
|
|
mov ah,55h ; version number
|
|
mov al,dev_count ; number of new units installed
|
|
mov bx,02h
|
|
callf preload_entry
|
|
broadcast_exit:
|
|
pop es
|
|
pop ds
|
|
ret
|
|
|
|
preload_device: ; preload disk compression driver before
|
|
; processing (D)CONFIG.SYS file
|
|
mov dx,preload_file
|
|
mov cx,mem_current ; next available segment address
|
|
mov es,cx
|
|
inc cx
|
|
mov es:DMD_ID,'D' ; control block type is 'D' (device
|
|
mov es:DMD_PSP,cx ; driver), owner is self
|
|
mov di,8 ; copy the name to the control block
|
|
mov si,dx ; device filename
|
|
preload_device10:
|
|
lodsb ; get a character
|
|
cmp al,'\' ; have we found the '\' yet ?
|
|
jne preload_device10 ; no, go swallow another character
|
|
movsw ! movsw ! movsw ! movsw ; copy the 8 bytes
|
|
mov ax,es
|
|
inc ax ; segment address of free memory
|
|
mov dev_load_seg,ax ; destination segment for EXEC call
|
|
mov dev_reloc_seg,ax ; relocation factor for .EXE drivers
|
|
push ax
|
|
push ds ! pop es
|
|
mov bx,offset dev_epb ; ES:BX control structure
|
|
mov ax,(MS_X_EXEC * 256)+3
|
|
int DOS_INT
|
|
pop es ; ES:0 -> preload driver
|
|
jnc load_ok
|
|
load_bad:
|
|
jmp preload_exit
|
|
load_ok:
|
|
cmp es:WORD PTR .12h,2e2ch ; preload device signature
|
|
jne load_bad
|
|
mov preload_seg,es ; back door entry to preload driver
|
|
cmp es:word ptr .82h,6 ; is it the old DBLSPACE driver ?
|
|
jne load_new_dblspace
|
|
mov preload_ver,6 ; yes, give it the old version
|
|
load_new_dblspace:
|
|
mov rel_unit,0 ; reset driver relative unit no.
|
|
mov bx,offset request_hdr ; DS:BX static INIT request header
|
|
mov ax,mem_max ; highest segment available to the
|
|
mov ds:RH0_RESIDENT,0 ; driver
|
|
mov ds:RH0_RESIDENT+2,ax
|
|
mov ax,cs
|
|
mov es,ax
|
|
push ds
|
|
call init_static_request ; initialise remaining fields
|
|
mov ax,cs:preload_ver
|
|
callf preload_entry
|
|
pop ds
|
|
jc preload_exit ; INIT function fails if CARRY set
|
|
or ax,ax ; or AX != 0
|
|
jnz preload_exit
|
|
;; mov ax,ds:RH0_RESIDENT ; end of resident portion (offset)
|
|
;; add ax,15 ; convert offset to paragraph size
|
|
;; mov cl,4
|
|
;; shr ax,cl
|
|
;; add ax,ds:RH0_RESIDENT+2 ; end of resident portion (segment)
|
|
;JS we should check that the source and destination do not overlap here!
|
|
|
|
mov bx,04h ; find the size of the transient code
|
|
call call_preload_entry
|
|
sub mem_max,ax ; move the top of available memory down
|
|
|
|
mov es,mem_max ; ES destination for relocatable code
|
|
dec mem_max ; last free segment address
|
|
mov bx,06h ; call driver to relocate code etc.
|
|
call call_preload_entry
|
|
inc ax
|
|
mov mem_current,ax ; update base of free TPA
|
|
|
|
mov es,preload_seg
|
|
xor di,di ; ES:DI -> preload driver
|
|
mov bx,offset request_hdr
|
|
mov al,ds:RH0_NUNITS
|
|
test al,al
|
|
jz preload_char_dev
|
|
add preload_drv,al ; remember how many preloaded drives
|
|
call block_device ; setup the DPBs and other block structures
|
|
|
|
preload_char_dev:
|
|
mov cx,mem_current
|
|
mov ax,preload_seg
|
|
sub cx,ax ; CX = length of preload driver
|
|
dec ax
|
|
mov es,ax ; ES:0 -> "DMD" for preload driver
|
|
mov es:DMD_LEN,cx
|
|
inc ax
|
|
mov es,ax
|
|
xor di,di ; ES:DI -> "device" header
|
|
mov ax,0FFFFh
|
|
mov es:[di],ax
|
|
mov es:2[di],ax
|
|
call device_insert ; insert device into chain
|
|
|
|
mov dx,mem_max ; top of available memory
|
|
mov cx,mem_current ; base of available memory
|
|
sub dx,cx
|
|
mov ax,5500h ; AH = version, AL = all internal drives
|
|
mov bx,02h ; mount existing container files
|
|
call call_preload_entry
|
|
|
|
xor bx,bx ; complete initialisation (hook
|
|
call call_preload_entry ; interrupts etc.)
|
|
|
|
preload_exit:
|
|
push cs ! pop es
|
|
ret
|
|
|
|
|
|
|
|
func_hidevice: ; HIDEVICE=filename
|
|
; Look for /L:r1[,s1][;r2[,s2]] [/S] filename
|
|
; Look for SIZE=<hexnumber> filename
|
|
|
|
mov himem_region,0 ; assume no region supplied
|
|
mov himem_size,0 ; and no size
|
|
mov di,offset region_opt ; check out /L: region option
|
|
call compare
|
|
jc hidevice10
|
|
call parse_region ; get region and size
|
|
jnc hidevice20
|
|
xor si,si ; something wrong, give an error
|
|
mov dx,offset bad_filename
|
|
jmp config_error
|
|
|
|
hidevice10:
|
|
mov di,offset size_opt ; check out SIZE= option
|
|
call compare
|
|
jc hidevice20 ; if no SIZE= just try to load it
|
|
call parse_size ; else get supplied value
|
|
hidevice20:
|
|
call whitespace ; we may have a '=' lurking
|
|
hidevice30:
|
|
lodsb ; strip off optional '='
|
|
cmp al,'=' ; before the filename
|
|
je hidevice30
|
|
dec si ; it wasn't a '=' after all
|
|
call whitespace
|
|
|
|
mov ax,himem_size ; get size parameter
|
|
cmp ax,6000h ; should we just load low ?
|
|
jae func_device
|
|
push si
|
|
test ax,ax ; have we been given a size ?
|
|
jne hidevice40
|
|
call size_file ; get requirements from file size
|
|
hidevice40:
|
|
call himem_setup ; try and find some hi memory
|
|
pop si
|
|
jc func_device ; if we can't load low
|
|
push si
|
|
call func_device ; now try and install the device
|
|
call himem_cleanup ; clean up our hi memory
|
|
pop si
|
|
jc func_device ; error loading hi, try low
|
|
ret
|
|
|
|
|
|
func_device: ; DEVICE=filename
|
|
push si
|
|
mov di,offset dev_name ; Copy the Device Filename into a
|
|
mov byte ptr [di],0 ; local buffer and zero terminate
|
|
call copy_file
|
|
pop si ; Restore original SI
|
|
jc device_error
|
|
|
|
push es
|
|
push si
|
|
mov es,mem_current ; ES points at structure
|
|
call BuildHeader
|
|
pop si
|
|
push si
|
|
call FindName ; DS:SI -> leafname
|
|
call SetName ; Fill in name info
|
|
pop si
|
|
pop es
|
|
|
|
mov ax,mem_current ; Get Current Memory Base
|
|
inc ax ; allow a para for a header
|
|
mov dev_load_seg,ax ; Setup the Load Address and the
|
|
mov dev_reloc_seg,ax ; relocation factor to be applied to
|
|
; .EXE device drivers
|
|
|
|
mov ax,(MS_X_OPEN * 256)+0 ; open file r/o
|
|
mov dx,offset dev_name
|
|
int DOS_INT
|
|
jc device_error
|
|
mov bx,ax ; now find out how big the file is
|
|
mov ax,(MS_X_LSEEK * 256)+2
|
|
xor cx,cx
|
|
xor dx,dx ; by seeking to zero bytes from end
|
|
int DOS_INT
|
|
jc device_error
|
|
|
|
xchg ax,cx ; save lo byte of length
|
|
|
|
mov ah,MS_X_CLOSE ; close this file
|
|
int DOS_INT
|
|
jc device_error
|
|
|
|
mov ax,cx ; DX:AX file length
|
|
or cx,dx ; do we have a zero length file
|
|
jz device_error ; if so stop now
|
|
|
|
mov cl,4
|
|
shr ax,cl ; convert file size to para
|
|
mov cl,12
|
|
shl dx,cl ; ignore > 1 MByte portion
|
|
or dx,ax ; dx = file size in para
|
|
inc dx ; one for rounding error
|
|
|
|
add dx,mem_current ; top of area needed for the load
|
|
cmp dx,mem_max ; will file fit (really HIDEVICE check)
|
|
jb func_device10 ; no, stop now
|
|
ret
|
|
|
|
device_error:
|
|
mov error_level,ax ; save error code
|
|
mov al,0
|
|
mov si,offset dev_name
|
|
mov dx,offset bad_filename
|
|
jmp config_error
|
|
|
|
func_device10:
|
|
mov ax,(MS_X_EXEC * 256)+3 ; Use the Load Overlay function to
|
|
mov bx,offset dev_epb ; Read in and Relocate the Device
|
|
mov dx,offset dev_name ; driver
|
|
int DOS_INT ;
|
|
jc device_error
|
|
|
|
; mov dosVersion,ax ; set version number
|
|
|
|
push es
|
|
mov ax,mem_current ; Get Current Memory Base
|
|
push ax
|
|
inc ax ; skip the header
|
|
xor di,di ; Address of the first device header
|
|
mov es,ax ; in the device chain
|
|
|
|
if ADDDRV
|
|
test es:DH_ATTRIB[di],DA_CHARDEV
|
|
jnz f_dev30 ;Character device driver ?
|
|
|
|
; Can't install block device drivers so output error message and exit.
|
|
pop es
|
|
pop es
|
|
|
|
xor al,al
|
|
mov si,offset dev_name
|
|
mov dx,offset err_block_device
|
|
jmp config_error
|
|
|
|
f_dev30:
|
|
endif
|
|
|
|
call PreloadFixup ; fiddle things for preload
|
|
if DOS5
|
|
call ProtmanFixup ; fiddle int 12 memory for PROTMAN$
|
|
endif
|
|
call device_init ; initialise the device drivers
|
|
if DOS5
|
|
call ProtmanCleanup ; restore int 12 memory after PROTMAN$
|
|
endif
|
|
mov error_level,ax ; save error code
|
|
pop es ; by old EMM386.SYS
|
|
mov ax,mem_current
|
|
mov es:DMD_LEN,ax ; save memory field
|
|
mov ax,es
|
|
inc ax
|
|
sub es:DMD_LEN,ax
|
|
pop es
|
|
call PreloadCleanup ; cleanup after ourselves
|
|
ret
|
|
|
|
|
|
BuildHeader:
|
|
;-----------
|
|
; On Entry:
|
|
; ES:0 -> header
|
|
; On Exit:
|
|
; None
|
|
;
|
|
xor di,di
|
|
mov al,'D'
|
|
stosb ; ID_FIELD
|
|
mov ax,es
|
|
inc ax
|
|
stosw ; OWNER_FIELD
|
|
xor ax,ax
|
|
stosb ! stosw ! stosw ; zero rest up to name
|
|
ret
|
|
|
|
|
|
FindName:
|
|
;--------
|
|
; On Entry:
|
|
; DS:SI -> pathname of file
|
|
; On Exit:
|
|
; DS:SI -> final leaf name of file
|
|
; CX = length of leaf name
|
|
;
|
|
mov cx,si ; remember start of leaf
|
|
FindName10:
|
|
lodsb
|
|
cmp al,' ' ; end of the name ?
|
|
jbe FindName30
|
|
call dbcs_lead ; is it a double byte pair ?
|
|
jne FindName20
|
|
lodsb ; include the second byte
|
|
jmps FindName10
|
|
FindName20:
|
|
cmp al,'\' ; is it a seperator ?
|
|
je FindName
|
|
cmp al,'/'
|
|
je FindName
|
|
jmps FindName10
|
|
FindName30:
|
|
xchg cx,si ; SI -> start of leaf name
|
|
sub cx,si
|
|
dec cx ; CX = length
|
|
ret
|
|
|
|
|
|
SetName:
|
|
;-------
|
|
; On Entry:
|
|
; DS:SI -> leaf name to update
|
|
; ES = DMD to update
|
|
; On Exit:
|
|
; CX/SI preserved
|
|
;
|
|
push si
|
|
mov di,offset DMD_NAME ; point at the owners name field
|
|
SetName10:
|
|
lodsb
|
|
cmp al,' ' ; end of the name ?
|
|
jbe SetName30
|
|
call dbcs_lead ; is it a double byte pair ?
|
|
jne SetName20
|
|
stosb ; copy 1st byte of pair
|
|
cmp di,(offset DMD_NAME)+DMD_NAME_LEN
|
|
jae SetName30 ; don't overflow if name too long
|
|
movsb ; and the second
|
|
jmps SetName10
|
|
|
|
SetName20:
|
|
stosb
|
|
cmp al,'.' ; discard all following '.'
|
|
je SetName30
|
|
cmp di,(offset DMD_NAME)+DMD_NAME_LEN
|
|
jb SetName10 ; don't overflow if name too long
|
|
SetName30:
|
|
dec di
|
|
xor ax,ax
|
|
SetName40:
|
|
stosb ; zero the '.'
|
|
cmp di,(offset DMD_NAME)+DMD_NAME_LEN
|
|
jb SetName40 ; zero the rest of the name
|
|
SetName50:
|
|
pop si
|
|
ret
|
|
|
|
if DOS5
|
|
|
|
ROS_MEMORY equ .413h ; main memory on KB
|
|
|
|
protmanName db 'PROTMAN$'
|
|
|
|
protmanAdjust dw 0
|
|
|
|
ProtmanFixup:
|
|
;------------
|
|
; On Entry:
|
|
; ES:DI -> device driver header
|
|
; On Exit:
|
|
; All regs preserved
|
|
;
|
|
; fiddle int 12 memory for PROTMAN$
|
|
;
|
|
push cx
|
|
push si
|
|
push di
|
|
mov cs:protmanAdjust,0 ; assume it's not protman
|
|
mov si,offset protmanName
|
|
lea di,DH_NAME[di] ; ES:DI -> device driver name
|
|
mov cx,8/2
|
|
repe cmpsw ; does the name match ?
|
|
jne ProtmanFixup10
|
|
mov si,mem_max ; SI = top of memory in para
|
|
push ds
|
|
; xor cx,cx
|
|
mov ds,cx
|
|
mov cl,10-4
|
|
shr si,cl ; convert para to KBytes
|
|
mov cx,ds:ROS_MEMORY ; CX = existing top of memory
|
|
sub cx,si ; CX = amount to hide
|
|
sub ds:ROS_MEMORY,cx ; hide it
|
|
pop ds
|
|
mov cs:protmanAdjust,cx ; remember how much we hid
|
|
ProtmanFixup10:
|
|
pop di
|
|
pop si
|
|
pop cx
|
|
ret
|
|
|
|
ProtmanCleanup:
|
|
;--------------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; All regs preserved
|
|
; restore int 12 memory after PROTMAN$
|
|
;
|
|
push ds
|
|
push ax
|
|
xor ax,ax
|
|
mov ds,ax
|
|
mov ax,cs:protmanAdjust ; normally zero..
|
|
add ds:ROS_MEMORY,ax
|
|
pop ax
|
|
pop ds
|
|
ret
|
|
|
|
endif
|
|
|
|
;
|
|
; The syntax currently supported is "COUNTRY=NNN,[YYY],[FILENAME]"
|
|
; where:-
|
|
; NNN is a valid country code based on
|
|
; the International Dialing Code
|
|
;
|
|
; YYY is the default CODEPAGE
|
|
;
|
|
; FILENAME is the location and name of the COUNTRY.SYS
|
|
; file containing the extended country info.
|
|
;
|
|
;
|
|
if not ADDDRV
|
|
func_country: ; COUNTRY=nnn,[yyy],[filename]
|
|
call atoi ; ax = country code
|
|
jc f_ctry50 ; check for error
|
|
mov country_code,ax ; save the Country Code
|
|
call separator ; look for ','
|
|
jc f_ctry20
|
|
call atoi ; Get the Code Page
|
|
jc f_ctry10 ; invalid or non existent code page
|
|
mov code_page,ax ; save default Code Page
|
|
f_ctry10:
|
|
call separator ; look for ','
|
|
jc f_ctry20 ; copy the supplied pathname
|
|
les bx,drdos_ptr ; Get the internal data area
|
|
mov di,es:DRDOS_COUNTRY_FILE[bx]
|
|
; ES:DI -> pcmode buffer for name
|
|
call copy_file
|
|
push cs ; restore ES
|
|
pop es
|
|
f_ctry20:
|
|
call nls_hook ; install our lang support
|
|
mov bx, country_code ; bx = country code
|
|
mov ah, MS_S_COUNTRY
|
|
mov al, 0FFh ; 16 bit country code
|
|
mov dx, 0FFFFh ; set country code subfunction
|
|
int DOS_INT
|
|
jc f_ctry40 ; check for error
|
|
mov bx, code_page ; bx = code page
|
|
or bx,bx
|
|
jz f_ctry30 ; No Code Page Set leave as 437
|
|
mov ax, MS_X_SETCP ; set codepage subfunction
|
|
int DOS_INT ; to set Current CodePage
|
|
f_ctry30:
|
|
jmp nls_unhook ; remove our lang support
|
|
; ret
|
|
|
|
f_ctry40:
|
|
call nls_unhook ; remove our lang support
|
|
f_ctry50:
|
|
; Bad or non-existant number in command tail. Display error message.
|
|
xor si,si
|
|
mov dx, offset bad_country
|
|
jmp config_error
|
|
|
|
|
|
|
|
func_shell: ; SHELL=filename
|
|
mov di,offset shell ; Copy the New Command Name
|
|
call copy_file ; into the BIOSINIT buffer
|
|
mov al,0 ! stosb ; Terminate the Environment Correctly
|
|
jc shell_error
|
|
|
|
mov di,offset shell_cline+1 ; Now copy the default command
|
|
mov al,' ' ! stosb ; into place
|
|
mov cx,0 ;
|
|
f_sh10:
|
|
lodsb ! stosb ; Copy the next Character
|
|
inc cx ; Increment the count
|
|
cmp al,CR ; Was this the end of string Char
|
|
jnz f_sh10 ; No so Repeat
|
|
mov shell_cline,cl ; Save Command Length
|
|
ret
|
|
|
|
shell_error:
|
|
mov al,CR
|
|
mov dx,offset bad_shell
|
|
jmp config_error
|
|
|
|
|
|
func_lastdrive: ; LASTDRIVE=d:
|
|
call atoi ; are we supplying a decimal number?
|
|
jc f_lastdrive10
|
|
cmp al,32 ; is it in range ?
|
|
jbe f_lastdrive20
|
|
lastdrv_error:
|
|
xor si,si ; Do not display Failing String
|
|
mov dx,offset bad_lastdrive ; and display an error message
|
|
jmp config_error
|
|
|
|
f_lastdrive10:
|
|
lodsb ; Get the Option Character
|
|
call toupper ; and convert to Upper Case
|
|
sub al, 'A' ; al should be between 0..25
|
|
jc lastdrv_error
|
|
cmp al, 'Z'-'A'
|
|
ja lastdrv_error
|
|
inc al ; al = number of drives in system
|
|
f_lastdrive20:
|
|
mov last_drv,al ; remember for later
|
|
ret
|
|
|
|
|
|
|
|
func_break: ; BREAK=ON/OFF
|
|
call check_onoff ; Check for ON or OFF
|
|
mov dl,al ; Get value found
|
|
jnc set_break ; and check for error
|
|
|
|
xor si,si ; Do not display Failing String
|
|
mov dx, offset bad_break ; Bad or non-existant ON/OFF string
|
|
jmp config_error ; in command tail. Display error message.
|
|
|
|
set_break:
|
|
mov ah,MS_S_BREAK ; Set the Default Break Flag
|
|
mov al,01 ; Set Sub-Function
|
|
int DOS_INT ; Execute function and return
|
|
ret
|
|
|
|
|
|
|
|
func_numlock: ; NUMLOCK=ON/OFF
|
|
call check_onoff ; Check for ON or OFF
|
|
jc numlock20
|
|
push ds
|
|
xor bx,bx
|
|
mov ds,bx
|
|
mov bx,417h ; DS:BX -> keyboard control
|
|
or ds:byte ptr [bx],20h ; set numlock bit
|
|
test al,al ; was it numlock on ?
|
|
jnz numlock10 ; if not, better clear the bit
|
|
and ds:byte ptr [bx],not 20h
|
|
numlock10:
|
|
pop ds
|
|
mov ah,MS_C_STAT ; get console status
|
|
int DOS_INT ; (usually int 16 update NUMLOCK state)
|
|
numlock20:
|
|
ret
|
|
|
|
|
|
|
|
func_hibuffers: ; HIBUFFERS=nn[,nn]
|
|
or buffersIn,BUFFERS_IN_HMA
|
|
; jmp func_buffers
|
|
|
|
func_buffers: ; BUFFERS=nn[,nn]
|
|
call atoi ; AX = # of buffers
|
|
jc buffer_error ; check for error
|
|
cmp ax,MIN_NUM_BUFFS ; check if less than minimum
|
|
jae func_buf1
|
|
mov ax,3 ; force to use minimum if less
|
|
func_buf1:
|
|
cmp ax,MAX_NUM_BUFFS ; check if more than maximum
|
|
jbe func_buf2
|
|
mov ax,MAX_NUM_BUFFS ; force to use maximum if more
|
|
func_buf2:
|
|
mov init_buf,al ; update if we want more
|
|
call separator ; look for ','
|
|
jc func_buf4
|
|
call atoi ; Get read-ahead buffer size
|
|
jc buffer_error
|
|
cmp ax,MIN_READ_AHEAD
|
|
jb buffer_error
|
|
cmp ax,MAX_READ_AHEAD
|
|
ja buffer_error
|
|
mov num_read_ahead_buf,al
|
|
func_buf4:
|
|
ret
|
|
|
|
buffer_error:
|
|
xor si,si ; Do not display Failing String
|
|
mov dx, offset bad_buffers
|
|
jmp config_error
|
|
|
|
|
|
func_files: ; FILES=nn
|
|
call atoi ; AX = # of files
|
|
jc files_error ; check for error
|
|
cmp ax,MIN_NUM_FILES ; check if less than minimum
|
|
jae func_fil1
|
|
mov ax,MIN_NUM_FILES ; force to use minimum if less
|
|
func_fil1:
|
|
cmp ax,MAX_NUM_FILES ; check if more than maximum
|
|
jbe func_fil2
|
|
mov ax,MAX_NUM_FILES ; force to use maximum if more
|
|
func_fil2:
|
|
mov num_files,ax ; update the number required
|
|
ret
|
|
|
|
files_error:
|
|
xor si,si ; Do not display Failing String
|
|
mov dx, offset bad_files
|
|
jmp config_error
|
|
|
|
|
|
|
|
func_fcbs: ; FCBS=nn
|
|
call atoi ; AX = # of files
|
|
jc fcbs_error ; check for error
|
|
cmp ax,MIN_NUM_FCBS ; check if less than minimum
|
|
jae func_fcb1
|
|
mov ax,MIN_NUM_FCBS ; force to use minimum if less
|
|
func_fcb1:
|
|
cmp ax,MAX_NUM_FCBS ; check if more than maximum
|
|
jbe func_fcb2
|
|
mov ax,MAX_NUM_FCBS ; force to use maximum if more
|
|
func_fcb2:
|
|
mov num_fcbs,ax ; update number of FCB's
|
|
ret
|
|
|
|
fcbs_error:
|
|
xor si,si ; Do not display Failing String
|
|
mov dx, offset bad_fcbs
|
|
jmp config_error
|
|
endif ;not ADDDRV
|
|
|
|
|
|
|
|
func_common: ; [COMMON]
|
|
func_remark: ; REM Comment field
|
|
ret
|
|
|
|
func_switches: ; SWITCHES=...
|
|
; /N = disable F5/F8 feature
|
|
; /W = zap wina20.386 name
|
|
; /K = disable enhanced keyboard support
|
|
; /F = skip statup delay
|
|
|
|
call whitespace ; skip all spaces
|
|
lodsw ; get '/x'
|
|
cmp al,CR ; check for end-of-line
|
|
je func_switches10
|
|
cmp ah,CR ; check for end-of-line
|
|
je func_switches10
|
|
cmp ax,'N/'
|
|
jne func_switches
|
|
mov boot_options,0 ; disable boot options
|
|
func_switches10:
|
|
ret
|
|
|
|
func_stacks: ; STACKS=number,size
|
|
;-----------
|
|
call atoi ; ax = number of stacks
|
|
jc func_stacks20 ; check for error
|
|
test ax,ax ; special case ? (disabled)
|
|
jz func_stacks10
|
|
cmp ax,MIN_NUM_STACKS ; range check for a sensible value
|
|
jb func_stacks20
|
|
cmp ax,MAX_NUM_STACKS
|
|
ja func_stacks20
|
|
func_stacks10:
|
|
mov num_stacks,ax
|
|
call separator ; look for ','
|
|
jc func_stacks20
|
|
call atoi ; get size of a stack frame
|
|
jc func_stacks20
|
|
cmp ax,MIN_SIZE_STACK ; range check it
|
|
jb func_stacks20
|
|
cmp ax,MAX_SIZE_STACK
|
|
ja func_stacks20
|
|
mov stack_size,ax
|
|
func_stacks20:
|
|
ret
|
|
|
|
|
|
if not ADDDRV
|
|
func_deblock: ; DEBLOCK=xxxx
|
|
;------------
|
|
call atohex ; read hex number into DX:AX
|
|
jc func_deblock10
|
|
test dx,dx
|
|
jnz func_deblock10
|
|
mov DeblockSetByUser,TRUE ; the user has supplied a setting
|
|
push ds
|
|
mov ds,bios_seg
|
|
mov DeblockSeg,ax ; save deblocking segment
|
|
pop ds
|
|
func_deblock10:
|
|
ret
|
|
|
|
func_fastopen: ; FASTOPEN=nn
|
|
call atoi ; AX = # of files to cache
|
|
jc fopen_error ; check for error
|
|
test ax,ax ; disable fast open?
|
|
jz func_fopen2 ; yes, allow to set to 0000
|
|
cmp ax,MIN_NUM_FOPEN ; check if less than minimum
|
|
jae func_fopen1
|
|
mov ax,MIN_NUM_FOPEN ; force to use minimum if less
|
|
func_fopen1:
|
|
cmp ax,MAX_NUM_FOPEN ; check if more than maximum
|
|
jbe func_fopen2
|
|
mov ax,MAX_NUM_FOPEN ; force to use maximum if more
|
|
func_fopen2:
|
|
mov num_fopen,ax ; update if we want more
|
|
func_fopen3:
|
|
ret
|
|
|
|
fopen_error:
|
|
xor si,si ; Do not display Failing String
|
|
mov dx, offset bad_fopen
|
|
jmp config_error
|
|
|
|
|
|
func_drivparm: ; DRIVPARM = /d:nn [/c] [/f:ff] [h:hh] [/n] [/s:ss] [/t:tt]
|
|
;-------------
|
|
; This function specifies the drive parameters for a device
|
|
; and overrides the defaults assumed by the device driver.
|
|
|
|
mov drivp_drv,0FFh ; invalid drive
|
|
call get_switch ; get next switch
|
|
cmp al,'d' ; first option must be /d:dd
|
|
jne drivparm_error
|
|
call get_number ; get numeric parameter
|
|
mov drivp_drv,al
|
|
mov drivp_chg,FALSE
|
|
mov drivp_prm,FALSE
|
|
mov drivp_trk,80 ; assume 80 tracks
|
|
|
|
mov bl,drivp_drv ; get drive to set up
|
|
cmp bl,'Z'-'A'
|
|
ja drivparm_error
|
|
inc bl
|
|
mov ioctl_pb,0 ; return defaults
|
|
mov ax,440Dh ; generic IOCTL
|
|
mov cx,0860h ; get device parameters
|
|
mov dx,offset ioctl_func
|
|
int DOS_INT
|
|
jc drivparm_error ; skip if we can't get parameters
|
|
|
|
mov drivp_ff,2 ; assume 720K 3.5" drive
|
|
call set_form_factor ; set defaults for form factor
|
|
|
|
call get_switch ; get next switch
|
|
cmp al,'c' ; is it /c (change line available)
|
|
jne drivparm1
|
|
mov drivp_chg,TRUE ; disk change line available
|
|
call get_switch ; get next switch
|
|
drivparm1:
|
|
cmp al,'f' ; form factor specification?
|
|
jne drivparm2
|
|
call get_number ; get numeric parameter
|
|
mov drivp_ff,al ; set form factor
|
|
call set_form_factor ; set defaults for form factor
|
|
jmps drivparm_loop ; get more parameters
|
|
|
|
drivparm_error:
|
|
xor si,si
|
|
mov dx,offset bad_drivparm
|
|
jmp config_error
|
|
|
|
drivparm_loop:
|
|
call get_switch ; get next switch
|
|
drivparm2:
|
|
cmp al,'h' ; specify number of heads
|
|
jne drivparm3
|
|
call get_number ; get numeric parameter
|
|
cmp ax,99
|
|
ja drivparm_error
|
|
mov drivp_heads,ax ; set # of heads
|
|
jmps drivparm_loop
|
|
|
|
drivparm3:
|
|
cmp al,'n'
|
|
jne drivparm4
|
|
mov drivp_prm,TRUE ; non-removable media
|
|
jmps drivparm_loop
|
|
|
|
drivparm4:
|
|
cmp al,'s'
|
|
jne drivparm5
|
|
call get_number ; get numeric parameter
|
|
cmp ax,63 ; range check sector per track
|
|
ja drivparm_error
|
|
mov drivp_spt,ax ; set # of sectors/track
|
|
jmps drivparm_loop
|
|
|
|
drivparm5:
|
|
cmp al,'t'
|
|
jne drivparm_error
|
|
call get_number ; get numeric parameter
|
|
cmp ax,999
|
|
ja drivparm_error
|
|
mov drivp_trk,ax ; set # of sectors/track
|
|
jmps drivparm_loop
|
|
|
|
drivparm_done: ; now set drive parameters
|
|
mov bl,drivp_drv
|
|
cmp bl,'Z'-'A'
|
|
ja drivparm_error
|
|
|
|
mov ioctl_func,0000$0100b ; normal track layout assumed,
|
|
; set device BPB for drive
|
|
mov al,drivp_ff ; get form factor
|
|
mov ioctl_type,al
|
|
|
|
sub ax,ax ; assume removable, no disk change
|
|
cmp drivp_prm,FALSE
|
|
je drivp_d1
|
|
or ax,1 ; drive is permanent media
|
|
drivp_d1:
|
|
cmp drivp_chg,FALSE
|
|
je drivp_d2
|
|
or ax,2 ; drive supports disk change line
|
|
drivp_d2:
|
|
mov ioctl_attrib,ax ; set drive attributes
|
|
|
|
mov ax,drivp_trk ; set # of cylinders
|
|
mov ioctl_tracks,ax
|
|
|
|
mov ioctl_mtype,0 ; assume standard type
|
|
|
|
mov ax,drivp_spt ; get sectors/track
|
|
mov di,offset ioctl_layout
|
|
push ds ! pop es ; ES:DI -> layout table
|
|
stosw ; set # of sectors
|
|
xchg ax,cx ; CX = # of sectors
|
|
mov ax,1 ; start with sector 1
|
|
drivp_d3:
|
|
stosw ; set next sector #
|
|
inc ax ; move to next sector
|
|
loop drivp_d3 ; repeat for all sectors
|
|
|
|
mov ax,drivp_heads
|
|
mul drivp_spt
|
|
mov dx,drivp_trk
|
|
mul dx ; AX/DX = # of sectors/disk
|
|
mov di,offset ioctl_bpb
|
|
mov 8[di],ax ; set sectors per disk
|
|
test dx,dx
|
|
jz drivp_d4
|
|
mov word ptr 8[di],0 ; indicate large partition
|
|
mov 21[di],ax ; set disk size in sectors
|
|
mov 23[di],dx
|
|
drivp_d4:
|
|
mov bl,drivp_drv
|
|
inc bl
|
|
mov ax,440Dh
|
|
mov cx,0840h ; set drive parameters
|
|
mov dx,offset ioctl_func
|
|
int DOS_INT ; tell the BIOS, ignore errors
|
|
|
|
ret
|
|
|
|
|
|
get_switch: ; get next command line switch
|
|
call whitespace ; skip all spaces & tabs
|
|
lodsb ; get next character
|
|
pop dx ; get return address
|
|
cmp al,'/' ; did we get a switch?
|
|
je get_swt9 ; yes, return the character
|
|
jmp drivparm_done
|
|
get_swt9:
|
|
lodsb
|
|
or al,('a' xor 'A') ; return upper-cased character
|
|
jmp dx
|
|
|
|
|
|
get_number:
|
|
; entry: SI -> next character (must be ':')
|
|
|
|
lodsb ; get next character
|
|
cmp al,':' ; must be colon
|
|
jne get_num_err ; return error if not
|
|
call atoi ; get numeric value
|
|
jc get_num_err ; must be number
|
|
ret ; AX = number
|
|
|
|
get_num_err:
|
|
pop dx
|
|
jmp drivparm_error ; reject this command
|
|
|
|
|
|
set_form_factor:
|
|
mov bl,drivp_ff
|
|
cmp bl,7
|
|
ja set_form9
|
|
mov bh,0
|
|
shl bx,1
|
|
push si ! push es
|
|
mov si,ff_table[bx] ; SI -> default media BPB
|
|
push ds ! pop es
|
|
mov di,offset ioctl_bpb ; ES:DI -> local BPB
|
|
mov cx,21 ; copy initialized portion
|
|
rep movsb
|
|
pop es ! pop si
|
|
ret
|
|
|
|
set_form9:
|
|
jmp drivparm_error
|
|
|
|
|
|
;
|
|
; This function modifies the History buffer support provided by
|
|
; DR DOS the defaults are History OFF, 512 byte buffers,
|
|
; Insert ON, Search OFF, Matching OFF.
|
|
;
|
|
func_history: ; HISTORY = ON|OFF[,NNNN[,ON|OFF[,ON|OFF[,ON|OFF]]]]
|
|
;------------
|
|
mov history_flg,0 ; start with it all off
|
|
|
|
call check_onoff ; Check for ON|OFF Switch
|
|
jc f_hist_err
|
|
test al,al ! jz f_hist_exit ; if OFF forget the rest
|
|
or history_flg,RLF_ENHANCED+RLF_INS
|
|
|
|
call separator ; look for ','
|
|
jc f_hist_exit ; Buffer Size not Specified
|
|
call atoi ; Get the Buffer Size
|
|
jc f_hist_err ; Invalid on no existant size
|
|
cmp ax,128 ! jb f_hist_err ; Buffer Size to Small
|
|
cmp ax,4096 ! ja f_hist_err ; Buffer Size to Large
|
|
mov history_size,ax ; Save History Buffer Size
|
|
|
|
call separator ; look for ','
|
|
jc f_hist_exit ; Insert mode not Specified
|
|
call check_onoff ; Check for ON|OFF Switch
|
|
jc f_hist_err
|
|
test al,al ! jnz func_hist10
|
|
and history_flg,not RLF_INS ; Insert state OFF
|
|
func_hist10:
|
|
call separator ; look for ','
|
|
jc f_hist_exit ; Search mode not Specified
|
|
call check_onoff ; Check for ON|OFF Switch
|
|
jc f_hist_err
|
|
test al,al ! jz func_hist20
|
|
or history_flg,RLF_SEARCH ; Search state ON
|
|
func_hist20:
|
|
call separator ; look for ','
|
|
jc f_hist_exit ; Match mode not Specified
|
|
call check_onoff ; Check for ON|OFF Switch
|
|
jc f_hist_err
|
|
test al,al ! jz func_hist30
|
|
or history_flg,RLF_MATCH ; Match state ON
|
|
func_hist30:
|
|
|
|
f_hist_exit:
|
|
ret
|
|
|
|
f_hist_err:
|
|
xor si,si ; Do not display Failing String
|
|
mov dx, offset bad_history ; Bad or non-existant ON/OFF string
|
|
jmp config_error ; in command tail. Display error message.
|
|
|
|
|
|
;
|
|
; HIINSTALL filename [Command Line Parameters]
|
|
;
|
|
; As INSTALL, but uses high memory if possible
|
|
;
|
|
func_hiinstall:
|
|
|
|
mov ax,5802h
|
|
int DOS_INT ; get upper memory link
|
|
cbw
|
|
push ax ; save upper memory link
|
|
|
|
mov ax,5800h
|
|
int DOS_INT ; get alloc strategy
|
|
push ax ; save alloc strategy
|
|
|
|
mov ax,5803h
|
|
mov bx,1 ; set upper memory link
|
|
int DOS_INT ; to 1
|
|
|
|
mov ax,5801h
|
|
mov bx,80h ; set alloc strategy to lowest-upper
|
|
int DOS_INT ; if available
|
|
|
|
call func_install ; try and install it
|
|
|
|
mov ax,5801h
|
|
pop bx ; set alloc strategy
|
|
int DOS_INT ; to original value
|
|
|
|
mov ax,5803h
|
|
pop bx ; set upper memory link
|
|
int DOS_INT ; to original value
|
|
|
|
ret
|
|
|
|
|
|
|
|
;
|
|
; INSTALL filename [Command Line Parameters]
|
|
;
|
|
; INSTALL will load and execute "FILENAME" with the optional command
|
|
; line parameters and continue processing the DCONFIG.SYS file when
|
|
; the application terminates.
|
|
;
|
|
; Entry
|
|
; ds:si -> first character of CR terminated filename and option string.
|
|
; Exit
|
|
; none
|
|
;
|
|
; WARNING -
|
|
; This code make certain assumptions about memory layout.
|
|
; If memory gets fragmented then it all starts falling apart.
|
|
;
|
|
func_install:
|
|
;------------
|
|
push ds ! push es
|
|
;
|
|
; Shrink the previously allocated memory block to MEM_CURRENT
|
|
; in preparation of the INSTALL EXEC function.
|
|
;
|
|
mov es,mem_current_base ; ES: Base Allocated Memory
|
|
mov bx,mem_current ; Get the currently allocated memory
|
|
sub bx,mem_current_base ; and subtract mem_current_base to
|
|
mov ah,MS_M_FREE ; give the number of paragraphs used
|
|
jz func_i10 ; if none, free it all up
|
|
mov ah,MS_M_SETBLOCK ; else modify block accordingly
|
|
func_i10:
|
|
int DOS_INT
|
|
|
|
; Now to protect the CONFIG code in high memory
|
|
mov ah,MS_M_ALLOC ; we now try to find base of TPA
|
|
mov bx,0ffffh ; ASSUME it is the biggest bit
|
|
int 21h ; of free memory about
|
|
mov ah,MS_M_ALLOC ; give it to me please
|
|
int 21h ; ax:0 -> exec memory
|
|
push bx ; we have allocated BX para's
|
|
mov es,ax ; ES -> exec memory
|
|
mov bx,init_dseg ; we want to protect BX:0 and above
|
|
dec bx ; allow for DMD
|
|
sub bx,ax ; we can spare this many paras
|
|
mov ah,MS_M_SETBLOCK ; for the exec so grow the
|
|
int 21h ; block accordingly
|
|
pop ax ; AX = total, BX = amount for install
|
|
|
|
sub ax,bx ; AX = amount we just freed
|
|
dec ax ; allow for DMD
|
|
xchg ax,bx ; BX = freed portion (the Init code)
|
|
mov ah,MS_M_ALLOC ; ASSUME an allocation of this size
|
|
int 21h ; will contain SYSDAT/CONFIG
|
|
push ax ; save seg so we can free mem later
|
|
|
|
mov ah, MS_M_FREE ; now free up the bit we prepared
|
|
int 21h ; earlier so exec has something
|
|
push ds ! pop es ; to work in
|
|
|
|
mov di,offset dev_name ; Copy the filename into a local
|
|
call copy_file ; buffer
|
|
|
|
; Calculate the command line length
|
|
mov di,si ; by scanning the command line for
|
|
mov al,CR ; the terminating CR
|
|
mov cx,128
|
|
repnz scasb
|
|
dec di
|
|
mov ax,di
|
|
sub ax,si
|
|
dec si ; Point to the byte before the
|
|
mov byte ptr [si],al ; command line string and update
|
|
; the count
|
|
|
|
mov ax,offset envstart ; env buffer is para aligned
|
|
mov cl,4
|
|
shr ax,cl ; convert offset to paras
|
|
mov cx,ds
|
|
add ax,cx ; segment of env variables
|
|
mov exec_envseg,ax
|
|
mov exec_lineoff,si
|
|
mov exec_lineseg,ds
|
|
mov exec_fcb1off,0FFFEh ; Force PCMODE to generate
|
|
mov exec_fcb2off,0FFFEh ; correct FCB References
|
|
|
|
mov system_ss,ss
|
|
mov system_sp,sp
|
|
|
|
mov dx,offset dev_name ; Get ASCIIZ Command
|
|
mov bx,offset exec_envseg ; and Parameter Block Offset
|
|
mov ax,4B00h ; Load and Execute Program with Handle
|
|
int DOS_INT ; EXEC the application
|
|
|
|
cli ; Swap back to the original stack
|
|
mov ss,cs:system_ss ; again with interrupts disabled
|
|
mov sp,cs:system_sp
|
|
sti
|
|
|
|
mov ah,MS_X_WAIT ; if all went well return the
|
|
jnc func_i20 ; termination code
|
|
mov ah,MS_F_ERROR ; if we had an error from EXEC
|
|
xor bx,bx ; get extended error
|
|
func_i20:
|
|
int DOS_INT ; retrieve error value
|
|
mov cs:error_level,ax ; and save for testing
|
|
|
|
pop es ; recover the seg we protected
|
|
mov ah, MS_M_FREE ; so we can free that memory up
|
|
int 21h
|
|
|
|
mov ah,MS_M_ALLOC ; try and allocate as much as possible
|
|
mov bx, 0FFFFh ; ASSUME this will cover CONFIG
|
|
int 21h ; bx = No. of paras available
|
|
mov ah, MS_M_ALLOC ; give me bx paras please
|
|
int 21h ; ax:0 -> my memory
|
|
|
|
pop es ! pop ds
|
|
mov mem_current_base,ax ; save memory base
|
|
mov mem_current,ax ; for future allocations
|
|
ret
|
|
|
|
|
|
|
|
func_hidos: ; HIDOS ON/OFF
|
|
call check_onoff ; Check for ON or OFF
|
|
jc f_hidos10 ; Return on error
|
|
mov hidos,al ; update hidos flag
|
|
f_hidos10:
|
|
ret
|
|
|
|
|
|
func_dos: ; DOS=HIGH - relocate BIOS/BDOS/Buffer etc to FFFF
|
|
call separator ; Deblank Command
|
|
mov di,offset high_opt ; es:di -> "HIGH"
|
|
call compare ; do we have an "HIGH"?
|
|
jc func_dos10
|
|
push si
|
|
call func_dos_high ; execute HIGH
|
|
pop si
|
|
jmps func_dos
|
|
func_dos10:
|
|
mov di,offset low_opt ; es:di -> "LOW"
|
|
call compare
|
|
jc func_dos20
|
|
push si
|
|
call func_dos_low ; execute LOW
|
|
pop si
|
|
jmps func_dos
|
|
func_dos20:
|
|
mov di,offset umb_opt ; es:di -> "UMB"
|
|
call compare
|
|
jc func_dos30
|
|
push si
|
|
call func_dos_umb ; execute UMB
|
|
pop si
|
|
jmps func_dos
|
|
func_dos30:
|
|
mov di,offset noumb_opt ; es:di -> "NOUMB"
|
|
call compare
|
|
jc func_dos40
|
|
push si
|
|
call func_dos_noumb ; execute NOUMB
|
|
pop si
|
|
jmps func_dos
|
|
func_dos40:
|
|
ret
|
|
|
|
func_dos_high:
|
|
;-------------
|
|
; Move DOS into the HMA and allocate buffers etc. high too.
|
|
;
|
|
mov dos_target_seg,0FFFFh
|
|
mov bios_target_seg,0FFFFh
|
|
mov hidos,TRUE ; update hidos flag to be ON
|
|
or buffersIn,BUFFERS_IN_HMA; buffers at seg FFFF too
|
|
ret
|
|
|
|
func_dos_low:
|
|
;------------
|
|
; force all allocation to be low
|
|
;
|
|
mov dos_target_seg,0
|
|
mov bios_target_seg,0
|
|
mov hidos,FALSE ; system allocation from low memory
|
|
mov buffersIn,0 ; buffers from low memory
|
|
ret
|
|
|
|
func_dos_umb:
|
|
;------------
|
|
; allocate Upper Memory Blocks and link them to the DMD chain
|
|
;
|
|
mov hidos,TRUE ; update hidos flag to be ON
|
|
call initialise_dmd_upper ; build initial upper memory DMD
|
|
jc func_dos_umb30
|
|
func_dos_umb10:
|
|
call alloc_xms_umb ; allocate XMS upper memory
|
|
jc func_dos_umb20
|
|
call add_dmd_upper ; add to upper memory DMD's
|
|
jmps func_dos_umb10 ; go around again
|
|
func_dos_umb20:
|
|
call remove_last_dmd ; get rid of useless last DMD
|
|
func_dos_umb30:
|
|
mov ax,(MS_M_STRATEGY*256)+3
|
|
mov bx,1 ; link in upper memory region
|
|
int 21h
|
|
ret
|
|
|
|
func_dos_noumb:
|
|
;--------------
|
|
; Unlink Upper Memory blocks from the DMD chain
|
|
;
|
|
mov ax,(MS_M_STRATEGY*256)+3
|
|
xor bx,bx ; unlink upper memory region
|
|
int 21h
|
|
ret
|
|
|
|
alloc_xms_umb:
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; CY set is no upper memory available
|
|
; else
|
|
; BX = para base address
|
|
; DX = para size
|
|
;
|
|
; Try to allocate the largest possible block of XMS memory
|
|
; so we can link it to the upper memory chain
|
|
;
|
|
push es
|
|
mov ax,4300h ; check for XMS installation
|
|
int 2fh
|
|
cmp al,80h
|
|
jne alloc_xms10
|
|
mov ax,4310h ; get address of XMS driver
|
|
int 2fh
|
|
mov word ptr xms_driver,bx
|
|
mov word ptr xms_driver+2,es
|
|
mov ah,10h ; allocate upper memory block
|
|
mov dx,0FFFFh ; DX set to find largest block
|
|
callf xms_driver
|
|
cmp dx,3 ; we need at least 3 para's
|
|
jb alloc_xms10 ; before we contruct a DMD
|
|
mov ah,10h ; now allocate largest block
|
|
callf xms_driver
|
|
cmp ax,1 ; did we succeed ?
|
|
je alloc_xms20
|
|
alloc_xms10:
|
|
stc ; return CY set indicating failure
|
|
alloc_xms20:
|
|
pop es
|
|
ret
|
|
|
|
xms_driver rd 0
|
|
dw 0,0
|
|
|
|
initialise_dmd_upper:
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; CY set if chain already exists
|
|
; (BX/DX preserved)
|
|
;
|
|
; build initial upper memory DMD
|
|
; we rely on the fact the last para in memory is unused
|
|
; (but BIOSINIT makes sure that is true)
|
|
;
|
|
push es
|
|
push bx
|
|
push dx
|
|
if DOS5
|
|
les bx,func52_ptr ; ES:BX -> list of lists
|
|
cmp es:F52_DMD_UPPER[bx],0FFFFh
|
|
else
|
|
les bx,drdos_ptr
|
|
cmp es:DRDOS_DMD_UPPER[bx],0
|
|
les bx,funv52_ptr ; ES:BX -> list of lists
|
|
endif
|
|
stc ; assume error return required
|
|
jne initialise_dmd_upper30 ; bail out if chain already established
|
|
mov es,es:F52_DMDROOT ; ES -> 1st DMD
|
|
initialise_dmd_upper10:
|
|
cmp es:DMD_ID,IDZ ; end of DMD chain ?
|
|
je initialise_dmd_upper20
|
|
cmp es:DMD_ID,IDM ; do we have any more DMD's ?
|
|
stc
|
|
jne initialise_dmd_upper30 ; woops, chain must be bad
|
|
mov ax,es ; better point to it
|
|
inc ax
|
|
add ax,es:DMD_LEN ; AX:0 -> next DMD
|
|
mov es,ax
|
|
jmps initialise_dmd_upper10
|
|
|
|
initialise_dmd_upper20:
|
|
mov ax,es
|
|
add ax,es:DMD_LEN ; AX:0 -> will be upper memory chain
|
|
cmp ax,0A000h ; if the DMD chain is already into
|
|
cmc ; upper memory, lets make sure we
|
|
jb initialise_dmd_upper30 ; stop before we fall apart
|
|
mov es:DMD_ID,IDM ; no longer the last entry
|
|
dec es:DMD_LEN ; shorten last DMD to make room
|
|
mov es,ax ; point to new DMD
|
|
mov es:DMD_ID,IDZ ; there is only one entry in the chain
|
|
mov es:DMD_PSP,8 ; its' owned by "system"
|
|
xchg ax,cx ; CX = DMD
|
|
mov ax,0FFFFh
|
|
sub ax,cx ; it's this big
|
|
mov es:DMD_LEN,ax
|
|
if DOS5
|
|
les bx,func52_ptr
|
|
mov es:F52_DMD_UPPER[bx],cx
|
|
else
|
|
les bx,drdos_ptr
|
|
mov es:DRDOS_DMD_UPPER[bx],cx
|
|
endif
|
|
clc
|
|
initialise_dmd_upper30:
|
|
pop dx
|
|
pop bx
|
|
pop es
|
|
ret
|
|
|
|
|
|
remove_last_dmd:
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; None
|
|
;
|
|
; We have build an upper memory DMD chain, but we have left an extra
|
|
; DMD around covering the ROMs at the top of memory. Remove it if
|
|
; it's not required.
|
|
;
|
|
push es
|
|
les bx,func52_ptr ; ES:BX -> list of lists
|
|
mov es,es:F52_DMDROOT[bx] ; ES -> 1st DMD
|
|
remove_last_dmd10:
|
|
cmp es:DMD_ID,IDM ; do we have any more DMD's ?
|
|
jne remove_last_dmd20 ; bail out if we don't
|
|
mov ax,es ; remember previous DMD
|
|
mov dx,es
|
|
inc dx
|
|
add dx,es:DMD_LEN ; DX:0 -> next DMD
|
|
mov es,dx
|
|
cmp es:DMD_ID,IDZ ; end of DMD chain ?
|
|
jne remove_last_dmd10
|
|
cmp es:DMD_PSP,8 ; is it owned by "system" ?
|
|
jne remove_last_dmd20 ; if so we can ditch this entry
|
|
mov es,ax ; ES = next to last DMD
|
|
mov es:DMD_ID,IDZ ; new end of chain
|
|
inc es:DMD_LEN ; include last para
|
|
if DOS5
|
|
les bx,func52_ptr ; ES:BX -> list of lists
|
|
cmp dx,es:F52_DMD_UPPER[bx]
|
|
jne remove_last_dmd20 ; remove upper memory link if none left
|
|
mov es:F52_DMD_UPPER[bx],0FFFFh
|
|
else
|
|
les bx,drdos_ptr
|
|
cmp dx,es:DRDOS_DMD_UPPER[bx]
|
|
jne remove_last_dmd20 ; remove upper memory link if none left
|
|
mov es:DRDOS_DMD_UPPER[bx],0
|
|
endif
|
|
remove_last_dmd20:
|
|
pop es
|
|
ret
|
|
|
|
|
|
add_dmd_upper:
|
|
; On Entry:
|
|
; BX = base address of DMD
|
|
; DX = size of DMD
|
|
; On Exit:
|
|
; None
|
|
;
|
|
; Add this block into the upper memory chain.
|
|
; To do this we find the DMD containing the block and link it into place
|
|
;
|
|
push es
|
|
push bx ; save base address
|
|
les bx,func52_ptr ; ES:BX -> list of lists
|
|
mov ax,es:F52_DMDROOT[bx] ; AX -> 1st DMD
|
|
pop bx ; 1st DMD is always below XMS
|
|
cmp ax,bx ; memory, so bomb out if not
|
|
jae add_dmd_upper40 ; as our DMD's must be corrupt
|
|
add_dmd_upper10:
|
|
mov es,ax
|
|
add ax,es:DMD_LEN ; AX:0 -> end of this block
|
|
cmp ax,bx ; is the next block above us ?
|
|
ja add_dmd_upper20 ; if not try the next block
|
|
inc ax ; AX:0 -> next DMD
|
|
cmp es:DMD_ID,IDM ; do we have any more DMD's ?
|
|
je add_dmd_upper10 ; we should have......
|
|
jmps add_dmd_upper40 ; stop, DMD's are screwed up
|
|
|
|
add_dmd_upper20:
|
|
; We have found the block we wish to insert a new free block into
|
|
cmp es:DMD_PSP,8 ; it must be owned by "system"
|
|
jne add_dmd_upper40
|
|
; Shorten existing DMD to point to new block
|
|
mov ax,bx ; work out how far to new DMD
|
|
mov cx,es
|
|
sub ax,cx ; it's this many para's
|
|
dec ax ; forget the header
|
|
xchg ax,es:DMD_LEN ; set new length
|
|
; now we need to work out how much is left above the new DMD
|
|
sub ax,dx ; subtract length of new block
|
|
sub ax,es:DMD_LEN ; subtract the portion below
|
|
; Create DMD covering new block
|
|
mov cl,IDM ; create a new entry
|
|
xchg cl,es:DMD_ID ; CL = existing ID (M/Z)
|
|
mov es,bx ; ES -> base of new DMD
|
|
mov es:DMD_ID,IDM ; it's a link field
|
|
mov es:DMD_PSP,0 ; it's free
|
|
dec dx ; forget the header
|
|
add bx,dx ; last para is here
|
|
dec dx ; forget the next link
|
|
mov es:DMD_LEN,dx ; it's this long
|
|
; Build a new DMD at the top if the new block for anything above it
|
|
mov es,bx
|
|
mov es:DMD_ID,cl ; inherit the ID field
|
|
mov es:DMD_LEN,ax ; and it's this long
|
|
test ax,ax ; if zero length then
|
|
jz add_dmd_upper30 ; it's free
|
|
mov ax,8 ; else it's system
|
|
add_dmd_upper30:
|
|
mov es:DMD_PSP,ax ; set owner
|
|
add_dmd_upper40:
|
|
pop es
|
|
ret
|
|
|
|
|
|
|
|
|
|
func_set: ; SET envar=string
|
|
call whitespace ; deblank the command
|
|
mov di,offset envstart-1 ; point to our environment area
|
|
func_set5:
|
|
inc di
|
|
cmp es:word ptr [di],0 ; are we at the end yet
|
|
jne func_set5
|
|
cmp di,offset envstart ; if nothing is there yet start
|
|
je func_set10 ; at the NUL, else skip the NUL
|
|
inc di ; to leave a seperator
|
|
func_set10:
|
|
lodsb ; get a character
|
|
cmp al,CR ; end of the line yet ?
|
|
je func_set20
|
|
cmp di,offset envend ; have we room ?
|
|
jae func_set30 ; bail out if not
|
|
stosb ; save the character
|
|
jmps func_set10
|
|
func_set20:
|
|
xor ax,ax ; terminate with NULL
|
|
stosb
|
|
func_set30:
|
|
ret
|
|
endif ;not ADDDRV
|
|
|
|
|
|
func_echo: ; ECHO "string"
|
|
call whitespace ; Scan off all white space
|
|
lodsb ; before the optional
|
|
cmp al,'=' ; '=' character.
|
|
je func_echo10
|
|
dec si ; point at char
|
|
func_echo10:
|
|
mov dx,offset msg_dollar ; NUL error message
|
|
mov al,CR ; SI -> config line anyway
|
|
jmp config_error ; use error reporting routine
|
|
|
|
func_yeschar: ; yeschar "string"
|
|
call whitespace ; Scan off all white space
|
|
lodsb ; before the optional
|
|
cmp al,'=' ; '=' character.
|
|
je func_yeschar10
|
|
dec si ; point at char
|
|
func_yeschar10:
|
|
call whitespace
|
|
lodsb
|
|
mov yes_char,al ; update YES character
|
|
ret
|
|
|
|
func_chain: ; CHAIN="filename" - use as new CONFIG.SYS
|
|
mov di,offset dev_name ; Copy the Device Filename into a
|
|
mov byte ptr [di],0 ; local buffer and zero terminate
|
|
call copy_file
|
|
jc func_chain10 ; ignore if any problems
|
|
mov ax,(MS_X_OPEN*256)+80h ; Try to open the file
|
|
mov dx,offset dev_name ; as a new config file
|
|
int DOS_INT ; if we can't ignore it
|
|
jc func_chain10
|
|
mov bx,ax
|
|
mov ah,MS_X_CLOSE
|
|
int DOS_INT ; close the new file
|
|
mov si,offset dev_name
|
|
mov di,offset cfg_file
|
|
call copy_asciiz ; copy the new name
|
|
mov cfg_seeklo,0 ; start at begining of it
|
|
mov cfg_seekhi,0
|
|
mov cfg_tail,0 ; force a read
|
|
func_chain10:
|
|
ret
|
|
|
|
func_switch: ; SWITCH=option0, option1
|
|
; GOSUB to appropriate label
|
|
call wait_for_key ; wait until a key is pressed
|
|
jc func_switch01 ; ignore if timeout
|
|
mov ah,MS_C_RAWIN
|
|
int DOS_INT ; read a char
|
|
cmp al,CR
|
|
jne func_switch02
|
|
func_switch01:
|
|
mov al,default_switch_char ; use default character
|
|
func_switch02:
|
|
cmp al,'0' ; ignore if < '0'
|
|
jb func_switch
|
|
cmp al,'9' ; or > '9'
|
|
ja func_switch
|
|
sub al,'1' ; convert from ASCII
|
|
jns func_switch05
|
|
mov al,10 ; make '0' into 10
|
|
func_switch05:
|
|
cbw ; AX = lines to skip
|
|
xchg ax,cx ; make CX the loop count
|
|
jcxz func_switch30
|
|
mov bx,si ; BX -> saved command line start
|
|
func_switch10:
|
|
push bx
|
|
push cx
|
|
mov di,offset dev_name ; copy and discard a label
|
|
call copy_file
|
|
pop cx
|
|
pop bx
|
|
jc func_switch40 ; ignore if any problems
|
|
push bx
|
|
push cx
|
|
call separator ; look for ','
|
|
pop cx
|
|
pop bx
|
|
jc func_switch40 ; stop at end of line
|
|
loop func_switch10
|
|
func_switch30:
|
|
jmp func_gosub ; execute a GOSUB
|
|
|
|
func_switch40:
|
|
mov si,bx ; retract to start of line
|
|
jmps func_switch ; then back to sleep again
|
|
|
|
func_gosub: ; GOSUB="label"
|
|
;----------
|
|
pop ax ; get return address
|
|
mov bx,cfg_seeklo ; get existing offset
|
|
mov cx,cfg_seekhi ; in CONFIG file
|
|
sub bx,cfg_tail ; work out begining of buffer
|
|
sbb cx,0
|
|
add bx,cfg_head ; add in current offset in buffer
|
|
adc cx,0
|
|
push bx ; save as position to RETURN to
|
|
push cx
|
|
push ax ; save return address again
|
|
call func_goto ; try to GOTO label
|
|
jc func_return
|
|
ret ; RET, with old offset on stack
|
|
|
|
func_return: ; RETURN [n]
|
|
;-----------
|
|
pop bx ; get return address
|
|
cmp sp,save_sp ; is anything on stack ?
|
|
jae func_return20 ; no, cannot RETURN
|
|
pop cfg_seekhi
|
|
pop cfg_seeklo ; restore position in file
|
|
mov cfg_tail,0 ; force a read
|
|
push bx
|
|
call atoi ; returning a value ?
|
|
pop bx
|
|
jnc func_return10 ; default to 0
|
|
xor ax,ax
|
|
func_return10:
|
|
mov error_level,ax ; return result in error level
|
|
func_return20:
|
|
push bx ; save return address
|
|
ret ; and return to it
|
|
|
|
func_goto: ; GOTO="label"
|
|
;---------
|
|
mov di,offset dev_name ; Copy the label into a
|
|
mov byte ptr [di],0 ; local buffer and zero terminate
|
|
call copy_file
|
|
jc func_goto10 ; ignore if any problems
|
|
|
|
mov cfg_seeklo,0 ; Seek to start of file
|
|
mov cfg_seekhi,0
|
|
mov cfg_tail,0 ; force a re-read
|
|
|
|
func_goto5:
|
|
call readline ; read in a line
|
|
jc func_goto10 ; stop if end of file
|
|
call strupr ; upper case possible label
|
|
mov bx,offset cfg_buffer
|
|
cmp ds:byte ptr [bx],':' ; is it a label ?
|
|
jne func_goto5 ; no, try next line
|
|
mov si,offset dev_name
|
|
func_goto6:
|
|
inc bx ; next char in possible label
|
|
lodsb ; get a character
|
|
test al,al ; end of label ?
|
|
je func_goto10 ; we have a match !
|
|
cmp al,ds:byte ptr [bx] ; does it match
|
|
jne func_goto5 ; no, try next line
|
|
jmps func_goto6 ; yes, look at next character
|
|
func_goto10:
|
|
ret
|
|
|
|
func_exit:
|
|
; Stop processing CONFIG.SYS
|
|
call readline ; read in a line
|
|
jnc func_exit ; until we can read no more..
|
|
ret
|
|
|
|
func_cls:
|
|
; CLEAR SCREEN
|
|
; This is PC specific - sorry
|
|
mov ah,15 ; get current
|
|
int 10h ; screen mode
|
|
mov ah,0
|
|
int 10h ; reset it to clear screen
|
|
ret
|
|
|
|
func_cpos:
|
|
; Set cursor position
|
|
call atoi ; AX = row
|
|
jnc func_cpos10 ; check for error
|
|
xor ax,ax ; default to top left
|
|
jmps func_cpos40
|
|
func_cpos10:
|
|
push ax ; save row
|
|
call separator ; look for ','
|
|
jc func_cpos20 ; no col specified
|
|
call atoi ; get col
|
|
jnc func_cpos30
|
|
func_cpos20:
|
|
mov ax,1 ; default to left
|
|
func_cpos30:
|
|
pop dx
|
|
mov ah,dl ; AH = row, AL = col
|
|
sub ax,0101h ; compensate for being one based
|
|
func_cpos40:
|
|
xchg ax,dx ; DH = row, DL = col
|
|
xor bx,bx ; page zero
|
|
mov ah,2 ; set cursor position
|
|
int 10h ; Eeeek!! call the ROS
|
|
ret
|
|
|
|
func_timeout:
|
|
; set TIMEOUT for keyboard input
|
|
call atoi ; AX = # timeout count
|
|
jnc func_timeout10 ; check for error
|
|
xor ax,ax ; bad values mean no timeout
|
|
func_timeout10:
|
|
mov keyb_timeout,ax ; save timeout count
|
|
call separator ; look for ','
|
|
jc func_timeout20
|
|
lodsb ; get default query char
|
|
cmp al,LF ! je func_timeout20
|
|
cmp al,CR ! je func_timeout20
|
|
mov default_query_char,al
|
|
call separator ; look for ','
|
|
jc func_timeout20
|
|
lodsb ; get default switch char
|
|
cmp al,CR ! je func_timeout20
|
|
cmp al,LF ! je func_timeout20
|
|
mov default_switch_char,al
|
|
func_timeout20:
|
|
ret
|
|
|
|
func_error:
|
|
; ERROR='n'
|
|
call atoi ; AX = error count to match
|
|
jc func_error10
|
|
mov error_level,ax ; set error level
|
|
func_error10:
|
|
ret
|
|
|
|
func_onerror:
|
|
; ONERROR='n' optional command
|
|
;
|
|
call whitespace ; Scan off all white space
|
|
xor bx,bx ; index relationship = 1st item
|
|
xor dx,dx ; DX is bit to set
|
|
func_onerror10:
|
|
or bx,dx ; set reationship bit
|
|
lodsb ; now process a character
|
|
mov dx,2
|
|
cmp al,'=' ; if '=' set bit 1
|
|
je func_onerror10
|
|
mov dx,4
|
|
cmp al,'<' ; if '<' set bit 2
|
|
je func_onerror10
|
|
mov dx,8
|
|
cmp al,'>' ; if '>' set bit 3
|
|
je func_onerror10
|
|
dec si ; point at char
|
|
push bx ; save relationship
|
|
call atoi ; AX = error count to match
|
|
pop bx ; recover relationship
|
|
jc func_onerror20
|
|
cmp error_level,ax ; is it the error level we want ?
|
|
jmp func_onerror_tbl[bx] ; jump to handler
|
|
func_onerror20:
|
|
ret
|
|
|
|
func_onerror_tbl:
|
|
dw func_onerror_eq ; . . .
|
|
dw func_onerror_eq ; . . =
|
|
dw func_onerror_lt ; . < .
|
|
dw func_onerror_le ; . < =
|
|
dw func_onerror_gt ; > . .
|
|
dw func_onerror_ge ; > . =
|
|
dw func_onerror_ne ; > < .
|
|
dw func_onerror_take ; > < =
|
|
|
|
func_onerror_eq:
|
|
je func_onerror_take
|
|
ret
|
|
|
|
func_onerror_ne:
|
|
jne func_onerror_take
|
|
ret
|
|
|
|
func_onerror_lt:
|
|
jb func_onerror_take
|
|
ret
|
|
|
|
func_onerror_le:
|
|
jbe func_onerror_take
|
|
ret
|
|
|
|
func_onerror_gt:
|
|
ja func_onerror_take
|
|
ret
|
|
|
|
func_onerror_ge:
|
|
jae func_onerror_take
|
|
ret
|
|
|
|
func_onerror_take:
|
|
pop ax ; discard return address
|
|
xor ax,ax ; boot key options = none
|
|
jmp cfg_continue ; and execute this command
|
|
|
|
|
|
|
|
func_query:
|
|
; ?optional command
|
|
cmp boot_options,F5KEY ; if F5 has been pressed then
|
|
je func_query50 ; do nothing
|
|
call whitespace ; discard any following whitespace
|
|
lodsb ; get a character
|
|
cmp al,'?' ; is it another '?', is so swallow it
|
|
je func_query ; and go round again
|
|
dec si ; it wasn't a '?', forget we looked
|
|
push si ; save current position
|
|
lodsb ; get next real char
|
|
xor cx,cx ; assume no prompt string
|
|
cmp al,'"' ; '?"user prompt"' - keep silent as
|
|
jne func_query10 ; user has supplied prompt
|
|
xchg ax,cx ; CL = " if user prompt
|
|
lodsb
|
|
func_query10:
|
|
cmp al,cl ; is this the user prompt char ?
|
|
je func_query20 ; then stop now
|
|
xchg ax,dx ; DL= character
|
|
mov ah,MS_C_WRITE
|
|
int DOS_INT ; output the character
|
|
lodsb
|
|
cmp al,CR ; is it the end of the line ?
|
|
jne func_query10 ; no, do another character
|
|
mov ah,MS_C_WRITESTR ; Output msg of form " (Y/N) ? "
|
|
mov dx,offset confirm_msg1
|
|
int DOS_INT ; do " ("
|
|
mov ah,MS_C_WRITE
|
|
mov dl,yes_char
|
|
int DOS_INT ; do "Y"
|
|
mov dl,'/'
|
|
int DOS_INT ; do "/"
|
|
mov dl,no_char
|
|
int DOS_INT ; do "N"
|
|
mov ah,MS_C_WRITESTR
|
|
mov dx,offset confirm_msg2
|
|
int DOS_INT ; do ") ? "
|
|
func_query20:
|
|
jcxz func_query30 ; if no user supplied prompt
|
|
pop ax ; don't discard original starting
|
|
push si ; position
|
|
func_query30:
|
|
call wait_for_key ; wait until a key is pressed
|
|
mov al,default_query_char ; if we timeout default
|
|
jc func_query40
|
|
mov ah,MS_C_RAWIN
|
|
int DOS_INT ; read a char
|
|
test al,al ; is it a function key ?
|
|
jnz func_query40
|
|
mov ah,MS_C_RAWIN
|
|
int DOS_INT ; throw away function keys
|
|
jmps func_query30
|
|
func_query40:
|
|
push ax ; save response
|
|
mov ah,MS_C_WRITE
|
|
mov dl,al
|
|
int DOS_INT ; echo the char
|
|
mov ah,MS_C_WRITESTR
|
|
mov dx,offset confirm_msg3
|
|
int DOS_INT ; now do CR/LF to tidy up
|
|
pop ax
|
|
call toupper ; make response upper case
|
|
pop si ; recover starting position
|
|
cmp al,yes_char
|
|
jne func_query50
|
|
pop ax ; Discard Return Address
|
|
xor ax,ax ; boot key options = none
|
|
jmp cfg_continue ; Execute the command
|
|
func_query50:
|
|
ret ; Return without Executing Command
|
|
|
|
|
|
|
|
func_getkey: ; GETKEY
|
|
call wait_for_key ; wait until a key is pressed
|
|
mov ax,CONFIG_ERRLVL ; assume we have timed out
|
|
jc func_getkey10 ; ignore if timeout
|
|
mov ah,MS_C_RAWIN
|
|
int DOS_INT ; read a char
|
|
xor ah,ah ; convert to word
|
|
func_getkey10:
|
|
mov error_level,ax
|
|
ret
|
|
|
|
|
|
; CONFIG_ERROR is the global error handler for the CONFIG.SYS
|
|
; commands. It is called with SI pointing to the CR/LF terminated string
|
|
; that caused the error and with DX pointing to an "informative" error
|
|
; message.
|
|
;
|
|
; On Entry:- AL Terminating Character
|
|
; DX Offset of Error Message
|
|
; SI 0000 No Message to display
|
|
; Offset of AL terminated string
|
|
;
|
|
config_error:
|
|
|
|
if ADDDRV
|
|
mov error_flag,1
|
|
endif
|
|
|
|
push ax
|
|
mov ah,MS_C_WRITESTR ; Print the Error Message
|
|
int DOS_INT ; passed in DX
|
|
pop ax
|
|
|
|
mov ah,al ; AH = terminating character
|
|
test si,si ; display the failing string ?
|
|
jz cfg_e20 ; YES then scan for terminator
|
|
cfg_e10:
|
|
lodsb ; get char to display
|
|
cmp al,ah ; have we reached the terminator ?
|
|
je cfg_e20
|
|
xchg ax,dx ; DL = character to display
|
|
mov ah,MS_C_WRITE ; print a character at a time
|
|
int DOS_INT
|
|
xchg ax,dx ; terminator back in AH
|
|
jmps cfg_e10
|
|
cfg_e20:
|
|
;; jmp crlf ; Terminate with a CRLF
|
|
|
|
Public crlf
|
|
crlf:
|
|
push dx
|
|
mov dx,offset msg_crlf ; Print a CR LF
|
|
mov ah,MS_C_WRITESTR
|
|
int DOS_INT
|
|
pop dx
|
|
ret
|
|
|
|
;
|
|
; Scan the command table for a match with the first entry in the
|
|
; CR/LF terminated string passed in SI
|
|
scan:
|
|
call whitespace ; scan off all white space
|
|
push bx ; save the CONFIG Handle
|
|
mov bx,offset cfg_table - CFG_SIZE
|
|
|
|
scan_10:
|
|
add bx,CFG_SIZE ; bx -> next entry in table
|
|
mov di,CFG_NAME[bx] ; es:di -> next entry name
|
|
test di,di ; end of table ?
|
|
stc ; assume so
|
|
jz scan_exit ; Yes Exit with the Carry Flag Set
|
|
push si ; Save the String Offset
|
|
call compare
|
|
pop ax ; Remove String Address
|
|
jnc scan_20 ; String Matched
|
|
xchg ax,si ; Restore the original String Address
|
|
jmps scan_10 ; and test the next entry
|
|
|
|
scan_20:
|
|
and CFG_FLAGS[bx],not CF_QUERY
|
|
test CFG_FLAGS[bx],CF_LC ; should we upper case line ?
|
|
jnz scan_50 ; skip if not
|
|
xchg ax,si
|
|
call strupr ; upper case the command line
|
|
xchg ax,si
|
|
scan_30:
|
|
call whitespace ; Scan off all white space before and
|
|
lodsb ; after the option '=' character
|
|
cmp al,'?' ; are we querying things ?
|
|
jne scan_40
|
|
or CFG_FLAGS[bx],CF_QUERY ; remember the query, now go and
|
|
jmps scan_30 ; remove any other whitespace
|
|
scan_40:
|
|
cmp al,'=' ; '=' character.
|
|
je scan_30
|
|
dec si
|
|
scan_50:
|
|
mov di,bx ; Save the Table Entry
|
|
xor ax,ax ; and exit with the Carry Flag Reset
|
|
scan_exit:
|
|
pop bx
|
|
ret
|
|
|
|
; Compare two strings in case insensitive manner
|
|
; On Entry:
|
|
; ds:si -> String 1 (upper/lower case, length determined by string 2)
|
|
; es:di -> String 2 (uppercase, null terminated)
|
|
; On Exit:
|
|
; Carry clear: strings are the same
|
|
; ds:si -> character immediately following end of string 1
|
|
; es:di -> character immediately following end on string 2
|
|
;
|
|
; Carry set: strings different
|
|
; ds:si -> As on entry
|
|
; es:di -> undefined
|
|
;
|
|
compare:
|
|
;-------
|
|
push bx
|
|
push si ; save starting position
|
|
compare10:
|
|
mov al,es:[di] ; al = next character
|
|
inc di
|
|
test al,al ; end of string 2 yet ?
|
|
jz compare40 ; yes, strings must be equal
|
|
call dbcs_lead ; DBCS lead byte?
|
|
jnz compare20 ; no
|
|
mov ah,al
|
|
lodsb ; is 1st byte of pair the same ?
|
|
cmp al,ah
|
|
jne compare30
|
|
cmpsb ; is 2nd byte of pair equal ?
|
|
jne compare30
|
|
jmps compare10
|
|
compare20:
|
|
call toupper ; just uppercase this byte
|
|
xchg ax,bx ; BL = string2 character
|
|
lodsb ; al = next char in string 1
|
|
call toupper ; (can't be KANJI if it matches)
|
|
cmp al,bl ; check the characters are
|
|
je compare10 ; identical stop the compare
|
|
compare30:
|
|
stc ; on a mismatch and set CY
|
|
compare40:
|
|
pop bx ; recover starting position
|
|
jnc compare50
|
|
mov si,bx ; SI = original start
|
|
compare50:
|
|
pop bx
|
|
ret
|
|
|
|
|
|
separator:
|
|
;---------
|
|
; On Entry:
|
|
; DS:SI -> string
|
|
; On Exit:
|
|
; DS:SI -> next option
|
|
; CY set if end of line
|
|
;
|
|
; Strips off all whitespace, and the optional ','
|
|
; CY set at end of line
|
|
call whitespace ; deblank string and
|
|
lodsb ; check for ',' separator
|
|
cmp al,',' ; discarding if found
|
|
je separator10
|
|
cmp al,CR ; end of the line ?
|
|
stc ; assume so
|
|
je separator10
|
|
dec si ; something else, leave alone
|
|
clc ; not end of line
|
|
separator10:
|
|
ret
|
|
|
|
separator20:
|
|
call whitespace ; strip of following spaces
|
|
clc ; not end of line
|
|
ret
|
|
|
|
|
|
strupr:
|
|
;------
|
|
; Uppercase a null terminated string.
|
|
; Entry
|
|
; ds:si -> null terminated string
|
|
; Exit
|
|
; none (string is uppercased)
|
|
; Lost
|
|
; no registers changed
|
|
|
|
|
|
push si
|
|
push ax
|
|
|
|
spr_loop:
|
|
mov al, [si] ; al = next byte from string
|
|
test al, al ; end of string?
|
|
jz spr_done ; yes - exit
|
|
|
|
; cmp al,' ' ; BAP. End at first space
|
|
; je spr_done ; or comma or slash
|
|
; cmp al,',' ; so that parameters
|
|
; je spr_done ; are not uppercased
|
|
; cmp al,'/' ; Took out again cos it caused
|
|
; je spr_done ; problems with labels (I think).
|
|
|
|
call dbcs_lead ; DBCS lead byte?
|
|
jnz spr_not_dbcs ; no
|
|
inc si ; yes - skip first and second bytes of
|
|
inc si ; pair as they cannot be uppercased
|
|
jmp spr_loop ; loop round
|
|
spr_not_dbcs:
|
|
|
|
call toupper ; just uppercase this byte
|
|
mov [si], al ; return the result to the string
|
|
inc si
|
|
jmp spr_loop ; continue
|
|
|
|
spr_done:
|
|
pop ax
|
|
pop si
|
|
ret
|
|
|
|
|
|
dbcs_lead:
|
|
;---------
|
|
; Return true if given byte is the first of a double byte character.
|
|
; Entry
|
|
; al = byte to be tested
|
|
; Exit
|
|
; Z Flag = 1 - byte is a DBCS lead
|
|
; 0 - byte is not a DBCS lead
|
|
; Lost
|
|
; no registers changed
|
|
|
|
|
|
push ds
|
|
push si
|
|
push bx
|
|
push ax
|
|
|
|
; First get a pointer to the double byte lead table in the COUNTRY info.
|
|
lds si, dbcs_tbl ; ds:si -> double byte table
|
|
inc si
|
|
inc si ; skip table length
|
|
|
|
; Examine each entry in the table to see if it defines a range that includes
|
|
; the given character.
|
|
mov bl, al ; bl = byte to be tested
|
|
dbcs_loop:
|
|
lodsw ; al/ah = start/end of range
|
|
test ax, ax ; end of table?
|
|
jz dbcs_no ; yes - exit (not in table)
|
|
cmp al, bl ; start <= bl?
|
|
ja dbcs_loop ; no - try next range
|
|
cmp ah, bl ; bl <= end?
|
|
jb dbcs_loop ; no - try next range
|
|
|
|
cmp al, al ; return with Z flag set
|
|
jmp dbcs_exit
|
|
|
|
dbcs_no:
|
|
cmp al, 1 ; return with Z flag reset
|
|
|
|
dbcs_exit:
|
|
pop ax
|
|
pop bx
|
|
pop si
|
|
pop ds
|
|
ret
|
|
|
|
|
|
|
|
toupper:
|
|
;-------
|
|
; Return the uppercase equivilant of the given character.
|
|
; The uppercase function defined in the international info block is
|
|
; called for characters above 80h.
|
|
; Entry
|
|
; al = character to uppercase
|
|
; Exit
|
|
; al uppercased
|
|
; Lost
|
|
; no registers lost
|
|
|
|
push bx
|
|
mov bh, ah
|
|
mov ah, 0 ; ax = character to be converted
|
|
cmp al, 'a' ; al < 'a'?
|
|
jb exit_toupper ; yes - done (char unchanged)
|
|
cmp al, 'z' ; al <= 'z'?
|
|
jbe a_z ; yes - do ASCII conversion
|
|
cmp al, 80h ; international char?
|
|
jb exit_toupper ; no - done (char unchanged)
|
|
|
|
; ch >= 80h -- call international routine
|
|
callf dword ptr ctry_info+CI_CASEOFF
|
|
jmp exit_toupper
|
|
|
|
a_z:
|
|
; 'a' <= ch <= 'z' -- convert to uppercase ASCII equivilant
|
|
and al, 0DFh
|
|
|
|
exit_toupper:
|
|
mov ah, bh
|
|
pop bx
|
|
ret
|
|
|
|
|
|
|
|
|
|
;
|
|
; Scan the string DS:SI for ON or OFF return with the carry flag set
|
|
; on error or AL = 1 for ON and AL = 0 for OFF.
|
|
;
|
|
check_onoff:
|
|
call whitespace ; Deblank Command
|
|
push si
|
|
mov di,offset cmd_on ; es:di -> "ON"
|
|
call compare ; do we have an "ON"?
|
|
mov al,01 ; Assume ON found
|
|
jnc chk_onoff10
|
|
|
|
pop si ! push si ; Save String Location in Case of Error
|
|
mov di,offset cmd_off ; es:di -> "OFF"
|
|
call compare ; do we have an "OFF"?
|
|
mov al,00
|
|
jnc chk_onoff10
|
|
pop si ; No match so return original address
|
|
stc ; with the CARRY falg set.
|
|
ret
|
|
|
|
chk_onoff10:
|
|
pop di ; Remove Old String address
|
|
ret ; and return to caller
|
|
|
|
atohex:
|
|
;------
|
|
; To convert a hex number in the form of an ASCII string to a 32 bit
|
|
; integer.
|
|
;
|
|
; On Entry:
|
|
; DS:SI -> ASCII hex number
|
|
; (the end of the number is taken as the first non-digit)
|
|
; On Exit:
|
|
; CY clear:
|
|
; DX:AX = converted number
|
|
; ds:si -> first non-digit
|
|
;
|
|
; CY set:
|
|
; Either the first character was not a digit
|
|
; or the number could not be represented in 32 bits
|
|
; ds:si -> point at which error occured
|
|
; ax undefined
|
|
; Lost
|
|
; no other register
|
|
|
|
push bx
|
|
push cx
|
|
push di
|
|
call whitespace ; Deblank Line
|
|
mov di,si ; save string start offset
|
|
xor dx,dx
|
|
xor bx,bx ; number is formed in DX:BX
|
|
|
|
atohex10:
|
|
lodsb ; AL = next char from string
|
|
call toupper ; upper case it
|
|
cmp al,'A'
|
|
jb atohex20
|
|
cmp al,'F'
|
|
ja atohex20
|
|
sub al,'A'-10
|
|
jmps atohex30
|
|
atohex20:
|
|
sub al, '0'
|
|
jc atohex40 ; stop if invalid character
|
|
cmp al, 9
|
|
ja atohex40
|
|
atohex30:
|
|
cbw ; AX = digit
|
|
test dh,0f0h ; will we overflow ?
|
|
jnz atohex_error
|
|
mov cl,4
|
|
push bx ; save (top 4 bits)
|
|
shl bx,cl ; *16
|
|
add bx,ax ; add in new digit
|
|
pop ax
|
|
rol ax,cl ; top 4 bits to bottom 4 bits
|
|
and ax,000Fh ; isolate them
|
|
shl dx,cl
|
|
add dx,ax ; add in new digit
|
|
jmp atohex10
|
|
|
|
atohex40:
|
|
dec si ; forget the char we stopped on
|
|
cmp si, di ; was there at least one digit?
|
|
ja atohex50 ; yes - exit with carry clear
|
|
atohex_error:
|
|
stc ; set error flag
|
|
atohex50:
|
|
xchg ax,bx ; AX = result
|
|
pop di
|
|
pop cx
|
|
pop bx
|
|
ret
|
|
|
|
atoi:
|
|
;----
|
|
; To convert a decimal number in the form of an ASCII string to a 16 bit
|
|
; integer.
|
|
;
|
|
; Entry
|
|
; ds:si -> ASCII decimal number
|
|
; (the end of the number is taken as the first non-digit)
|
|
; Exit
|
|
; Carry clear:
|
|
; ax = converted number
|
|
; ds:si -> first non-digit
|
|
;
|
|
; Carry set:
|
|
; Either the first character was not a digit
|
|
; or the number could not be represented in 16 bits
|
|
; ds:si -> point at which error occured
|
|
; ax undefined
|
|
; Lost
|
|
; no other register
|
|
|
|
push bx ! push cx
|
|
push dx ! push di
|
|
call whitespace ; Deblank Line
|
|
mov di, si ; save string start offset
|
|
mov cx, 10 ; for multiply
|
|
xor ax, ax ; number is formed in ax
|
|
|
|
atoi_loop:
|
|
mov bl, [si] ; bl = next char from string
|
|
sub bl, '0'
|
|
jc atoi_done
|
|
cmp bl, 9
|
|
ja atoi_done
|
|
mov bh, 0 ; bx = next digit
|
|
|
|
mul cx ; ax = 10 * ax
|
|
jc exit_atoi ; check for 16 bit overflow
|
|
add ax, bx ; ax = (10 * ax) + bx
|
|
jc exit_atoi
|
|
|
|
inc si ; ds:si -> next char in string
|
|
jmp atoi_loop
|
|
|
|
atoi_done:
|
|
cmp si, di ; was there at least one digit?
|
|
jne exit_atoi ; yes - exit with carry clear
|
|
stc ; no - set error flag
|
|
|
|
exit_atoi:
|
|
pop di ! pop dx
|
|
pop cx ! pop bx
|
|
ret
|
|
|
|
atol:
|
|
;----
|
|
; To convert a decimal number in the form of an ASCII string to a 32 bit
|
|
; integer.
|
|
;
|
|
; Entry
|
|
; ds:si -> ASCII decimal number
|
|
; (the end of the number is taken as the first non-digit)
|
|
; Exit
|
|
; CY clear:
|
|
; DX:AX = converted number
|
|
; ds:si -> first non-digit
|
|
;
|
|
; CY set:
|
|
; Either the first character was not a digit
|
|
; or the number could not be represented in 32 bits
|
|
; ds:si -> point at which error occured
|
|
; ax undefined
|
|
; Lost
|
|
; no other register
|
|
|
|
push bx
|
|
push cx
|
|
push di
|
|
call whitespace ; Deblank Line
|
|
mov di, si ; save string start offset
|
|
xor ax, ax ; number is formed in
|
|
cwd ; DX/AX
|
|
|
|
atol10:
|
|
xor bx,bx ; use CX/BX for next digit
|
|
xor cx,cx
|
|
mov bl,[si] ; BL = next char from string
|
|
sub bl,'0'
|
|
jc atol20
|
|
cmp bl, 9 ; validate digit
|
|
ja atol20
|
|
add ax,ax
|
|
adc dx,dx ; * 2
|
|
jc atol30
|
|
add bx,ax ; * 2 + new digit
|
|
adc cx,dx
|
|
jc atol30
|
|
add ax,ax
|
|
adc dx,dx ; * 4
|
|
jc atol30
|
|
add ax,ax
|
|
adc dx,dx ; * 8
|
|
jc atol30
|
|
add ax,bx ; * 10 + new digit
|
|
add dx,cx
|
|
jc atol30
|
|
inc si ; ds:si -> next char in string
|
|
jmp atol10
|
|
|
|
atol20:
|
|
cmp si,di ; was there at least one digit?
|
|
jne atol30 ; yes - exit with carry clear
|
|
stc ; no - set error flag
|
|
atol30:
|
|
pop di
|
|
pop cx
|
|
pop bx
|
|
ret
|
|
|
|
readline:
|
|
;--------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; DS:SI -> line in buffer
|
|
; CY set if we have a problem (eg at EOF)
|
|
;
|
|
mov cx,CFG_BUF_LEN-2 ; Read the next command line
|
|
mov di,offset cfg_buffer ; into the CFG_BUFFER
|
|
mov si,di ; Save the Destination String
|
|
; address
|
|
read_l10:
|
|
call getchar ; al = next char from file
|
|
cmp al,CR ! jz read_l10 ; end of line ?
|
|
cmp al,LF ! jz read_l10 ; end of line ?
|
|
cmp al,EOF ! jne read_l20 ; end of file ?
|
|
stc ; indicate a problem
|
|
ret
|
|
|
|
read_l20:
|
|
stosb ; put next char into the buffer
|
|
call getchar ; al = next char from file
|
|
|
|
cmp al,EOF ! jz read_l30 ; end of file ?
|
|
cmp al,CR ! jz read_l30 ; end of line ?
|
|
cmp al,LF ! jz read_l30 ; end of line ?
|
|
loop read_l20 ; loop while space remains
|
|
|
|
; If we fall through to this point the line is too long. Make it a comment.
|
|
mov di, si ; ds:di -> start of buffer
|
|
mov al, ';'
|
|
stosb ; place ';' at buffer start
|
|
mov cx, 1 ; get another one character
|
|
jmps read_l20 ; loop until all of this line consumed
|
|
|
|
; At this point buffer contains a line of text from CCONFIG.SYS.
|
|
; Terminate it properly
|
|
read_l30:
|
|
mov al,CR ! stosb ; terminate line with CR
|
|
mov al,LF ! stosb ; and a LF
|
|
xor al,al ! stosb ; Reset the Carry Flag
|
|
ret
|
|
|
|
getchar:
|
|
mov bx,cfg_head ; we are here in the buffer
|
|
cmp bx,cfg_tail ; are there any more characters ?
|
|
jae getchar10 ; no, read some in from disk
|
|
push ds
|
|
mov ds,init_dseg
|
|
mov al,CONFIG_BUF[bx] ; get a character from the buffer
|
|
pop ds
|
|
inc cfg_head ; inc the pointer
|
|
ret
|
|
|
|
getchar10:
|
|
; we need to read some characters from disk into our buffer
|
|
push cx ! push dx ; Assume something will go wrong
|
|
mov cfg_tail,0 ; say nothing is in the buffer
|
|
|
|
mov ax,(MS_X_OPEN*256)+80h ; Open the configuration file
|
|
mov dx,offset cfg_file
|
|
int DOS_INT
|
|
jc getchar40 ; failure, return EOF
|
|
|
|
mov bx,ax
|
|
mov ax,(MS_X_LSEEK*256)+0
|
|
mov dx,cfg_seeklo
|
|
mov cx,cfg_seekhi
|
|
int DOS_INT ; seek to current file position
|
|
jc getchar30 ; failure to seek, close and exit
|
|
|
|
mov ah,MS_X_READ
|
|
mov cx,CONFIG_BUF_SIZE
|
|
push ds
|
|
mov ds,init_dseg
|
|
mov dx,offset CONFIG_BUF ; lets try and fill out buffer
|
|
int DOS_INT
|
|
pop ds
|
|
jc getchar30
|
|
mov cfg_tail,ax
|
|
mov ax,(MS_X_LSEEK*256)+1
|
|
xor dx,dx
|
|
xor cx,cx
|
|
int DOS_INT ; get current file position
|
|
mov cfg_seeklo,ax ; and save for possible
|
|
mov cfg_seekhi,dx ; future re-opens
|
|
getchar30:
|
|
mov ah,MS_X_CLOSE ; Close the CONFIG file
|
|
int DOS_INT
|
|
getchar40:
|
|
mov bx,cfg_tail ; now lets see if we filled the buffer
|
|
cmp bx,CONFIG_BUF_SIZE ; if not its EOF so mark it as such
|
|
je getchar50
|
|
push ds
|
|
mov ds,init_dseg
|
|
mov CONFIG_BUF[bx],EOF ; add an EOF mark
|
|
pop ds ; in case there isn't one already
|
|
inc cfg_tail
|
|
inc cfg_seeklo
|
|
jnz getchar50
|
|
inc cfg_seekhi
|
|
getchar50:
|
|
push ds
|
|
mov ds,init_dseg
|
|
mov al,CONFIG_BUF ; return 1st char from buffer
|
|
pop ds
|
|
mov cfg_head,1 ; remember we have returned char
|
|
pop dx ! pop cx
|
|
ret
|
|
|
|
|
|
;
|
|
; On a DEVICEHIGH we have encountered a line
|
|
; /L:r1[,s1][;r2[,s2]]... [/S]
|
|
; where r1 = load region, s1 = hex size in bytes, r2,s2 etc are further regions
|
|
; currently only r1/s1 are supported
|
|
; /S says the regions should m#be minimised
|
|
|
|
parse_region:
|
|
;On Entry:
|
|
; DS:SI -> command line following '/L:'
|
|
; On Exit:
|
|
; DS:SI -> 1st non-parsed character
|
|
; CY set on error
|
|
;
|
|
call atoi ; get a region to load in
|
|
jc parse_region40
|
|
mov himem_region,ax ; remember region to try
|
|
call whitespace ; scan off all white space
|
|
lodsb ! dec si ; now see is we have an optional size
|
|
cmp al,',' ; have we a ',' character ?
|
|
mov ax,0 ; assume minimum size not supplied
|
|
jne parse_region30
|
|
inc si
|
|
call atol ; read number into DX:AX
|
|
cmp dx,15 ; is number too big ?
|
|
ja parse_region40
|
|
mov cx,16 ; convert to para's
|
|
div cx
|
|
inc ax ; allow for round up
|
|
inc ax ; and for header
|
|
push ax ; save size of region
|
|
parse_region10:
|
|
mov di,offset slashs_opt ; do we have a "/S" to minimise
|
|
call compare ; the UMB's (ignore it if so)
|
|
jnc parse_region20
|
|
call whitespace ; scan off all white space
|
|
lodsb ! dec si ; strip off other regions
|
|
cmp al,';' ; another region follows ';'
|
|
jne parse_region20
|
|
inc si
|
|
call atoi ; eat the region number
|
|
jc parse_region20
|
|
call whitespace ; scan off all white space
|
|
lodsb ! dec si
|
|
cmp al,',' ; is a size specified ?
|
|
jne parse_region10 ; no, check for another region
|
|
inc si
|
|
call atol ; eat the size
|
|
jnc parse_region10
|
|
parse_region20:
|
|
pop ax
|
|
parse_region30:
|
|
clc ; we can proceed
|
|
ret
|
|
|
|
parse_region40:
|
|
mov himem_size,0FFFFh ; 1 MByte wanted (ho, ho)
|
|
stc ; we had problems..
|
|
ret
|
|
|
|
; On a DEVICEHIGH we may encounter a line
|
|
; SIZE [=] s
|
|
; where s = size of region in hex bytes
|
|
|
|
parse_size:
|
|
;On Entry:
|
|
; DS:SI -> command line following '/L:'
|
|
; On Exit:
|
|
; DS:SI -> 1st non-parsed character
|
|
; CY set on error
|
|
;
|
|
call whitespace ; Scan off all white space
|
|
lodsb ; before and after the optional
|
|
cmp al,'=' ; '=' character.
|
|
je parse_size
|
|
dec si
|
|
call atohex ; read hex number into DX:AX
|
|
jc parse_size10
|
|
cmp dx,15 ; is number too big ?
|
|
ja parse_size20 ; just load low
|
|
mov cx,16 ; convert to para's
|
|
div cx
|
|
inc ax ; allow for round up
|
|
inc ax ; and for header
|
|
mov himem_size,ax ; remember size required
|
|
parse_size10:
|
|
clc
|
|
ret
|
|
|
|
parse_size20:
|
|
mov himem_size,0FFFFh ; 1 MByte wanted (ho, ho)
|
|
stc
|
|
ret
|
|
|
|
|
|
; A size has not been suppleied with DEVICEHIGH, so guess-timate one
|
|
; based on file size
|
|
|
|
size_file:
|
|
; On Entry:
|
|
; DS:SI -> filename
|
|
; On Exit:
|
|
; DS:SI preserved
|
|
;
|
|
push si
|
|
mov di,offset dev_name ; copy the device filename into a
|
|
mov byte ptr [di],0 ; local buffer and zero terminate
|
|
call copy_file
|
|
pop si
|
|
mov ax,(MS_X_OPEN * 256)+0 ; open file r/o
|
|
mov dx,offset dev_name
|
|
int DOS_INT
|
|
jnc size_file10
|
|
mov ax,0FFFFh ; can't open file, force low to prevent
|
|
ret ; two sets of error messages
|
|
|
|
size_file10:
|
|
xchg ax,bx ; handle in BX
|
|
mov ah,MS_X_READ
|
|
mov cx,EXE_LENGTH
|
|
mov dx,offset exeBuffer
|
|
int DOS_INT ; read in possible exe header
|
|
jc size_file20
|
|
cmp ax,cx ; did we read all we wanted ?
|
|
jb size_file40 ; if not it can't be an EXE
|
|
cmp exeSignature,'ZM' ; check the signature
|
|
jne size_file40 ; if invalid can't be an EXE
|
|
mov ax,512
|
|
mul exeSize ; DX/AX bytes in image
|
|
add ax,exeFinal
|
|
adc dx,0
|
|
mov cx,16
|
|
cmp dx,cx ; are we too big ?
|
|
jae size_file20 ; yes, force low
|
|
div cx ; AX = para's required
|
|
inc ax ; one for rounding error
|
|
jz size_file20
|
|
add ax,exeMinpara ; add on extra para's required
|
|
jnc size_file30
|
|
size_file20:
|
|
mov ax,0FFFFh ; problems, force a load low
|
|
size_file30:
|
|
push ax ; save para's required
|
|
mov ah,MS_X_CLOSE ; close this file
|
|
int DOS_INT
|
|
pop ax ; AX para's required
|
|
ret
|
|
|
|
size_file40:
|
|
mov ax,(MS_X_LSEEK * 256)+2
|
|
xor cx,cx ; now find out how big the file is
|
|
xor dx,dx ; by seeking to zero bytes from end
|
|
int DOS_INT
|
|
jc size_file20
|
|
mov cx,16
|
|
cmp dx,cx ; are we too big ?
|
|
jae size_file20 ; yes, force low
|
|
div cx ; AX = para's required
|
|
inc ax ; one for rounding error
|
|
jmps size_file30
|
|
|
|
himem_setup:
|
|
; On Entry:
|
|
; AX = minimum amount of upper memory required (in para's)
|
|
; On Exit:
|
|
; CY clear if able to satisfy request
|
|
; CY set on error (we then load low)
|
|
;
|
|
; try and find some hi memory
|
|
; we allocate the biggest available chunk of upper memory
|
|
;
|
|
push es
|
|
mov cx,ax ; CX = para's required
|
|
mov ax,mem_current_base
|
|
mov himem_current_base,ax ; save mem_current_base
|
|
mov ax,mem_current
|
|
mov himem_current,ax ; save mem_current
|
|
mov ax,mem_max
|
|
mov himem_max,ax ; save mem_max
|
|
mov ah,MS_M_ALLOC
|
|
mov bx,0FFFFh ; give me all memory (please)
|
|
int 21h ; bx = No. of paras available
|
|
cmp bx,cx ; do we have enough ?
|
|
jc himem_setup40 ; no, give up now
|
|
|
|
cmp himem_region,0 ; is there a region specified ?
|
|
je himem_setup20 ; no, allocate largest block
|
|
|
|
; Allocate the region specified by /L:
|
|
les bx,func52_ptr ; ES:BX -> list of lists
|
|
mov ax,es:F52_DMD_UPPER[bx] ; get upper memory link
|
|
cmp ax,0FFFFh ; make sure there is one
|
|
je himem_setup20 ; shouldn't happen....
|
|
mov es,ax
|
|
himem_setup10:
|
|
cmp es:DMD_ID,'M' ; is there another block ?
|
|
stc ; if we run out of blocks then
|
|
jne himem_setup40 ; we load low
|
|
mov es,ax ; ES -> DMD
|
|
mov bx,es:DMD_LEN ; get length in para'a
|
|
inc ax
|
|
add ax,bx ; AX -> next DMD
|
|
cmp es:DMD_PSP,0 ; is it free ?
|
|
jne himem_setup10 ; no, try the next
|
|
dec himem_region ; found the right region yet ?
|
|
jnz himem_setup10
|
|
cmp bx,cx ; do we have enough ?
|
|
jc himem_setup40 ; no, go low
|
|
mov ax,es ; ES -> DMD header to allocate
|
|
inc ax
|
|
mov es,ax ; ES -> data in block
|
|
mov ah, MS_M_SETBLOCK
|
|
int 21h ; "allocate" this block
|
|
mov ax,es
|
|
jnc himem_setup30 ; this can only fail if DMD chain
|
|
jmps himem_setup40 ; is corrupt...
|
|
|
|
himem_setup20:
|
|
; allocate the largest block available for DEVICEHIGH
|
|
mov ah, MS_M_ALLOC
|
|
mov bx, 0FFFFh ; give me all memory (please)
|
|
int 21h ; bx = No. of paras available
|
|
mov ah, MS_M_ALLOC ; give me bx paras please
|
|
int 21h ; ax:0 -> my memory
|
|
jc himem_setup40 ; woops, what happened ?
|
|
himem_setup30:
|
|
mov mem_current_base,ax
|
|
mov mem_current,ax ; save base of himem area
|
|
mov mem_max,ax
|
|
add mem_max,bx ; top of himem area
|
|
himem_setup40:
|
|
pop es
|
|
ret
|
|
|
|
himem_cleanup:
|
|
; clean up our high memory - this hook should free up any difference
|
|
; between himem_current and himem_max
|
|
mov ax,himem_max
|
|
mov mem_max,ax ; restore mem_max
|
|
mov ax,himem_current_base
|
|
xchg mem_current_base,ax ; restore mem_current_base
|
|
mov bx,himem_current
|
|
xchg mem_current,bx ; restore mem_current
|
|
push es
|
|
mov es,ax ; ES -> memory block
|
|
sub bx,ax ; has any memory been used ?
|
|
jz himem_cleanup10
|
|
mov ah,MS_M_SETBLOCK ; try and shrink the block
|
|
int DOS_INT ; to the size we used
|
|
pop es
|
|
; clc ; return success
|
|
ret
|
|
|
|
himem_cleanup10:
|
|
mov ah,MS_M_FREE ; free it all up
|
|
int DOS_INT
|
|
pop es
|
|
stc ; return an error
|
|
ret
|
|
|
|
copy_asciiz:
|
|
;-----------
|
|
lodsb ; get a character
|
|
stosb ; copy it
|
|
test al,al ; is it the terminating NUL ?
|
|
jnz copy_asciiz ; do next char
|
|
ret
|
|
|
|
wait_for_key:
|
|
;------------
|
|
; On Entry:
|
|
; None
|
|
; On Exit:
|
|
; CY set if no key pressed within timeout
|
|
;
|
|
mov cx,keyb_timeout ; get timeout value
|
|
clc ; assume no timeout
|
|
jcxz wait_for_key30
|
|
wait_for_key10:
|
|
push cx
|
|
mov ah,MS_T_GETTIME ; get current time
|
|
int DOS_INT ; so we can do timeout
|
|
mov bx,dx ; save secs in BH
|
|
pop cx
|
|
wait_for_key20:
|
|
mov ah,MS_C_STAT ; is a character ready ?
|
|
int DOS_INT ; if so process it
|
|
test al,al ; do we have a character ?
|
|
jnz wait_for_key30
|
|
push cx
|
|
mov ah,MS_T_GETTIME ; get current time
|
|
int DOS_INT ; so we can do timeout
|
|
pop cx
|
|
cmp bh,dh ; have we timed out ?
|
|
je wait_for_key20
|
|
loop wait_for_key10 ; another second gone by
|
|
stc ; we have timed out
|
|
wait_for_key30:
|
|
ret
|
|
|
|
;
|
|
; COPY_FILE copies the next parameter from DS:SI into the buffer
|
|
; at ES:DI and terminates with a NULL character. The parameter is
|
|
; expected to be a FileName. DS:SI are returned pointing to the
|
|
; next parameter in the command.
|
|
;
|
|
copy_file:
|
|
call whitespace ; DeBlank the Command Line
|
|
mov cx,MAX_FILELEN ; Limit FileName Length
|
|
push si ; Save SI in case of error
|
|
copy_f10:
|
|
lodsb ; Copy upto the first Space or
|
|
cmp al,' ' ; Control Character
|
|
jbe copy_f20
|
|
cmp al,',' ; stop at ',' too
|
|
je copy_f20
|
|
cmp al,'/' ; Also stop scanning when a switch
|
|
je copy_f20 ; character is detected
|
|
stosb
|
|
loop copy_f10
|
|
pop si ; Restore the original SI
|
|
mov ax,13 ; 13 = invalid data error
|
|
stc ; and return with an error
|
|
ret
|
|
|
|
copy_f20:
|
|
pop ax ; Remove Original String address
|
|
dec si ; Point at the failing character
|
|
xor ax,ax
|
|
stosb ; Zero Terminate FileName
|
|
ret
|
|
|
|
|
|
INITDATA DSEG 'INITDATA'
|
|
|
|
if ADDDRV
|
|
extrn err_no_command_file:byte
|
|
extrn err_block_device:byte
|
|
else
|
|
extrn shell:byte ; Default Command Processor
|
|
extrn shell_cline:byte ; Default Command Line
|
|
endif
|
|
extrn dev_epb:byte
|
|
extrn dev_count:byte
|
|
extrn rel_unit:word
|
|
|
|
extrn dos_target_seg:word
|
|
extrn bios_target_seg:word
|
|
extrn mem_current_base:word ; Current Base Address
|
|
extrn mem_current:word ; Current Load Address
|
|
extrn mem_max:word ; Top of Available Memory
|
|
extrn mem_size:word ; Real top of Memory
|
|
|
|
extrn init_dseg:word ; Current Init Data Segment
|
|
|
|
|
|
include initmsgs.def ; Include TFT Header File
|
|
|
|
|
|
; extrn bad_command:byte
|
|
; extrn bad_filename:byte
|
|
|
|
if not ADDDRV
|
|
; extrn bad_shell:byte
|
|
; extrn bad_country:byte
|
|
; extrn bad_lastdrive:byte
|
|
; extrn bad_break:byte
|
|
; extrn bad_buffers:byte
|
|
; extrn bad_files:byte
|
|
; extrn bad_fcbs:byte
|
|
; extrn bad_fopen:byte
|
|
; extrn bad_drivparm:byte
|
|
; extrn bad_history:byte
|
|
endif
|
|
|
|
; extrn yes_char:byte ; In BIOSMSGS.ASM
|
|
; extrn no_char:byte
|
|
|
|
extrn dev_load_seg:word
|
|
extrn dev_reloc_seg:word
|
|
extrn dev_epb:byte
|
|
extrn dev_name:byte
|
|
extrn dev_name:byte
|
|
extrn dosVersion:word
|
|
extrn strategy_off:word
|
|
extrn strategy_seg:word
|
|
extrn interrupt_off:word
|
|
extrn interrupt_seg:word
|
|
extrn request_hdr:byte
|
|
extrn next_drv:byte
|
|
extrn strategy_seg:word
|
|
extrn strategy:dword
|
|
extrn interrupt:dword
|
|
extrn func52_ptr:dword
|
|
extrn strategy_seg:word
|
|
extrn condev_off:word
|
|
extrn condev_seg:word
|
|
extrn clkdev_off:word
|
|
extrn clkdev_seg:word
|
|
extrn num_blkdev:byte
|
|
extrn blkdev_table:byte
|
|
extrn last_drv:byte
|
|
extrn next_drv:byte
|
|
extrn max_secsize:word
|
|
extrn max_clsize:word
|
|
extrn country_code:word
|
|
extrn code_page:word
|
|
extrn drdos_ptr:dword
|
|
extrn init_buf:byte
|
|
extrn num_read_ahead_buf:byte
|
|
extrn buffersIn:byte
|
|
extrn num_files:word
|
|
extrn num_fcbs:word
|
|
extrn num_fopen:word
|
|
extrn history_flg:byte ; In INIT code
|
|
extrn history_size:word ;
|
|
extrn num_stacks:word
|
|
extrn stack_size:word
|
|
|
|
if not ADDDRV
|
|
extrn hidos:byte
|
|
extrn bios_seg:word
|
|
extrn DeblockSetByUser:Byte
|
|
extrn DeblockSeg:word ; In BIOS data
|
|
endif
|
|
|
|
extrn dbcs_tbl:dword
|
|
extrn ctry_info:byte
|
|
|
|
|
|
|
|
Public cfg_file, cfg_file_end
|
|
|
|
preload_entry rd 0 ; preload back door entry
|
|
dw 14h ; offset is pre-initialised to 14h
|
|
preload_seg rw 1
|
|
|
|
preload_ver dw 10 ; version to give DBLSPACE
|
|
|
|
Public preload_drv
|
|
preload_drv db 0 ; number of preload drives
|
|
alt_drive db 0 ; preload checks alternative drive,
|
|
; (only used loading from A:)
|
|
|
|
; The preload_file is used as is to open a preload device
|
|
; It is also used to initialise a "DMD" name, and the code to do this
|
|
; currently finds the "\" and then copies the next 8 characters.
|
|
; This works with the current names - any new names may require modifications
|
|
|
|
preload_file dw offset security_file ; initially '\SECURITY.BIN'
|
|
security_file db '\SECURITY.BIN',0
|
|
stacker_file db 'C:\STACKER.BIN',0
|
|
dblspace_file db 'C:\DBLSPACE.BIN',0
|
|
|
|
cfg_file db 'DCONFIG.SYS',0 ; Configuration File
|
|
rb 64 ; space for bigger CHAIN'd file
|
|
cfg_file_end rb 0
|
|
|
|
cfg_seeklo dw 0 ; offset we have reached in CONFIG file
|
|
cfg_seekhi dw 0 ; in case Richards CONFIG file > 64k
|
|
|
|
cfg_head dw 0 ; offset we are at in CONFIG_BUF
|
|
cfg_tail dw 0 ; # bytes currently in CONFIG_BUF
|
|
|
|
cfg_buffer rb CFG_BUF_LEN ; individual lines live here
|
|
|
|
;
|
|
; EXEC parameter blocks for INSTALL function
|
|
;
|
|
exec_envseg rw 1 ; Environment Segment
|
|
exec_lineoff rw 1 ; Command Line Offset
|
|
exec_lineseg rw 1 ; Command Line Segment
|
|
exec_fcb1off rw 1 ; Offset of FCB 1 (5Ch)
|
|
exec_fcb1seg rw 1 ; Segment of FCB 1 (5Ch)
|
|
exec_fcb2off rw 1 ; Offset of FCB 2 (6Ch)
|
|
exec_fcb2seg rw 1 ; Segment of FCB 2 (6Ch)
|
|
rd 2 ; Initial SS:SP & CS:IP
|
|
|
|
system_sp rw 1
|
|
system_ss rw 1
|
|
|
|
ioctl_pb rb 0
|
|
ioctl_func rb 1 ; special functions
|
|
ioctl_type rb 1 ; device type (form factor)
|
|
ioctl_attrib rw 1 ; device attributes
|
|
ioctl_tracks rw 1 ; # of tracks
|
|
ioctl_mtype rb 1 ; media type, usually zero
|
|
ioctl_bpb rb 31 ; default BPB for this type of disk
|
|
ioctl_layout rw 1+64 ; support 64 sectors/track max.
|
|
|
|
drivp_drv rb 1 ; drive 0-15
|
|
drivp_chg rb 1 ; change line support
|
|
drivp_prm rb 1 ; permanent media flag
|
|
drivp_ff rb 1 ; form factor
|
|
drivp_trk dw 80
|
|
drivp_spt equ word ptr ioctl_bpb+13
|
|
drivp_heads equ word ptr ioctl_bpb+15 ; # of heads
|
|
|
|
ff_table dw bpb360, bpb1200, bpb720 ; 360/1200/720 Kb
|
|
dw bpb243, bpb1200 ; 8" sd/dd
|
|
dw bpb360, bpb360 ; hard disk, tape
|
|
dw bpb1440 ; 1440 Kb
|
|
|
|
bpb360 dw 512
|
|
db 2
|
|
dw 1
|
|
db 2
|
|
dw 112
|
|
dw 40*2*9
|
|
db 0FDh
|
|
dw 2
|
|
dw 9
|
|
dw 2
|
|
|
|
bpb1200 dw 512
|
|
db 1
|
|
dw 1
|
|
db 2
|
|
dw 224
|
|
dw 80*2*15
|
|
db 0F9h
|
|
dw 7
|
|
dw 15
|
|
dw 2
|
|
|
|
bpb720 dw 512 ; bytes per sector
|
|
db 2 ; sectors/cluster
|
|
dw 1 ; FAT address
|
|
db 2 ; # of FAT copies
|
|
dw 112 ; root directory size
|
|
dw 80*2*9 ; sectors/disk
|
|
db 0F9h ; media byte
|
|
dw 3 ; size of single FAT copy
|
|
dw 9 ; sectors per track
|
|
dw 2 ; # of heads
|
|
|
|
bpb1440 dw 512
|
|
db 1
|
|
dw 1
|
|
db 2
|
|
dw 224
|
|
dw 80*2*18
|
|
db 0F9h
|
|
dw 7
|
|
dw 18
|
|
dw 2
|
|
|
|
bpb243 dw 128
|
|
db 4
|
|
dw 1
|
|
db 2
|
|
dw 64
|
|
dw 77*1*26
|
|
db 0E5h
|
|
dw 1
|
|
dw 26
|
|
dw 1
|
|
|
|
msg_crlf db CR, LF
|
|
msg_dollar db '$'
|
|
|
|
CFG_NAME equ word ptr .0000h ; Command Name
|
|
CFG_FUNC equ word ptr .0002h ; Command Subroutine
|
|
CFG_FLAGS equ word ptr .0004h ; Command flags
|
|
CFG_SIZE equ 6 ; Size of each Entry
|
|
|
|
CF_NOF equ 0001h ; set if F5/F8 should be ignored
|
|
CF_LC equ 0002h ; set if case should be preserved
|
|
CF_QUERY equ 0004h ; set at run time eg. "DEVICE?"
|
|
|
|
cfg_table rw 0
|
|
if not ADDDRV
|
|
dw cmd_country, func_country, 0 ; COUNTRY=nnn,nnn,country
|
|
dw cmd_shell, func_shell, 0 ; SHELL=filename
|
|
dw cmd_lastdrive, func_lastdrive, 0 ; LASTDRIVE=d:
|
|
dw cmd_break, func_break, 0 ; BREAK=ON/OFF
|
|
dw cmd_buffers, func_buffers, 0 ; BUFFERS=nn
|
|
dw cmd_hibuffers, func_hibuffers, 0 ; HIBUFFERS=nn
|
|
dw cmd_fcbs, func_fcbs, 0 ; FCBS=nn
|
|
dw cmd_files, func_files, 0 ; FILES=nn
|
|
dw cmd_stacks, func_stacks, 0 ; STACKS=nn
|
|
dw cmd_fastopen, func_fastopen, 0 ; FASTOPEN=nnn
|
|
dw cmd_drivparm, func_drivparm, 0 ; DRIVPARM=/d:nn ...
|
|
dw cmd_history, func_history, 0 ; HISTORY=ON|OFF,NNN
|
|
dw cmd_hiinstall, func_hiinstall, 0 ; HIINSTALL=cmdstring
|
|
dw cmd_installhigh, func_hiinstall, 0 ; INSTALLHIGH=cmdstring
|
|
dw cmd_install, func_install, 0 ; INSTALL=cmdstring
|
|
dw cmd_hidos, func_hidos, 0 ; HIDOS=ON/OFF
|
|
dw cmd_dos, func_dos, 0 ; DOS=HIGH
|
|
dw cmd_set, func_set, CF_LC ; SET envar=string
|
|
dw cmd_switches, func_switches, CF_NOF ; SWITCHES=...
|
|
endif
|
|
dw cmd_hidevice, func_hidevice, 0 ; HIDEVICE=filename
|
|
dw cmd_devicehigh, func_hidevice, 0 ; DEVICEHIGH=filename
|
|
dw cmd_device, func_device, 0 ; DEVICE=filename
|
|
dw cmd_remark, func_remark, CF_NOF ; REM Comment
|
|
dw cmd_semicolon, func_remark, CF_NOF ; ; Comment
|
|
dw cmd_colon, func_remark, CF_NOF ; :label
|
|
dw cmd_chain, func_chain, 0 ; CHAIN=filename
|
|
dw cmd_goto, func_goto, 0 ; GOTO=label
|
|
dw cmd_gosub, func_gosub, 0 ; GOSUB=label
|
|
dw cmd_return, func_return, 0 ; RETURN (from GOSUB)
|
|
dw cmd_cls, func_cls, 0 ; Clear Screen
|
|
dw cmd_cpos, func_cpos, 0 ; Set Cursor Position
|
|
dw cmd_timeout, func_timeout, 0 ; set ? TIMEOUT
|
|
dw cmd_switch, func_switch, 0 ; SWITCH=n
|
|
dw cmd_onerror, func_onerror, CF_LC ; ONERROR='n' optional command
|
|
dw cmd_query, func_query, CF_NOF+CF_LC; ?optional command
|
|
dw cmd_echo, func_echo, CF_LC ; ECHO=string
|
|
dw cmd_exit, func_exit, 0 ; EXIT
|
|
dw cmd_error, func_error, 0 ; ERROR='n'
|
|
dw cmd_getkey, func_getkey, 0 ; GETKEY
|
|
dw cmd_yeschar, func_yeschar, 0 ; YESCHAR=
|
|
dw cmd_deblock, func_deblock, 0 ; DEBLOCK=xxxx
|
|
dw cmd_numlock, func_numlock, 0 ; NUMLOCK=ON/OFF
|
|
dw cmd_common, func_common, 0 ; [COMMON]
|
|
dw 0 ; end of table
|
|
|
|
if not ADDDRV
|
|
cmd_country db 'COUNTRY',0
|
|
cmd_shell db 'SHELL',0
|
|
cmd_lastdrive db 'LASTDRIVE',0
|
|
cmd_break db 'BREAK',0
|
|
cmd_buffers db 'BUFFERS',0
|
|
cmd_hibuffers db 'HIBUFFERS',0
|
|
cmd_fcbs db 'FCBS',0
|
|
cmd_files db 'FILES',0
|
|
cmd_stacks db 'STACKS',0
|
|
cmd_fastopen db 'FASTOPEN',0
|
|
cmd_drivparm db 'DRIVPARM', 0
|
|
cmd_history db 'HISTORY', 0
|
|
cmd_install db 'INSTALL', 0
|
|
cmd_hiinstall db 'HIINSTALL', 0
|
|
cmd_installhigh db 'INSTALLHIGH', 0
|
|
cmd_hidos db 'HIDOS',0
|
|
cmd_dos db 'DOS',0
|
|
cmd_set db 'SET',0
|
|
cmd_switches db 'SWITCHES',0
|
|
endif
|
|
cmd_hidevice db 'HIDEVICE',0
|
|
cmd_devicehigh db 'DEVICEHIGH',0
|
|
cmd_device db 'DEVICE',0
|
|
cmd_remark db 'REM', 0
|
|
cmd_semicolon db ';',0
|
|
cmd_colon db ':',0
|
|
cmd_chain db 'CHAIN',0
|
|
cmd_goto db 'GOTO',0
|
|
cmd_gosub db 'GOSUB',0
|
|
cmd_return db 'RETURN',0
|
|
cmd_cls db 'CLS',0
|
|
cmd_cpos db 'CPOS',0
|
|
cmd_timeout db 'TIMEOUT',0
|
|
cmd_switch db 'SWITCH',0
|
|
cmd_onerror db 'ONERROR',0
|
|
cmd_query db '?',0
|
|
cmd_echo db 'ECHO',0
|
|
cmd_exit db 'EXIT',0
|
|
cmd_error db 'ERROR',0
|
|
cmd_getkey db 'GETKEY',0
|
|
cmd_yeschar db 'YESCHAR',0
|
|
cmd_deblock db 'DEBLOCK',0
|
|
cmd_numlock db 'NUMLOCK',0
|
|
cmd_common db '[COMMON]',0
|
|
|
|
cmd_on db 'ON',0
|
|
cmd_off db 'OFF',0
|
|
|
|
confirm_msg1 db ' ($'
|
|
confirm_msg2 db ') ? $'
|
|
confirm_msg3 db CR,LF,'$'
|
|
|
|
region_opt db '/L:',0
|
|
slashs_opt db '/S',0
|
|
size_opt db 'SIZE',0
|
|
high_opt db 'HIGH',0
|
|
low_opt db 'LOW',0
|
|
umb_opt db 'UMB',0
|
|
noumb_opt db 'NOUMB',0
|
|
|
|
himem_region dw 0 ; region to hidevice into
|
|
himem_size dw 0 ; minimum size wanted
|
|
himem_current dw 0
|
|
himem_current_base dw 0
|
|
himem_max dw 0
|
|
|
|
if ADDDRV
|
|
error_flag db 0 ;1 if error occurred during command
|
|
;file processing, 0 otherwise
|
|
endif
|
|
|
|
default_query_char db CR
|
|
default_switch_char db '1'
|
|
|
|
keyb_timeout dw 0 ; default is no timeout
|
|
error_level dw 0 ; default is no error
|
|
save_sp dw 0 ; save SP here for GOSUB/RETURN's
|
|
|
|
|
|
INITENV DSEG PARA 'INITDATA'
|
|
|
|
envstart rb 253 ; initial env buffer
|
|
envend dw 0 ; make it double null terminated
|
|
db 1Ah ; EOF marker env buffer
|
|
Public boot_options
|
|
boot_options dw 0
|
|
; set by BIOS to either the SHIFT states, or to F5KEY or F8KEY
|
|
|
|
EXE_LENGTH equ 001Ch
|
|
|
|
exeBuffer rw 0
|
|
exeSignature dw 0 ; 0000 Valid EXE contains 'MZ'
|
|
exeFinal dw 0 ; 0002 Image Length MOD 512
|
|
exeSize dw 0 ; 0004 Image Length DIV 512
|
|
exeRelcnt dw 0 ; 0006 No. of Relocation Items
|
|
exeHeader dw 0 ; 0008 Header Size in paragraphs
|
|
exeMinpara dw 0 ; 000A Minimum No extra paragraphs
|
|
exeMaxpara dw 0 ; 000C Maximum No of extra paragraphs
|
|
exeSS dw 0 ; 000E Displacment of Stack Segment
|
|
exeSP dw 0 ; 0010 Initial SP
|
|
exeChecksum dw 0 ; 0012 Negative CheckSum
|
|
exeIP dw 0 ; 0014 Initial IP
|
|
exeCS dw 0 ; 0016 Code Segment displacement
|
|
exeReloff dw 0 ; 0018 Byte Offset of First REL item
|
|
exeOverlay dw 0 ; 001A Overlay Number (0 == Resident)
|
|
|
|
end
|
|
|