; 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= 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