; File : $CONFIG.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$ ; ; CONFIG.A86 1.35 93/12/01 18:30:29 ; nls_temp_area grows to 258 bytes ; CONFIG.A86 1.34 93/11/28 15:30:27 ; Support HIBUFFERS in UMB's ; CONFIG.A86 1.32 93/11/22 15:02:14 ; Bus Master checks do Int 21/0D before/after to discard any buffers ; CONFIG.A86 1.30 93/11/18 18:02:26 ; Add primitive multi-master checking ; CONFIG.A86 1.27 93/11/04 16:34:24 ; Extra callout to STACKER to determine if a drive is valid ; CONFIG.A86 1.26 93/11/03 17:00:19 ; Stop dblspace phantom drives from appearing (FATsize=0) ; CONFIG.A86 1.25 93/09/14 20:12:24 ; Initialise LFLG_PHYSICAL better - allow for zero FAT's meaning phantom drive ; CONFIG.A86 1.24 93/09/02 22:34:50 ; Add header to system allocations ; CONFIG.A86 1.23 93/08/06 20:55:23 ; re-arrange device init order for SCREATE.SYS on a VDISK.SYS ; CONFIG.A86 1.22 93/08/02 14:45:55 ; hide preload drives from func_device ; CONFIG.A86 1.20 93/07/28 19:19:08 ; call to SetupHMA before AllocHMA in setup_buffers allows buffers to go high ; on novell memory managers ; 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 CGROUP group INITCODE, INITDATA INITCODE CSEG PARA 'INITCODE' extrn AllocHMA:near extrn SetupHMA:near extrn alloc_instseg:near ; Allocate "Segment" Instance Memory extrn alloc_hiseg:near ; Allocate "Segment" High Memory extrn alloc_seg:near ; Allocate "Segment" Memory extrn config_process:near extrn InitStacks:near extrn HookInt2F:near extrn UnhookInt2F:near extrn Verify386:near Public config_init config_init: ; Initialize the CONFIG data ret Public country_init country_init: ;------------ push ds push es push di push si ; Obtain the address of the DBCS table in the BDOS. mov ax, 06507h ; Extended Country Info: get DBCS ptr mov bx, 0FFFFh ; codepage number: -1 for global cp mov cx, 00005h ; size of info. buffer mov dx, 0FFFFh ; country code: -1 for current country mov di, offset dbcs_buf push ds pop es ; es:di -> 5 byte buffer int DOS_INT ; returns with buffer filled in ; Get the current country information. mov dx, offset ctry_info ; ds:dx -> buffer to be filled in mov ax, 03800h ; get current country info int DOS_INT jnc ctry_info_done ; no carry = no error ; Failed to get country info. Place dummy uppercase routine in table. mov si, offset ctry_info mov CI_CASEOFF[si], offset myretf mov CI_CASESEG[si], cs mov bx, country_code ctry_info_done: mov country_code, bx pop si pop di pop es pop ds ret myretf: retf ; ; CONFIG is called after the BIOS INIT code has been relocated to high ; memory, the BIOS and BDOS have been initialised. ; Public config ; Process CONFIG.SYS, loading and config: ; initialising device drivers call country_init ; initialise DBCS tbl and country info mov ax,max_secsize ; get maximum sector size in BIOS les bx,func52_ptr cmp ax,es:F52_SECSIZE[bx] ; larger than default? jbe cfg_skip ; skip if not mov es:F52_SECSIZE[bx],ax ; else update sector size cfg_skip: call cpu_init ; initialise CPU type push ds ! pop es call config_process ; Process CONFIG.SYS cmp num_files,MIN_NUM_FILES ; Ensure the Minimum number of File jae cfg_ex10 ; have been allocated. mov num_files,MIN_NUM_FILES cfg_ex10: xor ax,ax mov al,init_buf ; now ensure we have allocated mov num_buf,ax ; the correct number of buffers call SetupDeblocking ; do our thing with deblocking call config_finish ; clean up configuration call setup_fopen ; allocate disk hashing call setup_history ret cpu_init: ; If we are on a 386 or above set CPU flag call Verify386 ; make sure it's a 386 jc cpu_init10 ; skip setting falg if not les bx,func52_ptr mov es:F52_CPU_TYPE[bx],1 ; we have a 386 ! cpu_init10: stc ; it's not a 386 ret SetupDeblocking: ;--------------- ; Some types of hard disk controller give us problems with disk access of ; mapped memory (eg. upper memory, LIM pages). ; We can force single sector deblocking in the disk driver to avoid these ; problems. On DRDOS 5/6 our default was single sector I/O above A000, but ; this gives performance problems when devices/tsr's are loaded into upper ; memory (eg. STACKER, SERVER). To avoid this we use the following strategy. ; ; Default for CONFIG is A000, and may be updated at any time by a DEBLOCK= ; statement. If this happens the user setting has priority. ; ; At the end of CONFIG, assume no user supplied setting, we do some simple ; tests for multi-master controllers. If these fail we leave the settings ; at A000. If they succeed we go ahead and change the default setting to ; deblock at FFFF. The test is to read the 1st sector into low memory, and ; again into upper memory. If we read the same thing then assume all is well. ; If we can't do this leave the deblocking set at A000. ; ; NB. We will still have problems from LIM, and from DMA crossing page ; boundaries on some memory managers (eg. DRDOS 5.0) ; les bx,func52_ptr lea bx,F52_DEVROOT[bx] ; ES:BX -> NUL device link cmp DeblockSetByUser,FALSE je SetupDeblocking20 ; the user is king SetupDeblocking10: push cs ! pop es ; ES -> local data again ret SetupDeblocking20: ; get next device driver les bx,es:[bx] ; we want resident disk device cmp bx,0FFFFh ; end of the chain ? je SetupDeblocking10 test es:word ptr 4[bx],8000h jnz SetupDeblocking20 ; we assume one disk device at 70:xxxx mov ax,es cmp ax,cs:bios_seg ; scan for resident disk in IO.SYS jne SetupDeblocking20 cmp es:word ptr 18[bx],0EDCh jne SetupDeblocking20 ; is it our disk driver ? lea ax,22[bx] ; deblocking variable is here mov deblockOffset,ax ; remember that for fixups mov deblockSeg,es mov ax,es:DH_STRATEGY[bx] ; Set up the STRATEGY Entry Point mov strategy_off,ax mov strategy_seg,es mov ax,es:DH_INTERRUPT[bx] ; Set up the INTERRUPT Entry Point mov interrupt_off,ax mov interrupt_seg,es mov al,es:DH_NAME[bx] ; get # supported units cbw mov numUnits,ax ; remember for later mov ax,(MS_M_STRATEGY*256)+1; set allocation strategy mov bl,42h ; to last fit, upper only int DOS_INT mov bx,512/16 ; we need a 512 byte buffer mov ah, MS_M_ALLOC ; try to allocate on in upper memory int DOS_INT jc SetupDeblocking50 mov UpperMemoryBuffer,ax ; use this for deblocking checks mov cx,numUnits ; CX = # of drives supported mov dx,'C'-'A' ; start with drive C: sub cx,dx ; CX = # of potential hard disks jbe SetupDeblocking40 ; skip tests if none SetupDeblocking30: ; DX = next drive, CX = drive count push cx ! push dx call BusMasterCheck ; check if drive DL bus master disk pop dx ! pop cx jc SetupDeblocking40 ; is so leave deblocking alone inc dx ; else move to next drive loop SetupDeblocking30 ; repeat for all drives les bx,deblockPointer mov es:word ptr [bx],0FFFFh ; safe to disable deblocking SetupDeblocking40: mov es,UpperMemoryBuffer mov ah,MS_M_FREE int DOS_INT ; free the upper memory buffer SetupDeblocking50: mov ax,(MS_M_STRATEGY*256)+1; set allocation strategy mov bl,0 ; to first fit int DOS_INT push cs ! pop es ; ES -> local data again ret BusMasterCheck: ; determine if we have an old troublesome controller ;-------------- ; On Entry: ; DL = drive to check (zero based) ; On Exit: ; CY set if troublesome drive ; call BusMasterRemovable ; is it a removable device ? jnc BMCheck10 ; yes, skip the checks mov ax,mem_current ; read into low memory call BusMasterRead ; read one sector from disk jc BMCheck10 ; give up if we couldn't read mov es,mem_current ; ensure at least the 1st word will mov ax,es:.0 ; differ if the read doesn't happen not ax mov es,UpperMemoryBuffer mov es:.0,ax mov ax,UpperMemoryBuffer ; read into upper memory call BusMasterRead ; read one sector from disk jc BMCheck10 ; give up if we couldn't read xor si,si xor di,di mov cx,512/2 mov es,mem_current mov ds,UpperMemoryBuffer repe cmpsw ; does the sector match ? push cs ! pop ds je BMCheck10 ; yes, everything is fine stc ; no, better leave DEBLOCK at A000 BMCheck10: ret BusMasterRemovable: ;------------------ ; On Entry: ; DL = drive ; On Exit: ; CY set if not a removable drive ; DL preserved ; push cs ! pop es mov bx,offset removableMediaRequest mov es:RH_UNIT[bx],dl callf cs:strategy callf cs:interrupt test es:RH_STATUS[bx],RHS_BUSY jz BusMasterRemovable10 stc ; busy bit set, it's a hard disk BusMasterRemovable10: ret BusMasterRead: ; Read first sector from drive BL ;------------- ; On Entry: ; AX = segment of buffer ; DL = 02 for C:, 03 for D:, etc. ; Exit: ; CY clear if deblocking not required ; DL preserved ; les bx,deblockPointer mov es:word ptr [bx],0FFFFh ; no deblocking during the test push cs ! pop es mov bx,offset readRequest mov es:RH_UNIT[bx],dl mov es:RH4_BUFOFF[bx],0 mov es:RH4_BUFSEG[bx],ax mov es:RH4_COUNT[bx],1 callf cs:strategy callf cs:interrupt test es:RH_STATUS[bx],RHS_ERROR jz BusMasterRead10 stc ; error bit set, say we had a problem BusMasterRead10: les bx,deblockPointer mov es:word ptr [bx],0A000h ; enable deblocking again ret Public setup_stacks setup_stacks: mov cx,num_stacks ; we want this many stacks jcxz setup_stacks10 ; skip if we don't want any mov dx,stack_size ; they should be this big call InitStacks ; initialise the stacks setup_stacks10: ret setup_history: test history_flg,RLF_ENHANCED jz setup_hist10 ; if history not enabled just exit push es les bx,drdos_ptr ; Get the internal data area mov si,es:DRDOS_HIST1CTL[bx]; Get the address of History Control mov ax,history_size ; Get Offset buffer for History data call setup_hist20 ; Allocate Buffer 1 mov si,es:DRDOS_HIST2CTL[bx]; Get the address of History Control 2 mov ax,history_size ; Get Offset buffer for History data call setup_hist20 ; Allocate Buffer 2 mov al,history_flg ; copy history state mov es:DRDOS_HISTFLG[bx],al ; into DRDOS data area pop es setup_hist10: ret setup_hist20: and ax,not 15 ; round to a complete paragraph add ax,16 ; always be a para bigger mov es:word ptr 02[si],ax ; Buffer Tail Address mov cl,4 push ax ; save buffers size in bytes shr ax,cl ; convert to para's mov dl,'H' ; History buffer call alloc_instseg ; Allocate Buffer mov es:word ptr 00[si],ax ; Buffer Start Address pop cx ; recover buffer size in bytes push es mov es,ax ; point ES at buffer seg xor di,di ; ES:DI -> buffer xor ax,ax ; zero it rep stosb ; before use pop es ret Public device_init, resident_device_init ; DEVICE_INIT will initialise the device with the Device Header at ; ES:BX ; ; Entry: ; ES:DI Address of First Device Header ; DS:SI Command Line Parameters ; ; Exit: ; AX Top bit set on error, error code in AL ; device_init: ;----------- push es ! push di call build_cmd_tail ; point DS:SI to dos style cmd line pop di ! pop es call HookInt2F ; hook the Int 2F vector call resident_device_init call UnhookInt2F ; get off the Int 2F vector test ax,ax ; set the flags ret resident_device_init: ;-------------------- mov rel_unit,0 ; set rel unit to zero for block devices mov dev_count,0 mov bx,offset request_hdr ; ds:bx -> command block if TRUE mov ax,mem_max ; AX:0 -> top of available memory mov ds:RH0_RESIDENT,0 ; pass to the device driver mov ds:RH0_RESIDENT+2,ax ; in the RESIDENT field else mov ds:RH0_RESIDENT,di ; Force the default RESIDENT field mov ds:RH0_RESIDENT+2,es ; to be the error condition endif dev_i10: push si push es ! push di call save_vecs ; save interrupt vectors mov bx,offset request_hdr ; ds:bx -> command block call dev_init ; initialise the device driver test es:DH_ATTRIB[di],DA_CHARDEV jnz dev_i18 ; skip if a character device cmp ds:RH0_NUNITS,0 ; no drives installed for disk device? je dev_i_err ; failed if no drives found dev_i18: mov ax,ds:RH0_RESIDENT ; Calculate the address of mov cl,4 ; last paragraph used by the shr ax,cl ; device driver. If this is the add ax,ds:RH0_RESIDENT+2 ; device driver CS then error test ds:RH0_RESIDENT,15 ; allow for partial para ? jz dev_i19 inc ax ; round it up dev_i19: cmp ax,strategy_seg jbe dev_i_err cmp ax,mem_max ; Check for Memory Overflow jb dev_i30 ; if it does then we can't install call restore_vecs ; so replace interrupt vectors dev_i_err: ; device initialization failed! les di,cs:func52_ptr ; ES:DI -> internal data mov ax,es or ax,di ; DOS data area present yet? pop di ! pop es ; recover the device header les di,es:DH_NEXT[di] ; try next device driver jz dev_i60 ; if it's resident initialisation mov di,0FFFFh ; else stop now jmps dev_i60 ; The device driver initialised OK so now build/update internal ; tables based on the device driver type. ; ; AX = next available paragraph ; DS:BX = request header ; ES:DI = device driver header ; dev_i30: ; DEV_INIT OK so update the Top of mov mem_current,ax ; memory field test es:DH_ATTRIB[di],DA_CHARDEV jz dev_i40 call char_device ; Handle Initialization of all jmps dev_i50 ; character devices dev_i40: call block_device ; Handle Initialization of all ; jmps dev_i50 ; Block Devices dev_i50: pop di ! pop es ; Retrieve the current device header push es:DH_NEXTSEG[di] ; save next entry on the list push es:DH_NEXTOFF[di] ; while we deal with existing one mov es:DH_NEXTOFF[di],0FFFFh; terminate the list call device_insert ; and insert into the list pop di pop es ; go round till the end dev_i60: pop si ; recover cmdline for next device cmp di,0FFFFh ; was that the last device to jne dev_i10 ; initialise, no do next mov bx,offset request_hdr ; ds:bx -> command block mov ax,ds:RH_STATUS ; return Status Register and ax,80FFh ; is there an error ? js dev_i70 xor ax,ax ; no, return success dev_i70: ret public init_static_request init_static_request: ; Set up request header for INIT command. sub ax,ax ; get a convenient zero mov ds:RH_LEN,RH0_LEN ; Init Request Length mov ds:RH_UNIT,al ; relative drive always 0 mov ds:RH_CMD,CMD_INIT ; Init Command mov ds:RH_STATUS,ax ; Zero Status Register mov ds:RH0_BPBOFF,si ; Save the command line offset and mov ds:RH0_BPBSEG,ds ; Segment in the BPB Pointer mov al,next_drv ; the first drive for this device sub al,preload_drv ; (not including preloaded devices) mov ds:RH0_DRIVE,al ; will be allocated as NEXT_DRV ;;; mov es:DH_NEXTSEG[di],0 ; force seg to zero (386max 4.05) ret dev_init: ;-------- ; On Entry: ; ES:DI -> device driver header ; DS:BX -> req header ; DS:SI -> command line ; On Exit: ; ES:DI/DS:BX ; mov ax,es:DH_STRATEGY[di] ; Set up the STRATEGY Entry Point mov strategy_off,ax mov strategy_seg,es mov ax,es:DH_INTERRUPT[di] ; Set up the INTERRUPT Entry Point mov interrupt_off,ax mov interrupt_seg,es call init_static_request push ds ! push es ; Save Segment registers push bx ! push si ! push di ; and pointers (not all preserve them) push ds ! pop es ; ES -> Points at the Data Segment mov ds,strategy_seg ; DS == Device Drive Segment mov si,di ; DS:SI -> device driver header callf cs:strategy ; Call Device Strategy Routine callf cs:interrupt ; Call Device Interrupt Routine pop di ! pop si ! pop bx ; recover the pointers pop es ! pop ds ; Restore Segment Registers mov es:DH_NEXTSEG[di],es ; ignore segment - it MUST be same one ret ; ; Character Device Driver Initialised OK so now build/update internal ; tables based on the device driver type. ; ; DS:BX Request Header ; ES:DI Device Driver Header ; char_device: test es:DH_ATTRIB[di],DA_ISCIN jz char_d10 ; is this the standard console device? mov condev_off,di ; save console device driver address mov condev_seg,es ret char_d10: test es:DH_ATTRIB[di],DA_ISCLK jz char_d20 ; is this the standard clock device? mov clkdev_off,di ; save clock device driver address mov clkdev_seg,es char_d20: ret ; Block device driver initialised OK. Save the values ; returned from the INIT call so we can later build all the ; required internal tables. ; ; entry: DS:BX -> request header ; ES:DI -> device driver header ; public block_device block_device: mov al,BLKDEV_LENGTH ; bytes per block device table entry mul byte ptr num_blkdev ; * # of block devices installed add ax,offset blkdev_table ; AX -> block dev init result table xchg ax,si ; pointer to next block device struct mov devoff,di mov devseg,es ; point to device driver header mov 0[si],di mov 2[si],es ; save device driver address for later mov ax,ds:RH0_BPBOFF mov 4[si],ax ; save BPB table address (offset) mov ax,ds:RH0_BPBSEG mov 6[si],ax ; save BPB table address (segment) mov cl,ds:RH0_NUNITS mov 8[si],cl ; get # of units supported by driver mov es:DH_NAME[di],cl ; set # of units in device name inc num_blkdev ; we've installed another block device add next_drv,cl ; update drive base for next driver add dev_count,cl ; number of new units mov ax,boot_device ; now for Andy's bit about boot device or ax,boot_device+2 ; have we already got a boot device? jnz not_boot_dev mov ch,init_drv sub ch,next_drv ; is sub unit in this driver ja not_boot_dev ; no, skip it add ch,cl ; work out which sub unit it is mov boot_drv,ch ; and remember it mov boot_device,di mov boot_device+2,es not_boot_dev: push si ! push es ! push di mov cl,8[si] mov ch,0 ; CX = # of drives found in driver les si,4[si] ; ES:SI -> BPB array in BIOS mov bpbseg,es ; remember the segment blkdev_loop: lods es:ax ; AX = offset of next BPB push es ! push si ! push cx mov bpboff,ax ; remember the offset xchg ax,di ; ES:DI -> next BPB mov ax,es:[di] ; AX = sector size for BPB cmp ax,max_secsize ; new maximum for sector size jbe blkdev_next1 ; skip if sector size not grown mov max_secsize,ax ; else set new maximum blkdev_next1: mov dl,es:2[di] ; get sectors per cluster mov dh,0 ; make this a word mul dx ; AX = bytes per cluster cmp ax,max_clsize ; more than previous maximum jbe blkdev_next2 ; skip if no new high score mov max_clsize,ax ; else record max. sector size blkdev_next2: les bx,func52_ptr ; ES:BX -> internal data mov ax,es or ax,bx ; DOS data area present yet? jz blkdev_next3 ; skip if BDOS not present yet call setup_drives ; update drives in BDOS data mov es,mem_current ; MUST create a DDSC just after driver add mem_current,(DDSC_LEN+15)/16 xor bp,bp ; ES:BP points to the DDSC call setup_ddsc ; add new DDSC_ to chain call setup_ldt ; initialise LDT for that drive blkdev_next3: pop cx ! pop si ! pop es loop blkdev_loop ; repeat for all BPBs in driver pop di ! pop es ! pop si ret resident_ddscs: ;-------------- ; Allocate DDSC's for the resident device drivers - we can only do this ; after the DOS data area is established. ; sub bx,bx ; start with 1st block device mov cx,num_blkdev ; get # of block devices jcxz res_ddsc40 ; skip if no block devices res_ddsc10: push bx ! push cx mov ax,BLKDEV_LENGTH mul bx add ax,offset blkdev_table xchg ax,si ; SI -> block device table sub cx,cx mov cl,8[si] ; CX = # of units on device sub bx,bx ; BX = relative unit # * 2 mov rel_unit,bx ; start with relative unit # 0 res_ddsc20: ; CX = remaining units push bx ! push cx ! push si ; BX = offset, SI -> drive structure lodsw mov devoff,ax ; save device header offset lodsw mov devseg,ax ; save device header segment les si,[si] ; get offset of BPB array mov ax,es:[bx+si] ; get offset for our BPB mov bpboff,ax mov bpbseg,es ; save pointer to BPB les bp,res_ddsc_ptr ; point to position for DDSC_ add ds:word ptr res_ddsc_ptr,DDSC_LEN call setup_ddsc ; setup one unit pop si ! pop cx ! pop bx inc bx ! inc bx ; increment (unit index*2) loop res_ddsc20 ; repeat for next unit, same driver pop cx ! pop bx inc bx loop res_ddsc10 ; repeat for next driver res_ddsc40: ; all block devices done ret setup_ddsc: ;---------- ; On Entry: ; ES:BP -> DDSC_ to initialise and link into chain ; bpbptr -> BPB to initialise from ; devseg:devoff -> device driver header ; abs_unit, rel_unit reflect drive ; On Exit: ; None ; push ds lds si,bpbptr ; DS:SI points to the BPB mov ah,53h ; build DDSC from BPB call int DOS_INT ; initialises the structure pop ds mov ax,devoff mov es:DDSC_DEVOFF[bp],ax mov ax,devseg mov es:DDSC_DEVSEG[bp],ax mov ax,abs_unit inc abs_unit mov es:DDSC_UNIT[bp],al ; set absolute unit (global) mov ax,rel_unit inc rel_unit mov es:DDSC_RUNIT[bp],al ; set relative unit (driver relative) mov ax,-1 ; set link to FFFFh:FFFFh mov es:word ptr DDSC_LINK[bp],ax mov es:word ptr DDSC_LINK+2[bp],ax mov es:DDSC_FIRST[bp],al ; set drive never accessed flag mov ax,es ; now link into device chain ; les bx,func52_ptr ; ES:BX -> secret 52h data lea bx,F52_DDSCPTR-(offset .DDSC_LINK)[bx] setup_ddsc10: cmp es:word ptr DDSC_LINK[bx],0FFFFh je setup_ddsc20 ; is there another one ? les bx,es:DDSC_LINK[bx] ; onto next DDSC_ jmps setup_ddsc10 setup_ddsc20: ; link new DDSC to end of chain mov es:word ptr DDSC_LINK[bx],bp mov es:word ptr DDSC_LINK+2[bx],ax ret ; now RAF will be happy Public setup_ldt setup_ldt: push ds push es les bx,func52_ptr ; get internal data in ES:BX mov al,LDT_LEN ; we need this many bytes per drive mul es:F52_LASTDRV[bx] ; *lastdrive xchg ax,cx ; CX = size to initialise mov al,es:F52_LASTDRV[bx] ; lastdrive push ax ; save for later les di,es:F52_PATHPTR[bx] ; now initialise the CSD's xor al,al ; to zero rep stosb ; zero them pop ax ; recover lastdrive xor bx,bx ; start with zero offset xor cx,cx ; start with drive A xchg al,cl ; AH = physical limit, CX logical limit ldt_init: push ax push cx push ax lea di,LDT_NAME[bx] add al,'A' ; make drive ASCII stosb mov ax,'\:' ; point at the root stosw mov ax,0FFFFh lea di,LDT_BLK[bx] ; set to FFFF to force LDT_ rebuild stosw ! stosw ! stosw ; next two words are FFFF too ; lea di,LDT_ROOTLEN[bx] mov ax,2 stosw ; set the length field pop ax lds si,cs:func52_ptr ; get internal data in DS:SI sub si,offset .DDSC_LINK ldt_init20: lds si,ds:DDSC_LINK[si] ; point to next PDT cmp si,-1 ; skip if there isn't one je ldt_init40 cmp al,ds:DDSC_UNIT[si] ; is this the DDSC for the drive jne ldt_init20 ; if not try another mov es:word ptr LDT_PDT[bx],si mov es:word ptr LDT_PDT+2[bx],ds cmp ds:DDSC_NFATS[si],0 ; no FATS, then it's a reserved drive je ldt_init40 push es push bx push ax ; save drive we are processing mov ax,4A11h xor bx,bx int 2Fh ; do an STACKER installation check pop dx ; DL = drive we are processing test ax,ax mov ax,LFLG_PHYSICAL ; assume a physical drive jnz ldt_init30 ; no STACKER, it's physical sub cl,'A' ; zero base STACKER drive returned cmp cl,dl ; should we check this drive ? ja ldt_init30 ; below 1st drive, it's physical push ax push dx mov ax,4A11h mov bx,1 ; ask STACKER for host drive int 2Fh pop dx pop ax cmp bl,dl ; is this the host drive ? jne ldt_init30 xor ax,ax ; drive is invalid ldt_init30: pop bx pop es mov es:LDT_FLAGS[bx],ax ldt_init40: add bx,LDT_LEN ; move onto next LDT_ pop cx pop ax inc ax ; and next drive loop ldt_init ; done to lastdrive ? no, do another pop es pop ds ret Public device_insert device_insert: ;------------- ; insert device drivers at ES:DI into global chain ; if we are initialising the resident device drivers then we don't have ; a global chain, so insert them on a local chain and try again later push ds lds bx,func52_ptr ; Internal Data Pointer lea si,F52_DEVROOT[bx] ; DS:SI -> NUL device mov ax,ds ; if BDOS data area isn't present or ax,bx ; we are initialising resident jnz dev_ins_next ; devices push cs ! pop ds mov si,offset resdev_chain ; it's resident devices dev_ins_next: cmp di,-1 ; end of device chain reached? je devins_done ; yes, all devices inserted mov ax,0[si] mov dx,2[si] ; DX:AX = original chain mov 0[si],di mov 2[si],es ; link our device at head of chain xchg ax,es:0[di] ; link old global chain to device xchg dx,es:2[di] ; & get next device in local chain mov di,ax ; point to next device in chain mov es,dx jmps dev_ins_next ; repeat until chain empty devins_done: pop ds ret public config_finish config_finish: ; finish off configuration ;------------- cmp resdev_off,-1 ; are resident devices already je cfg_fin10 ; installed les di,resdev_chain ; insert all the resident device call device_insert ; drivers into DOS chain call resident_ddscs ; build DDSC's for resident devices mov resdev_off,-1 ; only do this once... cfg_fin10: les bx,func52_ptr ; ES:BX -> base of DOS variables call setup_drives ; Update No of Physical Drives in case ; this is the first pass call setup_ldt ; setup the ldt's les bx,func52_ptr lea di,F52_CLKDEV[bx] ; ES:DI -> clock ptr, console ptr mov si,offset clkdev_off ; DS:SI -> local pointer values movsw ; set offset of clock device driver movsw ; set segment of clock device driver movsw ; set offset of console device driver movsw ; set segment of console device driver call setup_doshndl ; Allocate DOS compatible Handles ; NB must immediately follow devices ! call setup_buffers ; allocate the requested # ret setup_buffers: ;------------- ; entry: num_buf = minimum # of buffers required ; 0 - use temporary high buffers ; les bx,func52_ptr ; ES:BX -> internal data structure mov ax,num_buf ; fill in info in DOS for diagnostic mov es:F52_NUM_BUF[bx],ax ; programs mov al,num_read_ahead_buf mov es:F52_READ_AHEAD[bx],ax mov ax,es:F52_SECSIZE[bx] ; get DOS data sector size cmp ax,max_secsize ; has it been poked to a bigger value ? ja setup_b10 ; if so we must discard anyway mov ax,max_secsize ; get max. sector size found setup_b10: mov es:F52_SECSIZE[bx],ax ; update max. sector size in PCMODE mov max_secsize,ax ; update max. sector size locally add ax,offset .BCB_DATA ; add in the header size mov es,init_dseg ; ES:DI -> init buffers mov di,offset INIT_BUFFERS mov cx,NUM_BUFFS ; CX buffs, DX in size, at ES:DI mov dx,SIZEOF_BUFFS ; size of init buffers cmp num_buf,0 ; (zero at init time) je setup_b70 ; go ahead and initialise push ax ; save size of buffer mul num_buf ; AX = total bytes required test dx,dx ; > 64 K ? jz setup_b30 mov ax,0FFFFh ; do the maximum setup_b30: pop bx ; BX = size of a buffer mov cx,ax ; CX bytes required xor dx,dx div bx ; AX = # buffers push ax ; save # buffers push bx ; save size of a buffer push cx ; save bytes wanted test buffersIn,BUFFERS_IN_HMA stc ; do we want buffers at FFFF ? jz setup_b40 call SetupHMA ; make sure HMA chain is established pop cx ! push cx ; CX = bytes wanted mov dx,0FFFFh ; anywhere is OK call AllocHMA ; ES:DI -> allocated data setup_b40: pop ax ; AX = bytes wanted pop dx ; DX = size of a buffer pop cx ; CX = number of buffer jnc setup_b70 ; if CY clear ES:DI -> our space shr ax,1 ! shr ax,1 shr ax,1 ! shr ax,1 inc ax ; convert from bytes to para's push dx mov dl,'B' ; allocate as a Buffer test buffersIn,BUFFERS_IN_UMB jz setup_b50 ; allocation from UMB's OK ? call alloc_hiseg ; yes, try and allocate memory there jnc setup_b60 setup_b50: call alloc_seg ; allocate memory in bottom 640 K setup_b60: pop dx mov es,ax ; ES = segment xor di,di ; ES:DI -> start of buffer setup_b70: ; Buffer space for CX buffers, of size DX, allocated at ES:DI mov si,di ; remember where 1st buffer is setup_b80: push cx mov bx,di ; BX = current buffer mov cx,dx xor ax,ax rep stosb ; zero the buffer, ES:DI -> next buffer mov es:BCB_DRV[bx],0FFh ; invalidate buffer mov es:BCB_NEXT[bx],di ; point to where "next" will be mov ax,bx sub ax,dx ; work out what our previous was mov es:BCB_PREV[bx],ax ; and point to it pop cx loop setup_b80 ; do them all mov es:BCB_NEXT[bx],si ; the last's "next" is our first buffer mov es:BCB_PREV[si],bx ; the first's "previous" is our last mov ax,es ; AX:SI -> 1st buffer les bx,func52_ptr ; ES:BX -> internal data structure mov es:F52_BCBOFF[bx],si mov es:F52_BCBSEG[bx],ax ; fixup buffer pointers mov es:word ptr F52_BUF_INFO[bx],si mov es:word ptr F52_BUF_INFO+2[bx],ax inc ax ; seg FFFF ? jnz setup_b90 ; skip if not mov es:F52_HMAFLAG[bx],1 ; buffers are in HMA mov ax,es:F52_SECSIZE[bx] add ax,15 mov cl,4 shr ax,cl ; convert to para size mov dl,'B' ; allocate as a Buffer call alloc_seg ; allocate a deblocking buffer mov es:F52_DEBLOCK[bx],ax les bx,drdos_ptr ; ES:BX -> data area mov es:DRDOS_DEBLOCK[bx],ax ; of deblocking buffer setup_b90: ret setup_doshndl: push es les bx,func52_ptr ; Internal Data Pointer lea bx,F52_FILEPTR[bx] ; Start of Handle List mov cx,num_files ; Number of DOS Handles add cx,num_fcbs ; + some for FCB's cmp cx,255 jbe setup_dh10 mov cx,255 ; maximum IFN is 255 setup_dh10: cmp es:DCNTRL_DSOFF[bx],0FFFFh ; Last entry ? je setup_dh20 ; no, loop round again les bx,es:DCNTRL_DSADD[bx] ; Get the Next Entry sub cx,es:DCNTRL_COUNT[bx] ; Update the count jc setup_dh30 ; going negative isn't allowed jmps setup_dh10 setup_dh20: jcxz setup_dh30 ; any left to allocate ? mov ax,DHNDL_LEN ; How many bytes do we need mul cx ; for the structure mov dx,ax ; including the control add ax,DCNTRL_LEN+15 ; Ensure the new structure is shr ax,1 ! shr ax,1 ; a paragraph value shr ax,1 ! shr ax,1 ; allocate some memory mov dl,'F' ; allocate for Files call alloc_seg mov es:DCNTRL_DSOFF[bx],0 ; link the new seg mov es:DCNTRL_DSSEG[bx],ax ; to the end of the list ; We can now initialise the new structure les bx,es:DCNTRL_DSADD[bx] ; Get the New Entry mov es:DCNTRL_DSOFF[bx],0FFFFh ; terminate the list mov es:DCNTRL_DSSEG[bx],0FFFFh ; with -1,-1 mov es:DCNTRL_COUNT[bx],cx ; Number of elements ; Now zero the tables mov ax,DHNDL_LEN ; How many bytes do we have mul cx ; with this number of elements mov cx,ax ; in the structure lea di,DCNTRL_LEN[bx] ; Zero the contents of the sub al,al ; structure rep stosb setup_dh30: pop es ret setup_drives: mov al,next_drv ; AL = # of drives supported push es ! push bx les bx,func52_ptr ; ES:BX -> base of DOS variables mov es:F52_PHYDRV[bx],al ; set # of Physical drives installed mov es:F52_LASTDRV[bx],al ; set # of Logical drives installed pop bx ! pop es ret setup_fopen: ; allocate file hashing information ;----------- les bx,drdos_ptr mov ax,num_fopen ; get # of hashed directory entries cmp ax,-1 ; has it been set yet ? jne setup_fopen10 ; yes, then leave it alone mov ax,DEF_NUM_FOPEN cmp es:DRDOS_HIMEM_ROOT[bx],0; do we have a high memory chain ? jne setup_fopen10 ; high memory means no TPA hit xor ax,ax ; keep things small otherwise setup_fopen10: xor dx,dx ; AX/DX = 32 bit # of entries mov si,max_clsize ; max. cluster size mov cl,5 ; 32 byte per directory entry shr si,cl ; SI = directory entries per cluster add ax,si ; round up count to multiple of cluster dec ax div si ; AX = # of hashed blocks mov cx,ax jcxz setup_fopen90 ; skip if hashing disabled mov es:DRDOS_HASHMAX[bx],si ; maximum # dir entries allowed shl si,1 ; SI = bytes required for data lea si,HCB_DATA[si] ; + control information mul si ; AX bytes of data required test dx,dx jnz setup_fopen90 ; overflow (shouldn't happen) ; Allocate CX HCB_'s of size SI bytes, AX bytes in total mov bx,es:DRDOS_HIMEM_ROOT[bx]; do we have a high memory chain ? test bx,bx ; zero indicates we don't jz setup_fopen30 mov dx,0FFFFh ; use the magic FFFF segment mov es,dx cmp ax,es:2[bx] ; is there enough room ? ja setup_fopen30 ; no, forget try conventinal memory sub es:2[bx],ax ; else allocate the memory mov di,es:2[bx] ; get base+length add di,bx ; = our allocation mov ax,es:2[bx] ; if the section left is under cmp ax,2*WORD ; 2 words discard it jae setup_fopen20 ; as we may overwrite size/link mov ax,es:[bx] ; get next entry les bx,drdos_ptr ; and make it the new himem root mov es:DRDOS_HIMEM_ROOT[bx],ax setup_fopen20: xchg ax,dx ; AX = FFFF jmps setup_fopen40 ; AX:DI -> data block allocated setup_fopen30: shr ax,1 ; convert size to para's shr ax,1 shr ax,1 shr ax,1 inc ax ; allow for rounding mov dl,'E' call alloc_hiseg ; allocate it para aligned dec ax ; zero offset terminates the chain mov di,10h ; so start with a non-zero offset ; jmps setup_fopen40 ; AX:DI -> data block allocated setup_fopen40: ; setup CX HCB_'s of size SI at AX:DI les bx,drdos_ptr mov es:DRDOS_HASHOFF[bx],di mov es:DRDOS_HASHSEG[bx],ax mov es,ax setup_fopen50: mov es:HCB_DRV[di],-1 ; discard the HCB initially mov bx,di ; remember where it is add di,si ; onto next HCB_ mov es:HCB_LINK[bx],di ; link it to previous HCB_ loop setup_fopen50 ; allocate all hash control blocks mov es:HCB_LINK[bx],0 ; zero terminate the list setup_fopen90: ; all HCBs done, return push cs pop es ; back to 8080 model ret Public whitespace whitespace: lodsb ; Skip any White Space in the cmp al,' ' ! jz whitespace ; CR/LF terminated string cmp al,TAB ! jz whitespace dec si ret Public build_cmd_tail build_cmd_tail: push ds ! pop es mov cx,length cfg_buffer - 3 ; (leave room for 3 extra chars) mov di,offset cfg_buffer build_cl1: lodsb ; Copy the device name cmp al,' ' ; until a Control char (end of line) jbe build_cl2 ; or a Space (end of name) cmp al,'/' ; Also stop scanning when a switch je build_cl2 ; character is detected stosb loop build_cl1 mov al,CR ; indicate we can go no more.... build_cl2: cmp al,CR ; it it really the end ? mov al,' ' ; now insert a space character stosb je build_cl_exit ; CR meant it's time to go dec si ; rewind the source one character build_cl3: lodsb ; Copy the tail cmp al,CR ; until we find a CR je build_cl4 ; at the end of the line stosb loop build_cl3 mov al,CR ; no more room, so terminate build_cl4: stosb build_cl_exit: mov al,LF ; now insert the terminating linefeed stosb ; at the end of the buffer mov si,offset cfg_buffer ret save_vecs: ; save interrupt vectors so we can restore if device init fails push ds ! push es push si ! push di ! push cx mov cx,(length vec_save_buf)*2 ; CX = words to save xor si,si mov ds,si ; DS:SI -> vectors to save push cs ! pop es mov di,offset vec_save_buf ; ES:DI -> save area rep movsw ; save them pop cx ! pop di ! pop si pop es ! pop ds ret restore_vecs: ; replace interrupt vectors after a dd_init fails push ds ! push es push si ! push di ! push cx mov cx,length vec_save_buf ; CX = vectors to restore push cs ! pop ds mov si,offset vec_save_buf ; DS:SI -> save area xor di,di mov es,di ; ES:DI -> vectors to restore rest_vec1: mov ax,es:2[di] ; get updated vector cmp ax,mem_current ; below attempted driver? jb rest_vec2 ; yes, don't zap it cmp ax,mem_max ; above attempted driver? jae rest_vec2 ; yes, don't zap it movsw ! movsw ; else restore vector jmps rest_vec3 ; overwritten by driver rest_vec2: add si,dword ; skip vector in source add di,dword ; and in destination rest_vec3: loop rest_vec1 ; next vector pop cx ! pop di ! pop si pop es ! pop ds ret INITDATA DSEG 'INITDATA' extrn shell:byte ; Default Command Processor extrn shell_cline:byte ; Default Command Line extrn num_files:word ; default # of file handles extrn num_fcbs:word ; default # of fcb file handles extrn num_fopen:word ; default value for fast open extrn country_code:word ; Requested Country Code (Default US) extrn num_stacks:word ; # hardware stacks wanted extrn stack_size:word ; size of a hardware stack extrn mem_current:word ; Current Load Address extrn mem_max:word ; Top of Available Memory extrn init_dseg:word ; Current init Data Segment extrn dos_dseg:word extrn bios_seg:word extrn func52_ptr:dword extrn drdos_ptr:dword extrn res_ddsc_ptr:dword include initmsgs.def ; Include TFT Header File extrn preload_drv:byte extrn init_drv:byte ; the initial boot drive Public dev_load_seg, dev_reloc_seg, dev_epb, dev_name, dev_count Public rel_unit, dev_epb Public strategy_off, strategy_seg, interrupt_off, interrupt_seg, request_hdr Public next_drv, strategy_seg, strategy, interrupt Public strategy_seg, condev_off, condev_seg, clkdev_off, clkdev_seg Public num_blkdev, blkdev_table, next_drv, max_secsize Public max_clsize, init_buf, num_read_ahead_buf Public buffersIn, history_flg, history_size Public dbcs_tbl, ctry_info, boot_device, boot_drv, resdev_chain history_flg db 0 ; Disable history buffers to save RAM history_size dw 256 ; When enabled 2*history size are used for bufs cfg_buffer rb CFG_BUF_LEN ; extra termination for buggy Windows db CR,LF,0 ; device driver - give it CR/LF to hit init_buf db MIN_NUM_BUFFS ; default # of buffers num_read_ahead_buf db DEF_READ_AHEAD ; default # of read-ahead buffersIn db 0 ; default is low dev_count db 0 ; count of new drives (used by preload) next_drv db 0 ; Next Drive to Allocate num_blkdev dw 0 ; # of block devices installed boot_device dw 0,0 ; ptr to boot device boot_drv db 0 ; and the sub unit number max_secsize dw 0 ; max. sector size encountered max_clsize dw 0 ; max. cluster size encountered ; Do not change the order of the next four words: clkdev_off rw 1 ; clock device driver clkdev_seg rw 1 condev_off rw 1 ; console device driver condev_seg rw 1 bpbptr rd 0 ; temporary BPB pointer bpboff rw 1 bpbseg rw 1 devptr rd 0 ; temporary device header pointer devoff rw 1 devseg rw 1 resdev_chain rd 0 ; head of chain for resident device resdev_off dw -1 ; drivers resdev_seg dw -1 abs_unit dw 0 ; absolute unit # rel_unit dw 0 ; relative unit # blkdev_table rb BLKDEV_LENGTH*26 ; save block device driver addr. here ; ; Variable for the FUNC_DEVICE and DEV_INIT sub routines ; strategy rd 0 ; Device Strategy Entry Point strategy_off rw 1 ; Offset strategy_seg rw 1 ; Segment Address interrupt rd 0 ; Device Entry Point interrupt_off rw 1 ; Offset interrupt_seg rw 1 ; Segment Address dev_root rd 0 ; Pointer to Root of Device List dev_offset rw 1 ; Offset of First Device dev_segment rw 1 ; Segment of First Device request_hdr rb RH_SIZE ; DOS Request Header dev_name rb MAX_FILELEN dev_epb rw 0 dev_load_seg rw 1 ; Load Segment for Device Driver dev_reloc_seg rw 1 ; Relocation Factor to be Applied ; ; A number of routines share this common buffer as they are never required ; at the same time. The current clients are - ; ; BDOSLDR.A86: a sector buffer for boot load of IBMDOS ; CONFIG.A86: vector save buffer (during devicehigh) ; NLSFUNC.A86: scratch area for country info Public sector_buffer sector_buffer rb 0 ; rb 512 ; we need to read a single sector Public nls_temp_area nls_temp_area rb 0 ; NLS buffer can be shared with ; rb 258 ; vec_save_buf as they are never ; used together vec_save_buf rd 256 ; reserve space to save int vectors dbcs_buf rb 1 ; BDOS puts a 7 here dbcs_tbl rd 1 ; pointer to DBCS table in BDOS ctry_info rb CI_LENGTH ; country information num_buf dw 0 ; # of buffers allocated ; The following are used for detecting old bus master controllers: removableMediaRequest db 13 ; length of request db 0 ; unit db 15 ; removable media check command dw 0 ; status rb 8 ; reserved bytes readRequest db 30 ; length of request db 0 ; unit db 4 ; read command dw 0 ; status rb 8 ; reserved bytes db 0F8h ; media ID dw 0,0 ; buffer address dw 1 ; read one sector dw 0FFFFh ; use big sector read dw 0,0 ; Volume ID dw 1,0 ; starting sector zero UpperMemoryBuffer dw 0 numUnits dw 0 deblockPointer rd 0 deblockOffset dw 0 deblockSeg dw 0 Public DeblockSetByUser DeblockSetByUser db FALSE end