; File : $BDOSLDR.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$ ; ; ENDLOG include i:reqhdr.equ include i:driver.equ include config.equ ; MISC constants CR equ 0dh ;the usual LF equ 0ah DATTS equ byte ptr 11 DA_VOLUME equ 08h DA_DIR equ 10h DBLOCK1 equ word ptr 26 eject CGROUP group INITCODE, INITDATA INITCODE cseg 'INITCODE' eject if DOS5 VER_MUSTBE equ 1072h else VER_MUSTBE equ 1071h endif Public dos_version_check Public read_dos ; read BDOS from disk ;-------- read_dos: ; read in the BDOS ;-------- call login_drive ; build BPB for the boot drive mov si,offset dos_name ; get name of file to open call open_file ; open the BDOS file call read_file ; read in the system file ret login_drive: ;----------- les di,boot_device ; get device driver address mov dl,boot_drv ; get the boot drive sub unit mov ax,es or ax,di ; make sure boot device is initialised jnz login_drive10 jmp dev_fail login_drive10: mov ax,es:6[di] ; get strategy offset mov strat_off,ax mov strat_seg,es ; get strategy segment mov ax,es:8[di] ; get interrupt offset mov intrpt_off,ax mov intrpt_seg,es ; get interrupt segment mov bx,offset req_hdr mov ds:RH_UNIT,dl ; save logical unit to use mov ds:RH_CMD,CMD_BUILD_BPB call device_request ; tell it to build a BPB jc dev_fail ; return if can't determine BPB push ds ! push si push ds ! pop es mov di,offset local_bpb ; ES:DI -> local BPB copy mov cx,BPB_LENGTH lds si,ds:RH2_BPB ; copy BPB to local memory rep movsb pop si ! pop ds ; Now we have to figure out whether the media uses 12 or 16 bit FATs. ; To that end, we need to compute the # of clusters on the drive: mov fat16,0 ; assume 12 bit FAT mov al,BT_nfats ; compute FAT size mov ah,0 ; AX = # of FAT copies (usually 2) mul BT_fat_size ; AX/DX = size of FAT in sectors add ax,BT_reserved_sectors ; add in bootstrap sectors adc dx,0 mov cx,ax ; CX/BP = sector address of root dir mov bp,dx mov ax,32 ; compute root directory size mul BT_dirsize ; AX/DX = bytes in directory mov bx,BT_bytes_per_sector dec bx ; BX = sector size - 1 for rounding add ax,bx ; round up to next sector size adc dx,0 inc bx ; BX = sector size in bytes div bx ; AX = # of root directory sectors add cx,ax ; CX/BP = sectors before data area adc bp,0 mov ax,BT_total_sectors ; AX/DX = total disk size in sectors sub dx,dx test ax,ax ; is it actually larger than 65535? jnz dev_small ; no, AX/DX is correct mov ax,BT_total_long ; else get real size from extension mov dx,BT_total_long+2 dev_small: ; AX/DX = disk size in sectors sub ax,cx ; AX/DX = data sectors sbb dx,bp ; now convert this to clusters mov bl,BT_sctr_per_cluster mov bh,0 ; BX = sectors per clusters div bx ; AX = # of data clusters inc ax inc ax ; cluster 0,1 are reserved cmp ax,0FF6h ; is this too large for 12 bits? jbe dev_12bit ; skip if 12 bits will do mov fat16,-1 ; else we use 16 bits dev_12bit: ret dos_version_check: ;----------------- mov ax,4452h int 21h ; try and get DRDOS version number jc dev_fail ; it's not DRDOS ! cmp ax,VER_MUSTBE ; version check the DRDOS BDOS jne dev_fail ; reject all but the one we want ret ; return now I'm happy dev_fail: ; any error has occurred loading the BDOS ;-------- ; Print '$' terminated message at offset DX to console without using the BDOS ; mov dx,offset dos_msg les di,resdev_chain ; get first device driver address fail_scan: test es:DH_ATTRIB[di],DA_CHARDEV jz fail_next ; skip if not a character device test es:DH_ATTRIB[di],DA_ISCOT jnz fail_found ; skip if console device found fail_next: les di,es:[di] ; get next device jmps fail_scan fail_found: mov ax,es:6[di] ; get strategy offset mov strat_off,ax mov strat_seg,es ; get strategy segment mov ax,es:8[di] ; get interrupt offset mov intrpt_off,ax mov intrpt_seg,es ; get interrupt segment mov bx,offset req_hdr mov ds:RH_CMD,CMD_OUTPUT ; write to console mov ds:RH_LEN,RH4_LEN ; set request header length mov ds:RH4_BUFOFF,dx ; set address of string mov ds:RH4_BUFSEG,ds mov ds:RH4_COUNT,-1 mov si,dx ; now find the end of the string fail_count_chars: inc ds:RH4_COUNT ; print another char lodsb ; examine the next one cmp al,'$' ; terminating char ? jnz fail_count_chars call device_request ; call the console driver sti jmps $ ; wait for reboot device_request: ; general device driver interface ;-------------- ; entry: BX -> request header ; exit: CY = 1 if error push ds ! push es push ds ! pop es mov ds,strat_seg callf cs:strat_ptr callf cs:intrpt_ptr pop es ! pop ds test ds:RH_STATUS,RHS_ERROR jnz devreq_err clc ret devreq_err: jmp dev_fail ; print error message ret open_file: ; open BDOS system file ;--------- ; entry: SI -> 11 byte file name mov al,BT_nfats cbw mul BT_fat_size ; DX:AX = # FAT sectors mov cx,ax ; CX = rel_sctr dir start mov dx,BT_dirsize ; dx = # entries to scan open_f1: ; CX = current dir sector ; DX = current dir count ; SI -> file name push cx ! push dx ! push si push ds ! pop es ; ES:BX -> sector buffer mov bx,offset sector_buffer mov dx,1 ; read one directory sector call rd_sector_rel ; via disk driver pop si ! pop dx ! pop cx inc cx ; increment sector for next time sub bx,bx ; start at beginning of sector open_f2: lea di,sector_buffer[bx] ; ES:DI -> directory entry push si ! push di ! push cx ; save name ptr and count push ds ! pop es mov cx,11 repe cmpsb ; check if name matches pop cx ! pop di ! pop si jne open_f3 ; skip if name doesn't match test DATTS[di],DA_DIR+DA_VOLUME jz open_foundit ; skip if matches open_f3: dec dx ; count down root directory entries jz open_fail ; skip if root directory done add bx,32 ; next entry in directory sector cmp bx,BT_bytes_per_sector ; sector complete? jb open_f2 ; loop back while more jmps open_f1 ; read next directory sector open_fail: ; file not found jmp dev_fail open_foundit: ; found the open file handle mov ax,DBLOCK1[di] ; get first disk block mov start_cluster,ax ; save starting cluster xor ax,ax ret ; return success read_file: ; read BDOS files into memory at MEM_CURRENT:0000 ;--------- mov ax,current_dos ; Get the Segment address to mov dta_seg,ax ; load the BDOS at sub ax,ax mov dta_off,ax rd_file1: mov cluster_count,1 ; we can read at least one cluster mov cx,start_cluster rd_file2: ; check if next cluster contiguous push cx ; save current cluster number call next_cluster ; get link to next cluster pop dx ; get previous cluster # inc dx ; is current cluster contiguous? cmp cx,dx ; contiguos if CX == DX jne rd_file3 ; no, need a separate read inc cluster_count ; else read one more cluster jmps rd_file2 ; try again with next cluster rd_file3: ; CX = next chain, multi cluster read push cx ; save start of next chain les bx,dta_ptr ; ES:BX -> transfer address mov cx,start_cluster ; previous contiguous chain starts here mov dx,cluster_count ; length of chain in clusters call rd_cluster ; read DX clusters mov al,BT_sctr_per_cluster mov ah,0 ; AX = sectors per cluster mul cluster_count ; AX = sectors in chain to read mul BT_bytes_per_sector ; AX = bytes in chain to read add dta_off,ax pop cx ; CX = next (noncontiguous) cluster mov start_cluster,cx ; start of new chain inc cx ; was it end of file cluster number? jnz rd_file1 ; go back for more if not ; else all clusters done ret get_FAT_byte: ;------------ ; entry: BX = offset into FAT mov ax,bx ; BX = offset into FAT sub dx,dx ; AX/DX = 32 bit offset div BT_bytes_per_sector ; AX = sector, DX = offset in sector push dx ; save offset in sector call locate_FAT ; read FAT sector AX pop bx ; BX = offset in FAT sector mov al,sector_buffer[bx] ; get byte from FAT buffer ret locate_FAT: ;---------- ; entry: AX = FAT sector to locate cmp ax,current_fatsec ; AX = sector offset into FAT je locate_FAT_match ; O.K. if same as last time mov current_fatsec,ax ; set new sector for next time push cx ! push si ; preserve FAT index mov cx,ax ; CX = sector number mov bx,offset sector_buffer push ds ! pop es ; ES:BX -> sector buffer mov dx,1 ; DX = single sector call rd_sector_rel ; read FAT sector pop si ! pop cx ; restore FAT index locate_FAT_match: ; return with right sector in buffer ret eject ; reads sectors relative to start of DOS area on disk (start=0) ; same parameters as rd_sector rd_sector_rel: ;------------- ; entry: CX = sector address relative to first FAT sector ; DX = sector count sub bp,bp ;overflow word = 0 add cx,BT_reserved_sectors adc bp,0 ; jmps rd_sector ; reads absolute sectors from hard disk using rom bios rd_sector: ;--------- ; entry: DX = number of sectors ; ES:BX -> data transfer buffer ; DS -> program global data segment ; CX/BP = absolute sector # (32 bit) (low/high) push cx ! push dx ; save parameters mov req3_bufoff,bx ; save transfer offset mov req3_bufseg,es ; save transfer segment mov req3_count,dx ; set sector count mov bx,offset req_hdr ; BX -> request header mov ds:RH_CMD,CMD_INPUT ; read from disk device mov req3_sector,cx ; set requested sector address if DOS5 mov req_hdr,RH4_LEN mov req3_sector32,cx ; with 32 sector number mov req3_sector32+2,bp test bp,bp ; large sector number? jz rd_sec1 ; no, normal request header mov req3_sector,0FFFFh ; mark as a large request rd_sec1: else mov req3_sector+2,bp ; (support large DOS drives) mov req_hdr,24 ; indicate large request endif call device_request ; tell it to read sectors pop cx ! pop dx ret ; if CY, AH=error code rd_cluster: ;---------- ; entry: CX = DOS cluster number. ; DX = cluster count ; ES:BX -> transfer buffer push bx ! push es mov al,BT_sctr_per_cluster mov ah,0 ; AX = sectors per cluster mul dx ; AX = sectors in all clusters push ax ; save the sector count sub cx,2 ; cluster 2 is data area start mov al,BT_sctr_per_cluster cbw mul cx ; AX,DX = relative sector # mov cx,ax mov bp,dx ; CX,BP = data area sector # mov al,BT_nfats ; compute FAT size mov ah,0 ; AX = # of FAT copies (usually 2) mul BT_fat_size ; AX/DX = size of FAT in sectors add cx,ax adc bp,dx ; CX,BP = end of FAT sectors mov ax,32 mul BT_dirsize ; AX,DX = bytes in root directory mov bx,BT_bytes_per_sector dec bx add ax,bx ; round up directory size adc dx,0 inc bx div bx ; AX = root directory sectors add cx,ax adc bp,0 ; add root directory size add cx,BT_reserved_sectors ; add in boot sector(s) adc bp,0 pop dx ! pop es ! pop bx ; sector count, disk address jmp rd_sector ; DX secs from CX/BP to ES:BX ; Finds the NEXT cluster after the one passed in CX in an allocation ; chain by using the FAT. Returns the carry set if the end of chain ; mark is found, otherwise returns the NEW cluster # in CX. next_cluster: ;------------ push dx ! push bx ; save some registers cmp fat16,0 ; check if this is 12 bit media je next_cluster12 ; skip if old fashioned 12 bit mov ax,2 mul cx ; AX/DX = byte offset in FAT (max. 128K) div BT_bytes_per_sector ; AX = FAT sector #, DX = byte offset push dx ; save byte offset within sector call locate_FAT ; get FAT sector AX pop bx ; BX = offset within sector mov cx,word ptr sector_buffer[bx] ; get 16 bit from FAT cmp cx,0FFF7h ; check if too large for # jae next_cluster_eof ; set carry, EOF clc jmps next_cluster_ret ; good link next_cluster12: ; DOS 2.x disk push cx ; save cluster number mov bx,cx add bx,bx ; BX = cluster# * 2 add bx,cx ; BX = cluster# * 3 shr bx,1 ; BX = cluster# * 1.5 push bx ; save offset in the FAT inc bx ; BX = offset of high byte call get_FAT_byte ; get the high byte in AL pop bx ; BX = offset of low byte push ax ; save high byte on stack call get_FAT_byte ; get the low byte in AL pop bx ; pop off high byte into BL mov ah,bl ; set high byte, AX = word pop cx ; restore cluster number shr cx,1 ; test if even or odd jnc even_fat ; if even entry, done mov cl,4 ; odd entry, shift down one nibble shr ax,cl ; else need to justify even_fat: ; even entry, strip off top bits and ax,0fffh ; bx[0..11] are cluster mov cx,ax ; CX = cluster number cmp cx,0ff7h ; compare with largest legal 12 bit # jae next_cluster_eof ; check for end mark clc jmps next_cluster_ret ; return value in CX, CY = 0 next_cluster_eof: mov cx,-1 ; indicate end of chain stc ; end of chain next_cluster_ret: pop bx ! pop dx ret eject ; ; INITIALIZED DATA SEGMENT ; ======================== INITDATA dseg 'INITDATA' extrn resdev_chain:dword ; resident device driver root extrn current_dos:word ; current BDOS segment extrn boot_device:dword ; device driver we boot from extrn boot_drv:byte ; boot drive extrn dos_name:byte ; name of BDOS file strat_ptr rd 0 strat_off rw 1 strat_seg rw 1 intrpt_ptr rd 0 intrpt_off rw 1 intrpt_seg rw 1 dta_ptr rd 0 dta_off rw 1 dta_seg rw 1 start_cluster rw 1 cluster_count rw 1 current_fatsec dw -1 ; no FAT sector read yet fat16 dw 0 ; defaults to 12 bit FAT ; single error message if BDOS can't be loaded: include initmsgs.def ; Include TFT Header File ;dos_msg db CR,LF,'Can''t load DOS file.$' ; static request header for DOS device driver I/O req_hdr db 22 req_unit rb 1 req_cmd rb 1 req_status rw 1 rd 2 req_media rb 1 rb 16 req1_return equ byte ptr req_media+1 req1_volid equ word ptr req_media+2 req2_bufoff equ word ptr req_media+1 req2_bufseg equ word ptr req_media+3 req2_bpb equ word ptr req_media+5 req3_buffer equ dword ptr req_media+1 req3_bufoff equ word ptr req_media+1 req3_bufseg equ word ptr req_media+3 req3_count equ word ptr req_media+5 req3_sector equ word ptr req_media+7 req3_volid equ word ptr req_media+9 req3_sector32 equ word ptr req_media+13 ; local copy of the BPB for the boot device local_bpb rb 0 BT_bytes_per_sector rw 1 BT_sctr_per_cluster rb 1 BT_reserved_sectors rw 1 BT_nfats rb 1 BT_dirsize rw 1 BT_total_sectors rw 1 BT_fatid rb 1 BT_fat_size rw 1 BT_sectors_per_track rw 1 BT_nheads rw 1 BT_hidden_sectors rw 2 BT_total_long rw 2 BPB_LENGTH equ (offset $-offset local_bpb) extrn sector_buffer:byte end