; File : $DISK.ASM$ ; ; Description : ; ; Original Author : ; ; Last Edited By : $Author: RGROSS$ ; ;-----------------------------------------------------------------------; ; Copyright Unpublished Work of Novell, Inc. All Rights Reserved. ; ; THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL, ; PROPRIETARY AND TRADE SECRET INFORMATION OF NOVELL, INC. ; ACCESS TO THIS WORK IS RESTRICTED TO (I) NOVELL, INC. EMPLOYEES ; WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF ; THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN NOVELL, INC. WHO ; HAVE ENTERED INTO APPROPRIATE LICENSE AGREEMENTS. 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 NOVELL, 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: $ ; DISK.ASM 1.1 93/11/18 17:20:12 RGROSS ; ; DISK.ASM 1.41 93/11/18 17:20:25 IJACK ; ; DISK.ASM 1.40 93/11/10 00:28:12 IJACK ; Format changes so you can format your hard disk ; DISK.ASM 1.39 93/11/08 21:47:25 IJACK ; Add hidden sectors to ioctl format etc of hard disks ; DISK.ASM 1.38 93/11/02 16:09:29 IJACK ; Always zero BPB_HIDDEN_SECTORS on floppies - problem with PCW free disk which ; has garbage in those fields. ; DISK.ASM 1.37 93/10/18 17:33:18 IJACK ; format c: fix ; DISK.ASM 1.36 93/10/11 18:37:24 IJACK ; media-change checks serial-numbers for 3.5" disks ; DISK.ASM 1.35 93/10/06 22:09:16 IJACK ; vec_save extrn replaced by orgInt13 extrn ; DISK.ASM 1.34 93/09/03 20:13:32 IJACK ; Fix bug in disk formatting ; DISK.ASM 1.33 93/09/01 17:40:31 IJACK ; update UDSC_TIMER after media check forced by drive change ; (DBASE IV slow installation problem) ; DISK.ASM 1.32 93/08/12 15:33:07 IJACK ; Handle DMA error from multi-track read (ancient PC-XT hard disk) ; DISK.ASM 1.31 93/08/03 15:29:01 IJACK ; use serial numbers for media change detection ; DISK.ASM 1.30 93/08/02 18:44:50 IJACK ; don't trust the changeline if switching drives ; DISK.ASM 1.29 93/08/02 18:38:19 IJACK ; ; DISK.ASM 1.28 93/08/02 14:47:38 IJACK ; ; DISK.ASM 1.27 93/07/29 21:00:24 IJACK ; get rid of genpb_ptr and genpb_minor ; DISK.ASM 1.26 93/07/26 21:18:25 IJACK ; Correctly return UDSC_ root from Int 2F/0803 ; DISK.ASM 1.25 93/07/26 18:07:21 IJACK ; Switch ms-windows to full screen when prompting for disk ; DISK.ASM 1.24 93/07/23 17:34:27 IJACK ; fix floppy/driver.sys support ; DISK.ASM 1.23 93/07/22 20:37:42 IJACK ; ; DISK.ASM 1.22 93/07/22 19:43:46 IJACK ; switch over to REQUEST.EQU ; change floppy drive order, add get/set serial number ; DISK.ASM 1.21 93/07/19 18:57:21 IJACK ; Add header ; ; ENDLOG include BIOSGRPS.EQU include DRMACROS.EQU ; standard DR macros include IBMROS.EQU ; ROM BIOS equates include REQUEST.EQU ; request header equates include BPB.EQU ; BIOS parameter block equates include UDSC.EQU ; unit descriptor equates include DRIVER.EQU ; device driver equates int_____DISK_INT macro call Int13 endm FASTSETTLE equ FALSE ; disable "head settle == 0 ms" RETRY_MAX equ 3 ; do 3 retries if we get an error MAX_SPT equ 40 ; maximum sectors per track SECSIZE equ 512 IDOFF equ SECSIZE-2 ; last word in boot sector is ID PTOFF equ IDOFF-40h ; 4*16 bytes for partition def's DOS20_ID equ 1 ; DOS 2.0 partition, < 4086 clusters DOS30_ID equ 4 ; DOS 3.0 partition, < 65536 sectors DOSEX_ID equ 5 ; DOS 3.3 extended partition DOS331_ID equ 6 ; COMPAQ DOS 3.31 partition > 32 Mb ; Now for the secure partition types SEC_ID equ 0C0h ; New DR secure partition types SEC_ID2 equ 0D0h ; Old DR secure partition types page CGROUP group CODE, RCODE, ICODE, RESBIOS, IDATA CG equ offset CGROUP Assume CS:CGROUP, DS:CGROUP, ES:Nothing, SS:Nothing IVECT segment at 0000h org 0013h*4 i13off dw ? i13seg dw ? org 001Eh*4 i1eptr dd ? org 002Fh*4 i2Foff dw ? i2Fseg dw ? org 0472h reset_flag dw ? org 0504h dual_byte db ? ; multiple drive byte at 50:4 IVECT ends ROS segment at 0F000h org 0FFF0h reset proc far reset endp ROS ends CODE segment 'CODE' extrn endbios:word ; for device driver INIT function extrn read_system_ticks:near ; get system tick count in CX/DX extrn Int13Trap:near extrn Int2FTrap:near extrn orgInt13:dword extrn i13pointer:dword extrn i13off_save:word extrn i13seg_save:word extrn NumDiskUnits:byte extrn DeblockSeg:word extrn local_parms:byte extrn parms_spt:byte extrn parms_gpl:byte extrn local_buffer:byte extrn local_pt:word extrn local_id:word extrn layout_table:word extrn bpbs:word extrn bpb160:byte extrn bpb360:byte extrn bpb720:byte extrn NBPBS:abs extrn bpbtbl:word extrn req_off:word extrn req_seg:word udsc_root label dword dw -1,-1 orig_int1e_off dw 522h orig_int1e_seg dw 0 new_int1e_off dw 522h new_int1e_seg dw 0 Public i13_AX i13_AX label word i13_size db ? ; number of sectors to xfer i13_op db ? ; Int13 Operation i13_dma_ptr label dword i13_dma_off dw ? i13_dma_seg dw ? activeRosUnit db ? ; currently active ROS unit include biosmsgs.def ; Include TFT Header File ifdef JAPAN extrn disk_msgA_jpn :byte extrn disk_msgB_jpn :byte endif ;disk_msgA db 13,10,'Insert disk for drive ' ;disk_msgB db ': any press any key when ready ', 0 page Assume DS:nothing, SS:nothing, ES:nothing CODE ends RCODE segment 'RCODE' extrn DataSegment:word even Int13 proc near clc push bp int DISK_INT pop bp ret Int13 endp ros_errors db 03h, 80h, 08h, 10h, 40h, 04h, 06h, 00h dos_errors db 00h, 02h, 04h, 04h, 06h, 08h, 0Fh, 0Ch NUMROSERR equ dos_errors - ros_errors ; The following code is required in order to cope with ; application programs invoking Int 13h directly. It ; handles applications that format floppies or access the ; disk via Int 13h after a floppy disk change. Assume CS:CGROUP, DS:Nothing, ES:Nothing, SS:Nothing Public Int13Unsure ;---------- Int13Unsure proc far ;---------- sti cld push ds mov ds,cs:DataSegment Assume DS:CGROUP call i13_unsure ; no longer sure of this drive pop ds ret Int13Unsure endp Public Int13Deblock ;----------- Int13Deblock proc far ;----------- ; handle user programs formatting the disk sti cld push ds mov ds,cs:DataSegment Assume DS:CGROUP pushx ; save work registers mov i13_dma_off,bx mov i13_dma_seg,es i13_deblock10: pushx mov cl,4 mov ax,i13_dma_seg ; get transfer address shl ax,cl ; get A4..A15 from segment add ax,i13_dma_off ; combine with A0..A15 from offset not ax ; AX = # of bytes left in 64K bank xor dx,dx mov cx,SECSIZE div cx ; convert this to physical sectors mov dl,i13_size ; see if we can xfer amount wanted cmp al,dl ; capable of more than requested? jb i13_deblock20 ; skip if we can do it all xchg ax,dx ; we can do them all i13_deblock20: les bx,i13_dma_ptr ; do the xfer to here popx mov ah,i13_op ; get read/write/verify operation test al,al ; if zero length possible jz i13_deblock30 ; then deblock mov di,es ; get transfer address cmp di,DeblockSeg ; is this in high memory ? jb i13_deblock50 ; then force through deblock buffer i13_deblock30: push ds ; if deblocking then we'd better pop es ; point at local buffer we mov bx,CG:local_buffer ; will be using for actual I/O cmp i13_op,ROS_WRITE jne i13_deblock40 ; skip data copy if not writing to disk push ds push cx mov di,bx ; ES:DI -> local buffer lds si,i13_dma_ptr ; DS:SI -> data to write mov cx,SECSIZE/2 rep movsw ; copy to deblocking buffer pop cx pop ds i13_deblock40: mov al,1 ; do a single sector via buffer clc pushf ; fake an Int call i13pointer ; to the track handler jc i13_deblock90 ; stop on error mov al,1 ; restore AL for buggy bios's cmp i13_op,ROS_READ ; if we are reading then we'll jne i13_deblock60 ; have to copy data out of push cx ; the deblocking buffer les di,i13_dma_ptr ; ES:DI -> dest for data mov si,CG:local_buffer ; point at local buffer which mov cx,SECSIZE/2 ; contains actual data rep movsw ; copy from deblocking buffer pop cx jmps i13_deblock60 i13_deblock50: push ax ; save # sectors in xfer clc pushf ; fake an Int call i13pointer ; do the operation pop bx mov al,bl ; restore AL for buggy bios's jc i13_deblock90 ; stop on error i13_deblock60: ; we succeeded in doing AL sectors sub i13_size,al ; forget about those we have done jbe i13_deblock90 ; and do more if there are any push ax mov ah,SECSIZE/16 mul ah ; AX = paras to inc DMA address add i13_dma_seg,ax ; up DMA address by this amount pop ax call i13_point_unit ; ES:DI -> UDSC_ jc i13_deblock90 ; exit if we can't find it mov bx,cx ; get sector/cylinder in BX and bx,0003Fh ; BX = sector and cx,0FFC0h ; CX = mandled cylinder bits add bl,al ; work out new sector i13_deblock70: mov ax,es:UDSC_BPB+BPB_SPT[di] cmp bx,ax ; still on the same track ? jbe i13_deblock80 ; easy if no overflow onto next track sub bx,ax ; subtract a tracks worth inc dh ; and move onto next head mov al,dh ; isolate head from cylinder and ax,003Fh ; bits 10/11 cmp ax,es:UDSC_BPB+BPB_HEADS[di] jb i13_deblock70 ; onto next track yet ? and dh,0C0h ; back to head zero add ch,1 ; onto next track (bits 0-7) jnc i13_deblock70 ; overflow to bits 8-9 ? add cl,040h ; yes, "inc" bits 8/9 of cylinder jnc i13_deblock70 ; overflow to bits 10-11 ? add dh,040h ; yes, "inc" bits 10/11 of cylinder jmps i13_deblock70 i13_deblock80: or cx,bx ; recombine sector/cylinder jmp i13_deblock10 ; and do some more i13_deblock90: popx ; restore work registers pop ds ; recover user DS ret 2 ; return to user with result i13_point_unit proc near ;------------- ; On Entry: ; DL = ROS unit ; On Exit: ; ES:DI -> UDSC_ for that unit ; All other regs preserved ; les di,udsc_root ; ES:DI -> 1st es:UDSC_ i13_point_unit10: cmp dl,es:UDSC_RUNIT[di] ; find the physical unit je i13_point_unit20 les di,es:UDSC_NEXT[di] cmp di,0FFFFh ; else try the next es:UDSC_ jne i13_point_unit10 mov ah,09h ; return DMA error to caller as we stc ; don't know about this unit i13_point_unit20: ret i13_point_unit endp Int13Deblock endp i13_unsure proc near ;--------- ; mark physical drive DL as unsure ; pushx lds si,udsc_root i13_unsure10: cmp dl,ds:UDSC_RUNIT[si] ; does it match ROS drive? jne i13_unsure20 ; skip if not or ds:UDSC_FLAGS[si],UDF_UNSURE i13_unsure20: ; next drive lds si,ds:UDSC_NEXT[si] cmp si,0FFFFh jne i13_unsure10 popx ; restore registers ret i13_unsure endp Assume DS:Nothing, SS:Nothing, ES:Nothing Public Int2FHandler Int2FHandler proc far ;----------- ; On Entry we have offset/seg of next in chain on the stack ; (ie. we can pass on by a RETF) ; cmp ah,8 ; DRIVER.SYS support je i2F_driver cmp ah,13h ; int13 intercept jne i2F_iret ; ; Int 13 interception support ; --------------------------- ; ; On Entry: ; DS:DX -> New Int 13 vector ; ES:BX -> Int 13 vector restored by Int 19 ; ; On Exit: ; DS:DX -> Old Int 13 vector ; ES:BX -> Old Int 13 vector restored by Int 19 ; i2F_i13_intercept: mov ax,ds mov ds,cs:DataSegment Assume DS:CGROUP xchg dx,ds:i13off_save xchg ax,ds:i13seg_save push ax xchg bx,ds:word ptr orgInt13 mov ax,es xchg ax,ds:word ptr orgInt13+2 mov es,ax pop ds Assume DS:Nothing i2F_iret: iret ; ; DRIVER.SYS support ; ------------------- ; ; On Entry: ; AX=0800, installation check ; AX=0801, add new block device at DS:SI ; AX=0802, execute driver request at ES:BX ; AX=0803, return address of first es:UDSC_ ; i2F_driver: cmp al,1 jb i2F_driver_check je i2F_driver_add cmp al,3 jb i2F_driver_req je i2F_driver_point iret i2F_driver_check: ; ; Installation check ; mov al,0ffh ; say we are installed iret i2F_driver_add: ; ; Add new block device DS:DI ; push ds push es push ds pop es ; ES:DI -> unit mov ds,cs:DataSegment call add_unit pop es pop ds iret i2F_driver_point: ; ; return DS:DI -> first UDSC_ ; mov ds,cs:DataSegment ; DS -> our data lds di,ds:udsc_root iret ; ; Execute DRIVER.SYS request ES:BX ; i2F_driver_req: push ds mov ds,cs:DataSegment Assume DS:CGROUP mov ds:req_off,bx ; fill in request pointer mov ds:req_seg,es ; as if it was local pop ds Assume DS:Nothing push cs:driverTable ; fiddle the table address jmp DriverFunction ; then go to normal handler extrn DriverFunction:near extrn IntDiskTable:word ; = DiskTable driverTable dw CG:IntDiskTable ; push address of table on ; stack as DriverFunction ; examines it Int2FHandler endp Assume DS:CGROUP, SS:Nothing, ES:Nothing Public DiskTable DiskTable label word db 24 ; Last supported function dw CG:dd_init ; 0-initialize driver dw CG:dd_medchk ; 1-media change check dw CG:dd_build_bpb ; 2-build BIOS Parameter Block dw CG:dd_error ; 3-IOCTL string input dw CG:dd_input ; 4-input dw CG:dd_error ; 5-nondestructive input (char only) dw CG:dd_error ; 6-input status (char only) dw CG:dd_error ; 7-input flush dw CG:dd_output ; 8-output dw CG:dd_output_vfy ; 9-output with verify dw CG:dd_error ; 10-output status (char only) dw CG:dd_error ; 11-output flush (char only) dw CG:dd_error ; 12-IOCTL string output dw CG:dd_open ; 13-device open dw CG:dd_close ; 14-device close dw CG:dd_remchk ; 15-removable media check dw CG:dd_error ; 16-n/a dw CG:dd_error ; 17-n/a dw CG:dd_error ; 18-n/a dw CG:dd_genioctl ; 19-generic IOCTL dw CG:dd_error ; 20-n/a dw CG:dd_error ; 21-n/a dw CG:dd_error ; 22-n/a dw CG:dd_getdev ; 23-get logical drive dw CG:dd_setdev ; 24-set logical drive driver proc near point_unit: ; get unit descriptor for work drive ;---------- ; On Entry: ; ES:BX -> Request Header ; On Exit: ; AL = logical drive ; ES:DI -> es:UDSC_ ; (All other registers preserved) ; mov al,es:RH_UNIT[bx] ; get the unit number (0=A:, 1=B:, etc) les di,udsc_root ; ES:DI -> 1st es:UDSC_ point_unit10: cmp al,es:UDSC_DRIVE[di] ; stop if the logical drive matches je point_unit20 les di,es:UDSC_NEXT[di] cmp di,0FFFFh ; else try the next es:UDSC_ jne point_unit10 pop ax ; don't return to the caller mov ax,RHS_ERROR+1 ; return "invalid unit" error point_unit20: ret add_unit: ; add a new unit to the list ;-------- ; On Entry: ; ES:DI -> UDSC to add ; On Exit: ; ES:DI preserved ; mov al,es:UDSC_DRIVE[di] ; get the logical unit cmp al,MAXPART ; is it too many ? jae add_unit40 push ds mov es:word ptr UDSC_NEXT[di],0FFFFh ; make sure it's terminated and es:UDSC_FLAGS[di],UDF_HARD+UDF_CHGLINE lea si,udsc_root ; DS:SI -> [first UDSC_] add_unit10: cmp ds:word ptr UDSC_NEXT[si],0FFFFh je add_unit30 lds si,ds:UDSC_NEXT[si] ; DS:SI -> UDSC_ we already have mov al,es:UDSC_RUNIT[di] cmp al,ds:UDSC_RUNIT[si] ; do the logical units match ? jne add_unit10 mov ax,ds:UDSC_FLAGS[si] ; inherit some flags push ax and ax,UDF_HARD+UDF_CHGLINE mov es:UDSC_FLAGS[di],ax ; hard disk/changeline inherited pop ax test ax,UDF_HARD jnz add_unit10 ; skip owner stuff on hard drive test ax,UDF_VFLOPPY ; is this a multiple drive anyway ? jnz add_unit20 or ax,UDF_OWNER+UDF_VFLOPPY mov ds:UDSC_FLAGS[si],ax ; no, 1st person becomes owner add_unit20: or es:UDSC_FLAGS[di],UDF_VFLOPPY jmps add_unit10 ; go and try the next add_unit30: mov ds:word ptr UDSC_NEXT[si],di mov ds:word ptr UDSC_NEXT+2[si],es pop ds add_unit40: ret dd_error: ; 3-IOCTL string input ;-------- mov ax,RHS_ERROR+3 ; "invalid command" error ret dd_medchk: ; 1-media change check ;--------- ; entry: ES:BX -> request header ; exit: RH1_RETURN = 0, 1 or FF ; 00 = media may have changed ; 01 = media hasn't changed ; FF = media has been changed call point_unit ; get unit descriptor test es:UDSC_FLAGS[di],UDF_HARD jnz medchk2 ; "hasn't changed" if hard disk call ask_for_disk ; make sure we've got correct floppy mov ax,es:UDSC_FLAGS[di] ; get flags test ax,UDF_UNSURE ; has format/diskcopy occurred? jnz medchk6 ; may have changed to different format test ax,UDF_CHGLINE jz medchk3 ; skip ROS call if no change line mov dl,es:UDSC_RUNIT[di] mov al,dl ; don't trust changeline if we are xchg al,activeRosUnit ; changing floppies cmp al,dl ; return may have changed jne medchk3 mov ah,ROS_DSKCHG ; get disk change status function int_____DISK_INT ; AH=0: DC low, AH=6: DC active jc medchk5 ; disk change not active? medchk2: mov al,01h ; disk hasn't changed jmps medchk_ret medchk3: ; no changeline support, use timer call read_system_ticks ; get system tick count in CX/DX mov ax,dx xchg ax,es:UDSC_TIMER[di] ; get previous time and update sub dx,ax mov ax,cx xchg ax,es:UDSC_TIMER+2[di] sbb cx,ax ; CX/DX = # ticks since last access jne medchk5 ; media could have changed if > 64k cmp dx,18*3 ; more than three seconds expired? jb medchk2 ; "not changed" if access too recent medchk5: mov cx,1 ; read track 0, sector 1 (boot sector) call login_read ; to check the builtin BPB jc medchk6 ; may have changed if read error mov al,local_buffer+11+BPB_FATID cmp al,0F0h ; check if we find a BPB jb medchk6 ; may have changed if not good BPB cmp al,es:UDSC_BPB+BPB_FATID[di] jne medchk8 ; has media byte changed ? mov si,offset CGROUP:local_buffer+UDSC_BPB_LENGTH+11+2 lodsb ; get extended boot sub al,29h ; do we have an extended boot ? je medchk7 ; no, test against our dummy value push cs pop ds ; DS:SI -> our dummy value mov si,offset CGROUP:dummyMediaID medchk7: push di lea di,UDSC_SERIAL[di] mov cx,2 repe cmpsw ; is serial number unchanged ? pop di je medchk6 ; then return may have changed medchk8: lea ax,UDSC_LABEL[di] ; ES:AX -> ASCII label lds bx,REQUEST[bp] mov ds:word ptr RH1_VOLID[bx],ax mov ds:word ptr RH1_VOLID+2[bx],es mov al,0FFH ; return disk changed jmps medchk_ret medchk6: mov al,00h ; disk may have changed medchk_ret: and es:UDSC_FLAGS[di],not UDF_UNSURE les bx,REQUEST[bp] mov es:RH1_RETURN[bx],al ; set return value sub ax,ax ret page dd_build_bpb: ; 2-build BIOS Parameter Block ;------------ call point_unit ; get unit descriptor test es:UDSC_FLAGS[di],UDF_HARD jnz bldbpb1 ; BPB doesn't change for hard disks call login_media ; try to determine media type (BPB) jc bldbpb_err bldbpb1: mov es:UDSC_OPNCNT[di],0 ; no files open at this time and es:UDSC_FLAGS[di],not UDF_UNSURE ; media is sure lea si,UDSC_BPB[di] mov ax,es les bx,REQUEST[bp] mov es:RH2_BPBOFF[bx],si ; return the current BPB mov es:RH2_BPBSEG[bx],ax xor ax,ax ret bldbpb_err: jmp xlat_error ; return error code ; ret login_media: ; determine BPB for new floppy disk ;----------- push ds mov cx,1 ; read track 0, sector 1 (boot) call login_read ; to determine media type jc login_media_err ; abort if physical error cmp local_buffer+11+BPB_FATID,0F0h jb login_media10 ; fail unless FATID sensible lodsw ; get JMP instruction from boot sector xchg ax,bx ; save in BX lodsb ; get next 3rd byte in AX add si,8 ; skip JMP, OEM name, SI -> BPB cmp bl,0E9h ; does it start with a JMP ? je login_media40 cmp bl,069h je login_media40 cmp bl,0EBh ; how about a JMPS ? jne login_media10 cmp al,090h ; then we need a NOP je login_media40 login_media10: mov cx,2 ; read track 0, sector 2 (FAT) call login_read ; try to read the sector jc login_media_err ; abort if physical error cmp word ptr 1[si],-1 ; bytes 1, 2 must be 0FFh, 0FFh jne login_media30 ; default media if bad FAT lodsb ; else get FAT ID byte mov si,CG:bpb160 ; look through builtin BPB table mov cx,NBPBS ; # of builtin BPBs login_media20: cmp al,BPB_FATID[si] ; does it match one we know? je login_media40 ; yes, use builtin BPB add si,BPB_LENGTH ; else move to next BPB loop login_media20 ; repeat for all BPBs login_media30: ; can't find that FAT ID lea si,UDSC_DEVBPB[di] ; use the default type push es pop ds ; use BPB at DS:SI -> login_media40: push di lea di,UDSC_BPB[di] ; ES:DI -> unit descriptor (UDSC) mov cx,UDSC_BPB_LENGTH ; size of a BPB (less reserved stuff) rep movsb ; copy into unit descriptor pop di mov es:UDSC_BPB+BPB_SECSIZ[di],SECSIZE ; mov cx,0 mov es:word ptr (UDSC_BPB+BPB_HIDDEN)[di],cx mov es:word ptr (UDSC_BPB+BPB_HIDDEN+2)[di],cx cmp si,offset CGROUP:local_buffer+UDSC_BPB_LENGTH+11 jne login_media50 ; is the BPB from the boot sector ? ; mov ax,ds ; if so then check for media id ; cmp ax,cs:DataSegment ; seg check redundant as UDSC_DEVBPB ; jne login_media50 ; is followed by 7 bytes of zero lodsw ; skip 2 bytes lodsb ; now get possible boot signature cmp al,29h ; is it an extended boot sector ? je login_media60 ; yes, use it login_media50: push cs pop ds ; DS:SI -> our dummy value mov si,offset CGROUP:dummyMediaID login_media60: call UpdateMediaID ; update UDSC_ with media info clc login_media_err: pop ds ret dummyMediaID dd 0 ; serial number 0 db 'NO NAME ' db 'FAT12 ' UpdateMediaID: ;------------- ; On Entry: ; DS:SI -> extended boot record info ; ES:DI -> UDSC_ to update ; On Exit: ; ES:DI preserved ; push di xor ax,ax ; AX = a handy zero lea di,UDSC_SERIAL[di] movsw movsw ; copy serial number pop di push di lea di,UDSC_LABEL[di] mov cx,11 rep movsb ; copy the volume label stosb ; zero terminate it pop di push di lea di,UDSC_FSTYPE[di] movsw movsw movsw movsw ; copy the file system type stosb ; zero terminate it pop di ret login_read: ; entry: CH, CL = cylinder/sector to read ; exit: CY = 1, AH = status if error ; else local_buffer filled in mov dl,es:UDSC_RUNIT[di] ; DL = ROS drive mov dh,0 ; DH = head number login_read_dx: ; read on drive DL, head DH ;------------- ; (entry for hard disk login) mov P_RETRY[bp],RETRY_MAX ; initialize retry count logrd1: push es mov ax,ROS_READ*256 + 1 ; read one sector from ROS push ds pop es ; ES = DS = local segment mov bx,CG:local_buffer int_____DISK_INT ; call the ROM BIOS pop es jnc logrd3 ; skip if no disk error push ax ; mov ah,ROS_RESET xor ax,ax int_____DISK_INT ; reset the drive pop ax dec P_RETRY[bp] jnz logrd1 ; loop back if more retries logrd2: stc logrd3: mov si,CG:local_buffer ret page dd_output: ; 8-output ;--------- mov P_ROSCMD[bp],ROS_WRITE ; write to floppy/hard disk jmps io_common dd_output_vfy: ; 9-output with verify ;------------- mov P_ROSCMD[bp],ROS_VERIFY ; write & verify floppy/hard disk jmps io_common dd_input: ; 4-input ;-------- mov P_ROSCMD[bp],ROS_READ ; read from floppy/hard disk ; jmps io_common io_common: ; common code for the above three call point_unit ; get unit descriptor call ask_for_disk ; make sure we've got correct floppy call setup_rw ; setup for read/write operation jc io_ret ; return if bad parameters io_loop: call track_rw ; read as much as possible on track jc xlat_error ; return if physical disk error cmp P_COUNT[bp],0 ; test if any more stuff to read jne io_loop ; yes, loop back for more mov al,es:UDSC_RUNIT[di] ; remember the drive that is active mov activeRosUnit,al test es:UDSC_FLAGS[di],UDF_HARD+UDF_CHGLINE jnz io_exit ; skip timer read for hard/changeline call read_system_ticks ; get system tick count in CX/DX mov es:UDSC_TIMER[di],dx mov es:UDSC_TIMER+2[di],cx ; save time of successful access io_exit: xor ax,ax ; all done, no error encountered io_ret: ret xlat_error: ; translate ROS error to DOS error ;---------- ; entry: AH = ROS disk error code, CY = 1 ; exit: AX = status to be returned to BDOS pushx ; save some registers mov al,ah ; AL = ROS error code push cs pop es mov di,CG:ros_errors ; ES:DI -> ROS error code table mov cx,NUMROSERR repne scasb ; scan for match mov ax,RHS_ERROR ; get basic error indication or al,cs:(CG:dos_errors-CG:ros_errors-1)[di] ; combine with type of error popx stc ret setup_rw: ; prepare for INPUT, OUTPUT or OUTPUT_VFY ;-------- ; On Entry: ; ES:DI -> UDSC ; On Exit: ; if CY == 0: ; P_CYL, P_HEAD, P_SECTOR, ; P_DMAOFF, P_DMASEG, P_COUNT initialized ; if CY == 1: invalid parameters detected ; ES:DI preserved push ds lds bx,REQUEST[bp] mov ax,ds:RH4_BUFOFF[bx] ; get offset of transfer buffer mov P_DMAOFF[bp],ax ; set transfer offset mov ax,ds:RH4_BUFSEG[bx] ; get segment of transfer buffer mov P_DMASEG[bp],ax ; set transfer segment mov ax,ds:RH4_COUNT[bx] ; get sector count from request header mov P_COUNT[bp],ax ; save it locally for later mov ax,ds:RH4_SECTOR[bx] ; get low 16 bit of sector # sub dx,dx ; assume value is 16 bit only cmp ds:RH_LEN[bx],22 ; check if small request je setrw2 ; if so forget the rest cmp ds:RH_LEN[bx],24 ; check if large request jne setrw1 mov dx,ds:RH4_SECTOR+2[bx] ; yes, get 32-bit record number jmps setrw2 setrw1: cmp ds:RH_LEN[bx],30 jne setrw2 cmp ax,-1 ; magic number indicating it's jne setrw2 ; a 32-bit record number mov ax,ds:RH4_BIGSECTORLO[bx] mov dx,ds:RH4_BIGSECTORHI[bx] setrw2: pop ds mov cx,P_COUNT[bp] ; get requested count jcxz setrw3 ; invalid count dec cx ; CX = count - 1 cmp es:word ptr (UDSC_BPB+BPB_TOTSEC)[di],0 jne setrw4 ; skip if < 65536 sectors on disk add ax,cx adc dx,0 ; AX/DX = # of last sector for I/O jc setrw3 ; error if > 32 bits cmp dx,es:word ptr (UDSC_BPB+BPB_SIZE+2)[di] ja setrw3 ; skip if too large jb setrw5 ; O.K. if small enough cmp ax,es:word ptr (UDSC_BPB+BPB_SIZE)[di] jb setrw5 ; fail if too large setrw3: mov ax,RHS_ERROR+8 ; return "sector not found" stc ret setrw4: ; less than 65536 records add ax,cx ; compute end of transfer jc setrw3 ; skip if overflow cmp ax,es:UDSC_BPB+BPB_TOTSEC[di] jae setrw3 ; skip if too large setrw5: sub ax,cx sbb dx,0 ; add partition address for hard disk add ax,es:word ptr (UDSC_BPB+BPB_HIDDEN)[di] adc dx,es:word ptr (UDSC_BPB+BPB_HIDDEN+2)[di] push ax ; AX/DX = 32 bit starting record address push dx ; save starting record mov ax,es:UDSC_BPB+BPB_SPT[di] mul es:UDSC_BPB+BPB_HEADS[di]; get sectors per track * heads mov cx,ax ; CX = sectors per cylinder pop dx ; recover 32 bit start block pop ax div cx ; AX = cylinder #, DX = head/sec offset mov P_CYL[bp],ax ; save physical cylinder number xor ax,ax ; make remainder 32 bit so xchg ax,dx ; DX:AX = (head # * SPT) + sector # div es:UDSC_BPB+BPB_SPT[di] ; divide by sectors per track mov P_SECTOR[bp],dl ; DX = sector #, AX = head # mov P_HEAD[bp],al ; save physical sector/head for later clc ; tell them we like the parameters ret ; we've figured out starting address track_rw: ;-------- ; entry: P_CYL = cylinder for start of transfer ; P_HEAD = head # for start of transfer ; P_SECTOR = sector # for start of transfer ; P_COUNT = remaining sector count ; P_DMAOFF = transfer offset ; P_DMASEG = transfer segment ; ES:DI -> UDSC structure ; exit: CY = 0 if no error, P_COUNT = remaining sectors ; CY = 1 if error, AH = ROS error code call track_setup ; compute size of transfer if FASTSETTLE call new_settle ; set new head settle delay endif cmp P_DIRECT[bp],0 ; DMA boundary problem? jne trkrw10 ; no, direct transfer performed cmp P_ROSCMD[bp],ROS_READ je trkrw10 ; skip if not writing to disk pushx mov cx,SECSIZE/2 ; CX = # of word per sector push ds pop es ; ES:DI -> destination mov di,CG:local_buffer lds si,P_DMA[bp] ; DS:SI -> source rep movsw ; copy from deblocking buffer popx trkrw10: mov P_RETRY[bp],RETRY_MAX ; perform up to three retries trkrw20: ; loop back here for retries mov cx,P_CYL[bp] ; get cylinder # xchg cl,ch ; CH = bits 0..7, CL = bits 8..11 ror cl,1 ror cl,1 ; cylinder bits 8..9 in bits 6..7 mov dh,cl ; cylinder bits 10.11 in bits 0..1 and cl,11000000b ; isolate cylinder bits 8..9 add cl,P_SECTOR[bp] ; bits 0..5 are sector number inc cx ; make it one-relative for ROS ror dh,1 ror dh,1 ; cylinder bits 10..11 in bits 6..7 and dh,11000000b ; isolate cylinder bits 10..11 add dh,P_HEAD[bp] ; add physical head number mov dl,es:UDSC_RUNIT[di] ; get ROS unit # push es mov ax,ds mov es,ax mov bx,CG:local_buffer ; point at our local buffer cmp P_DIRECT[bp],0 ; DMA boundary problem? je trkrw30 ; no, direct transfer performed les bx,P_DMA[bp] ; ES:BX -> transfer address trkrw30: mov ax,P_MCNT[bp] ; AL = physical sector count mov ah,P_ROSCMD[bp] ; AH = ROS read command cmp ah,ROS_VERIFY ; write with verify? jne trkrw40 ; skip if ROS_READ or ROS_WRITE mov ah,ROS_WRITE ; else first perform normal write int_____DISK_INT ; call ROS to write to disk jc trkrw50 ; skip if any errors occurred mov ax,P_MCNT[bp] ; else get sector count mov ah,ROS_VERIFY ; verify disk sectors trkrw40: ; AH = function, AL = count int_____DISK_INT ; read/write/verify via ROM BIOS trkrw50: ; CY = 1, AH = error code pop es jnc trkrw70 ; skip if no errors occurred call disk_reset ; reset the hardware cmp ah,11h ; ECC corrected data? je trkrw60 ; first sector known to be good cmp ah,03h ; write protect error je trkrw_error ; don't recover, report to user dec P_RETRY[bp] ; count # of errors so far jnz trkrw20 ; retries done, declare it permanent trkrw_error: ; disk error occurred if FASTSETTLE call old_settle ; restore head settle delay endif stc ; CY = 1 indicates error, AH = code ret trkrw60: ; ECC error, only 1st sector OK mov P_MCNT[bp],1 ; say we have done one sector trkrw70: ; read/write/verify succeeded cmp P_DIRECT[bp],0 ; DMA boundary problem? jne trkrw80 ; no, direct transfer performed cmp P_ROSCMD[bp],ROS_READ jne trkrw80 ; skip if not reading from disk pushx mov cx,SECSIZE/2 ; CX = # of word per sector mov si,CG:local_buffer les di,P_DMA[bp] ; DS:SI -> source, ES:DI -> destination rep movsw ; copy from deblocking buffer popx trkrw80: mov ax,P_MCNT[bp] ; get physical transfer length sub P_COUNT[bp],ax ; subtract from total transfer length jz trkrw90 ; exit if none left add P_SECTOR[bp],al ; update current sector mov ah,SECSIZE/16 mul ah ; AX = paras to inc DMA address add P_DMASEG[bp],ax ; update DMA segment xor ax,ax mov al,P_SECTOR[bp] ; get current sector cmp ax,es:UDSC_BPB+BPB_SPT[di] jb trkrw90 ; skip if on same track mov P_SECTOR[bp],0 ; else start at beginning of new track inc P_HEAD[bp] ; move to the next head mov al,P_HEAD[bp] ; get current head cmp ax,es:UDSC_BPB+BPB_HEADS[di] jb trkrw90 ; did we go over end of cylinder? mov P_HEAD[bp],0 ; start with first head... inc P_CYL[bp] ; ... on the next cylinder trkrw90: if FASTSETTLE call old_settle ; restore head settle delay endif clc ; indicate no errors ret disk_reset: ;---------- ; entry: DL = ROS drive code push ax ; save the error status ; mov ah,ROS_RESET ; try a restore xor ax,ax int_____DISK_INT ; might sort things out pop ax ; restore error status ret track_setup: ; prepare for I/O on disk track ;----------- ; entry: P_CYL = cylinder for start of transfer ; P_HEAD = head # for start of transfer ; P_SECTOR = sector # for start of transfer ; P_COUNT = remaining sector count ; P_DMAOFF = transfer offset ; P_DMASEG = transfer segment ; ES:DI -> UDSC structure ; exit: P_DIRECT = 1 if no deblocking ; P_MCNT = # of sectors possible in one ROS call mov ax,P_DMASEG[bp] ; get transfer address cmp ax,DeblockSeg ; is this in high memory ? jae trksu20 ; then force through deblock buffer mov ax,P_COUNT[bp] ; assume we can transfer all mov P_MCNT[bp],ax ; that's requested this time mov P_DIRECT[bp],1 ; directly to destination test es:UDSC_RUNIT[di],80h ; is it a hard disk transfer ? jnz trksu30 ; yes, transfer the lot ; floppy transfer, break up into tracks mov dx,es:UDSC_BPB+BPB_SPT[di] ; DX = sectors per track sub dl,P_SECTOR[bp] ; subtract starting sector cmp dx,ax ; more than we want? jae trksu10 ; no, use this count mov P_MCNT[bp],dx ; set count for this pass trksu10: mov ax,P_DMASEG[bp] ; get transfer address mov cl,4 shl ax,cl ; get A4..A15 from segment add ax,P_DMAOFF[bp] ; combine with A0..A15 from offset not ax ; AX = # of bytes left in 64K bank sub dx,dx mov cx,SECSIZE div cx ; convert this to physical sectors cmp ax,P_MCNT[bp] ; capable of more than requested? jae trksu30 ; skip if we can do it all mov P_MCNT[bp],ax ; else update possible transfer length test ax,ax ; can we transfer anything at all? jnz trksu30 ; yes, perform the transfer trksu20: mov P_MCNT[bp],1 ; single sector transfer via buffer mov P_DIRECT[bp],0 ; if DIRECT = 0, deblocked transfer trksu30: ret if FASTSETTLE new_settle: ;---------- test es:UDSC_FLAGS[di],UDF_HARD ; fix head settle on floppies jnz new_settle9 cmp P_ROSCMD[bp],ROS_READ jne new_settle9 push ax pushx sub ax,ax mov ds,ax Assume DS:IVECT lds bx,i1eptr xchg al,9[bx] Assume DS:CGROUP popx mov P_SETTLE[bp],al pop ax new_settle9: ret old_settle: ;---------- test es:UDSC_FLAGS[di],UDF_HARD ; fix head settle on floppies jnz old_settle9 cmp P_ROSCMD[bp],ROS_READ jne old_settle9 pushx mov al,P_SETTLE[bp] sub bx,bx mov ds,bx Assume DS:IVECT lds bx,i1eptr mov 9[bx],al Assume DS:CGROUP popx old_settle9: ret endif dd_open: ; 13-device open ;------- call point_unit ; get unit descriptor inc es:UDSC_OPNCNT[di] ; increment open count sub ax,ax ret dd_close: ; 14-device close ;-------- call point_unit ; get unit descriptor dec es:UDSC_OPNCNT[di] ; decrement open count sub ax,ax ret dd_remchk: ; 15-removable media check ;--------- call point_unit ; get unit descriptor sub ax,ax ; assume floppy disk test es:UDSC_FLAGS[di],UDF_HARD jz remchk1 ; skip if it really is a floppy mov ax,RHS_BUSY ; else return "busy" for hard disk remchk1: ret dd_genioctl: ; 19-generic IOCTL ;----------- mov cx,es:RH19_CATEGORY[bx] ; get major & minor function xchg cl,ch ; swap them around call point_unit ; get unit descriptor cmp ch,8 ; is it the right major category? jne ioctl20 ; no, return an error or es:UDSC_FLAGS[di],UDF_UNSURE ; media unsure after IOCTL mov si,offset CGROUP:genioctlTable ioctl10: lods cs:byte ptr [si] ; get category mov ch,al ; keep in CH lods cs:word ptr [si] ; AX = function address cmp cl,ch ; is it the category we want ? je ioctl30 ; yes, go do it test ch,ch ; is it the end of the list ? jnz ioctl10 ; no, do another one ioctl20: mov ax,RHS_ERROR+3 ; "unknown command" ret ioctl30: jmp ax ; go do our routine genioctlTable label byte db RQ19_SET ; set device parameters dw offset CGROUP:ioctl_set db RQ19_GET ; get device parameters dw offset CGROUP:ioctl_get db RQ19_WRITE ; write track dw offset CGROUP:ioctl_write db RQ19_READ ; read track dw offset CGROUP:ioctl_read db RQ19_FORMAT ; format & verify track dw offset CGROUP:ioctl_format db RQ19_VERIFY ; verify track dw offset CGROUP:ioctl_verify db RQ19_GETMEDIA ; get media id dw offset CGROUP:ioctl_getmedia db RQ19_SETMEDIA ; set media id dw offset CGROUP:ioctl_setmedia db 0 ; terminate the list point_ioctl_packet: ;------------------ ; On Entry: ; None ; On Exit: ; DS:BX -> ioctl request packet ; All other regs preserved ; lds bx,REQUEST[bp] lds bx,ds:RH19_GENPB[bx] ; ES:BX -> request packet ret ioctl_get: ;--------- push ds call point_ioctl_packet ; DS:BX -> ioctl packet mov al,es:UDSC_TYPE[di] ; get drive type mov ds:1[bx],al ; return drive type (0/1/2/5/7) mov ax,es:UDSC_FLAGS[di] ; get device attributes and ax,UDF_HARD+UDF_CHGLINE ; isolate hard disk + change line bits mov ds:2[bx],ax ; return device attributes mov ax,es:UDSC_NCYL[di] ; get # of cylinders mov ds:4[bx],ax ; return # of cylinders sub ax,ax ; for now always say "default" mov ds:6[bx],al ; return media type test ds:byte ptr [bx],1 ; return default BPB? pop ds lea si,UDSC_DEVBPB[di] ; assume we want device BPB jz get1 ; skip if yes test es:UDSC_FLAGS[di],UDF_HARD jnz get1 ; BPB doesn't change for hard disks call ask_for_disk ; make sure we've got correct floppy call login_media ; determine floppy disk type jc get_err ; abort if can't login disk lea si,es:UDSC_BPB[di] ; get current BPB get1: push ds push es push di push es call point_ioctl_packet ; DS:BX -> ioctl packet push ds pop es lea di,7[bx] ; ES:DI -> BPB in parameter block pop ds ; DS:SI -> BPB to copy mov cx,UDSC_BPB_LENGTH rep movsb ; copy the BPB across to user pop di pop es pop ds xor ax,ax ; return success ret get_err: jmp xlat_error ; return error code ; ret ioctl_set: ; set device parameters ;--------- push ds push es call point_ioctl_packet ; DS:BX -> ioctl packet test ds:byte ptr [bx],2 ; ignore all but track layout? jnz set2 ; yes, skip BPB stuff mov al,ds:1[bx] ; get new drive type (0/1/2/5/7) mov es:UDSC_TYPE[di],al ; set drive type and es:UDSC_FLAGS[di],not (UDF_HARD+UDF_CHGLINE) mov ax,ds:2[bx] ; get new device attributes and ax,UDF_HARD+UDF_CHGLINE ; isolate hard disk + change line bits or es:UDSC_FLAGS[di],ax ; combine the settings mov ax,ds:4[bx] ; get new # of cylinders mov es:UDSC_NCYL[di],ax ; set # of cylinders lea ax,UDSC_BPB[di] ; AX -> media BPB in es:UDSC_ test ds:byte ptr [bx],1 ; fix BPB for "build BPB" call? jnz set1 ; skip if new media BPB only lea ax,UDSC_DEVBPB[di] ; AX -> device BPB in es:UDSC_ set1: lea si,7[bx] ; DS:SI -> new BPB from user xchg ax,di ; ES:DI -> BPB in es:UDSC_ mov cx,UDSC_BPB_LENGTH rep movsb ; copy BPB into UDSC as new default xchg ax,di ; ES:DI -> UDSC_ again set2: ; now set track layout lea si,BPB_LENGTH+7[bx] ; DS:SI -> new user layout mov es,cs:DataSegment mov di,CG:layout_table ; ES:DI -> BIOS layout table lodsw ; get sector count test ax,ax ; make sure this is good value jz set6 cmp ax,MAX_SPT ; make sure this is good value ja set6 ; so we don't overflow table xchg ax,cx ; CX = sector count set3: ; loop here for every sector inc di inc di lodsw ; get sector number stosb ; write sector number lodsw ; get sector size (0080, 0100, 0200, 0400) shl ax,1 ; double it (0100, 0200, 0400, 0800) set4: shr ah,1 ; halve the sector size until = 128 jc set5 ; we've shifted out bottom bit inc al ; count the # of bits jnz set4 ; (this should always jump) set5: stosb ; store LOG2 (sector size/128) loop set3 ; repeat for all sectors set6: pop es pop ds xor ax,ax ret ioctl_read: ;---------- mov P_ROSCMD[bp],ROS_READ ; read physical track jmps ioctl_rw_common ; use common code ioctl_write: ;----------- mov P_ROSCMD[bp],ROS_WRITE ; write physical track ; jmps ioctl_rw_common ; use common code ioctl_rw_common: call ask_for_disk ; make sure we've got correct floppy push ds call point_ioctl_packet ; DS:BX -> ioctl packet mov al,ds:5[bx] ; get logical sector (0..SPT-1) mov P_SECTOR[bp],al mov ax,ds:7[bx] ; get sector count mov P_COUNT[bp],ax mov ax,ds:9[bx] ; get transfer address mov P_DMAOFF[bp],ax mov ax,ds:11[bx] mov P_DMASEG[bp],ax mov ax,ds:1[bx] ; get head number mov P_HEAD[bp],al mov ax,ds:3[bx] ; get cylinder number mov P_CYL[bp],ax pop ds rw_loop: call track_rw ; read as much as possible on track jc rw_err ; return if physical disk error cmp P_COUNT[bp],0 ; test if any more stuff to read jne rw_loop ; yes, loop back for more sub ax,ax ; all done, no error encountered ret ; return O.K. code rw_err: jmp xlat_error ; translate ROS code to DOS error ; ret ioctl_verify: ;------------ ioctl_format: ;------------ call ask_for_disk ; make sure we've got correct floppy mov P_RETRY[bp],RETRY_MAX ; perform up to three retries format_retry: call set_format ; attempt data rate setup push ds call point_ioctl_packet ; DS:BX -> ioctl packet test ds:byte ptr [bx],1 ; are we testing parameters only ? jz format10 mov ds:[bx],al ; return AL pop ds xor ax,ax ; we succeeded ret format10: mov ax,es:UDSC_BPB+BPB_SPT[di] test ds:byte ptr [bx],2 ; is it undocumented "do 2 tracks" bit? jz format20 add ax,ax ; yes, double the count format20: mov P_COUNT[bp],ax ; save it locally for later mov dh,ds:1[bx] ; get head # mov cx,ds:3[bx] ; get cylinder # ror ch,1 ror ch,1 xchg cl,ch or cl,1 ; start with sector 1 mov dl,es:UDSC_RUNIT[di] ; get ROS drive # lds bx,REQUEST[bp] ; DS:BX -> Request Header mov bx,ds:RH19_CATEGORY[bx] ; get major & minor function pop ds push es xor ax,ax mov es,ax mov ax,new_int1e_off ; point floppy paramters at local xchg ax,es:[4*1Eh] mov orig_int1e_off,ax ; save old value mov ax,new_int1e_seg xchg ax,es:[4*1Eh+2] mov orig_int1e_seg,ax pop es format30: cmp bh,RQ19_FORMAT ; skip if verify only jne format40 test es:UDSC_FLAGS[di],UDF_HARD jnz format40 ; hard disks are always verify mov ax,P_COUNT[bp] mov ah,ROS_FORMAT push es push bx push ds pop es mov bx,CG:layout_table ; ES:BX -> parameter table int_____DISK_INT pop bx pop es jc format50 format40: ; no error on format, try verify mov ax,P_COUNT[bp] mov ah,ROS_VERIFY push es push bx xor bx,bx mov es,bx int_____DISK_INT pop bx pop es jc format50 xor ax,ax ; return success format50: push es push di push ax mov ax,0 mov es,ax mov di,78h mov ax,orig_int1e_off stosw mov ax,orig_int1e_seg stosw pop ax pop di pop es jnc format60 ; if no error's just exit call xlat_error ; translate to DOS error dec P_RETRY[bp] ; any more retries ? jz format60 ; no, just exit with error ; mov ah,ROS_RESET xor ax,ax int_____DISK_INT ; reset the drive jmp format_retry ; now give it another go format60: ret ; The following table indicates which combinations of drive ; types, sectors per track and tracks per disk are O.K. and ; which value in AL is required for those combinations for ; INT 13h, AH = 17h ("set DASD type for format"). ; +---------------------- 0 = 360Kb, 1 = 1.2Mb, 2 = 720Kb ; | +------------------ # of sectors/track (9, 15, 18) ; | | +--------------- # of tracks per disk (40 or 80) ; | | | +----------- 1 = 360 Kb in 360 Kb ; | | | | 2 = 360 Kb in 1.2 Mb ; | | | | 3 = 1.2 Mb in 1.2 Mb ; | | | | 4 = 720 Kb in 720 Kb ; | | | | +-------- gap length for format ; | | | | | ; V V V V V ok_fmt_table db 0, 9, 40, 1, 50h ; 360 Kb db 1, 9, 40, 2, 50h ; 360 Kb in 1.2 Mb db 1, 15, 80, 3, 54h ; 1.2 Mb in 1.2 Mb db 2, 9, 80, 4, 50h ; 720 Kb in 720 Kb db -1 ; end of table set_format: ;---------- ; On Entry: ; ES:DI -> UDSC_ ; On Exit: ; AL = 0 on success, else value to return in parameter block ; ES:DI preserved ; push ds call point_ioctl_packet ; DS:BX -> ioctl packet mov dh,ds:1[bx] ; get the head number mov cx,ds:3[bx] ; get the cylinder number pop ds mov si,CG:layout_table ; SI -> track layout table mov ax,MAX_SPT ; AX = # of sectors per track set_format10: mov 0[si],cl ; set cylinder number mov 1[si],dh ; set head number add si,4 ; next sector entry dec ax ; count down # of sectors jnz set_format10 ; repeat until all done call get_ncyl ; return # of tracks dec ax ; AX = max. cylinder # ror ah,1 ror ah,1 ; move bits 8,9 into 6,7 xchg al,ah mov cx,es:UDSC_BPB+BPB_SPT[di] ; get desired sectors/track or cx,ax ; CL, CH = max. cylinder/max. sector # cmp cx,2708h ; check for 40 track, 8 sectors/track jne set_format20 ; we convert 160, 320 to 180, 360 inc cx ; make it 9 sectors per track set_format20: mov dl,es:UDSC_RUNIT[di] ; get ROS unit number pushx mov ah,ROS_SETMEDIA ; set type for format int_____DISK_INT ; check if combination is legal mov new_int1e_off,di mov new_int1e_seg,es ; ES:DI -> new parameters if legal popx jc set_format40 ; did we succeed ? set_format30: xor ax,ax ; success, return no errors ret set_format40: ; ROM BIOS has given an error, if the function isn't supported drop ; thru' and try the older method's ; mov al,2 ; assume ROS doesn't support it cmp ah,0Ch ; media combination not supported ? je set_format80 ; return AL=2 inc ax ; AL = 3 cmp ah,80h ; drive not ready ? je set_format80 ; return AL=3 ; Lets look for a match in our tables call get_ncyl ; AX = number of cylinders mov cx,es:UDSC_BPB+BPB_SPT[di] ; CL = sectors per track mov ch,al ; CH = tracks per disk cmp cx,2808h ; 40 tracks, 8 sectors? jne set_format50 inc cx ; force it to 9 sectors/track set_format50: mov si,CG:ok_fmt_table-4 set_format60: add si,4 ; next table entry lods cs:byte ptr [si] ; get drive type cmp al,0FFh ; end of device/media list? je set_format70 ; yes, can't handle this combination cmp al,es:UDSC_TYPE[di] ; does the drive type match? jne set_format60 ; try next one if wrong drive cmp cx,cs:[si] ; do tracks/sectors match? jne set_format60 ; no, try next one mov parms_spt,cl ; set sectors/track mov al,cs:3[si] ; get required gap length from table mov parms_gpl,al ; set gap length for format mov ax,CG:local_parms mov new_int1e_off,ax ; use local parameters for formatting mov new_int1e_seg,ds ; set new interrupt vector address mov dl,es:UDSC_RUNIT[di] mov al,cs:2[si] ; get media/drive combination mov ah,ROS_SETTYPE ; set the drive type int_____DISK_INT jnc set_format30 ; return if no errors cmp es:UDSC_TYPE[di],0 ; is this a 360 K drive? je set_format30 ; go ahead, might be old ROS cmp es:UDSC_TYPE[di],2 ; is this a 720 K drive? je set_format30 ; go ahead, might be old ROS set_format70: mov al,1 ; return not supported set_format80: ret get_ncyl: ;-------- mov ax,es:UDSC_BPB+BPB_TOTSEC[di] xor dx,dx ; get sectors on disk test ax,ax ; zero means we use 32 bit value jnz get_ncyl10 mov ax,es:word ptr (UDSC_BPB+BPB_SIZE)[di] mov dx,es:word ptr (UDSC_BPB+BPB_SIZE+2)[di] get_ncyl10: div es:UDSC_BPB+BPB_SPT[di] ; AX = # of cylinders * heads call get_ncyl20 ; round up div es:UDSC_BPB+BPB_HEADS[di]; AX = # of cylinders get_ncyl20: test dx,dx ; do we have overflow ? jz get_ncyl30 inc ax ; round up xor dx,dx ; make it a 32 bit value get_ncyl30: ret ioctl_getmedia: ;-------------- mov P_ROSCMD[bp],ROS_READ ; read from floppy/hard disk call rw_media ; read the boot sector jc getmedia10 push es push di push ds call point_ioctl_packet ; DS:BX -> ioctl packet push ds pop es lea di,2[bx] ; ES:DI -> skip info word pop ds ; DS:SI -> boot sector media id mov cx,4+11+8 rep movsb ; copy the boot sector image pop di pop es xor ax,ax getmedia10: ret ioctl_setmedia: ;-------------- mov P_ROSCMD[bp],ROS_READ ; read from floppy/hard disk call rw_media ; read the boot sector jc setmedia10 push ds push si push es push di push ds push si call point_ioctl_packet ; DS:BX -> ioctl packet lea si,2[bx] ; DS:SI -> skip info word pop di pop es ; ES:DI -> boot sector image mov cx,4+11+8 rep movsb ; update the boot sector image pop di pop es pop si pop ds mov P_ROSCMD[bp],ROS_WRITE ; write to floppy/hard disk jmp rw_media ; write the boot sector setmedia10: ret rw_media: ;-------- ; On Entry: ; ES:DI -> UDSC ; On Exit: ; ES:DI preserved ; CY clear, SI -> boot record info ; CY set on error, AX = error code ; ; setup parameters to read/write boot sector to/from local buffer ; call ask_for_disk ; make sure we've got correct floppy mov P_DMAOFF[bp],CG:local_buffer mov P_DMASEG[bp],ds ; set transfer address mov P_COUNT[bp],1 ; read 1 sector mov ax,es:UDSC_BPB+BPB_SPT[di] mul es:UDSC_BPB+BPB_HEADS[di]; get sectors per track * heads xchg ax,cx ; CX = sectors per cylinder mov ax,es:word ptr (UDSC_BPB+BPB_HIDDEN)[di] mov dx,es:word ptr (UDSC_BPB+BPB_HIDDEN+2)[di] div cx ; AX = cylinder #, DX = head/sec offset mov P_CYL[bp],ax ; save physical cylinder number xor ax,ax ; make remainder 32 bit so xchg ax,dx ; DX:AX = (head # * SPT) + sector # div es:UDSC_BPB+BPB_SPT[di] ; divide by sectors per track mov P_SECTOR[bp],dl ; DX = sector #, AX = head # mov P_HEAD[bp],al ; save physical sector/head for later call rw_loop ; read the boot sector jc rw_media20 cmp local_buffer+11+BPB_FATID,0F0h jb rw_media10 mov si,offset CGROUP:local_buffer+UDSC_BPB_LENGTH+11+2 lodsb ; get extended boot sub al,29h ; do we have an extended boot ? je rw_media20 ; no, well we can't write a new one rw_media10: mov ax,RHS_ERROR+3 ; "unknown command" rw_media20: ret dd_getdev: ; 23-get logical drive ;--------- ; get logical drive that corresponds to the physical drive call point_unit ; get unit descriptor call get_owner ; DL = owning drive (zero not owned) jmps dd_setdev10 ; return the owner dd_setdev: ; 24-set logical drive ;--------- ; set logical drive that corresponds to the physical drive ; call point_unit ; get unit descriptor call set_owner ; set new owner dd_setdev10: les bx,REQUEST[bp] mov es:RH_UNIT[bx],dl ; return current logical drive xor ax,ax ret get_owner: ;--------- ; On Entry: ; ES:DI -> UDSC_ ; On Exit: ; DL = owning drive (zero = no owner) ; xor dx,dx ; assume one unit per physical drive mov ax,es:UDSC_FLAGS[di] test ax,UDF_HARD jnz get_owner40 test ax,UDF_VFLOPPY jz get_owner40 push ds mov ds,dx ; DS -> low memory Assume DS:IVECT mov dl,dual_byte pop ds Assume DS:CGROUP mov al,es:UDSC_RUNIT[di] ; lets look for this ROS drive test al,al ; is it physical unit zero ? jz get_owner30 ; yes, return dual_byte push ds ; no, search our internal info lds si,udsc_root Assume DS:Nothing get_owner10: cmp al,ds:UDSC_RUNIT[si] ; do we use the same drive ? jne get_owner20 test ds:UDSC_FLAGS[si],UDF_OWNER jz get_owner20 ; do we own it ? mov dl,ds:UDSC_DRIVE[si] ; get the logical drive owner get_owner20: lds si,ds:UDSC_NEXT[si] cmp si,0FFFFh ; try the next drive jne get_owner10 pop ds Assume DS:CGROUP get_owner30: inc dx ; make drive one based get_owner40: ret set_owner: ;--------- ; On Entry: ; ES:DI -> UDSC_ ; On Exit: ; ES:DI preserved ; DL = owning drive (zero = no owner) ; xor dx,dx ; assume one unit per physical drive mov ax,es:UDSC_FLAGS[di] test ax,UDF_HARD jnz set_owner40 test ax,UDF_VFLOPPY jz set_owner40 mov al,es:UDSC_DRIVE[di] mov ah,es:UDSC_RUNIT[di] ; get ROS unit test ah,ah ; is it unit zero ? jnz set_owner10 push ds mov ds,dx ; DS -> low memory Assume DS:IVECT mov dual_byte,al ; set dual drive support byte pop ds Assume DS:CGROUP set_owner10: push ds lds si,udsc_root Assume DS:Nothing set_owner20: cmp ah,ds:UDSC_RUNIT[si] ; does this unit use the same drive ? jne set_owner30 or ds:UDSC_FLAGS[si],UDF_UNSURE+UDF_OWNER cmp al,ds:UDSC_DRIVE[di] je set_owner30 and ds:UDSC_FLAGS[si],not UDF_OWNER set_owner30: lds si,ds:UDSC_NEXT[si] cmp si,0FFFFh ; end of the line ? jne set_owner20 pop ds Assume DS:CGROUP xchg ax,dx ; DL = owning drive inc dx ; make it one based set_owner40: ret ask_for_disk: ; make sure the right disk is in the floppy drive ;------------ call get_owner ; DL = owning drive dec dx ; make DL zero based js askfdsk30 ; stop if not a logical drive mov dh,es:UDSC_DRIVE[di] ; DH = new drive, DL = old drive cmp dl,dh ; do we own the drive ? je askfdsk30 ; yes, stop now push dx ; save for broadcast mov dl,dh ; new owner in DL call set_owner ; we are now the owner push es push di push cs call FullScreen pop di pop es pop dx mov ax,4A00h ; should we prompt ? xor cx,cx int 2Fh ; lets ask inc cx ; CX = FFFF ? jcxz askfdsk30 ; then skip prompt ifdef JAPAN mov ax,5001h ; get adaptor mode int VIDEO_INT ; .. cmp bx,81 ; japanese mode ? mov si,CG:disk_msgA_jpn ; get message to print for Japanese je askfdsk10 ; yes endif mov si,CG:disk_msgA ; get message to print askfdsk10: call WriteASCIIZ ; output the string mov al,es:UDSC_DRIVE[di] ; get drive letter for new drive add al,'A' dec si ; point to NUL call WriteNext ; output char, stop at NUL ifdef JAPAN mov ax,5001h ; get adaptor mode int VIDEO_INT ; .. cmp bx,81 ; japanese mode ? mov si,CG:disk_msgB_jpn ; get message to print for Japanese je askfdsk20 ; yes endif mov si,CG:disk_msgB ; get message to print askfdsk20: call WriteASCIIZ ; output the string mov ah,0 ; wait for any key to be pressed int KEYBOARD_INT ; read one key from keyboard askfdsk30: ret ; we've got the right drive WriteNext: int 29h ; output via fastconsole entry WriteASCIIZ: lods cs:byte ptr [si] ; get next char test al,al jnz WriteNext ; stop at NUL ret FullScreen: xor di,di mov es,di mov ax,1684h ; get the entry point mov bx,21 ; for DOSMGR int 2Fh mov bx,es or bx,di ; anyone there ? jz FullScreen10 mov ax,1 ; yes, go full screen please push es ; fake a JMPF to ES:DI push di FullScreen10: retf driver endp RCODE ends ; end of device driver code page ICODE segment 'ICODE' ; initialization code Assume CS:CGROUP, DS:CGROUP, ES:Nothing, SS:Nothing dd_init: ; 0-initialize driver ;------- call hard_init ; setup hard disk units call floppy_init ; setup floppy units les bx,REQUEST[bp] mov al,nunits ; get # of units installed mov es:RH0_NUNITS[bx],al ; return value to the BDOS mov NumDiskUnits,al ; also set it in device header mov ax,endbios ; get pointer to last resident byte mov es:RH0_RESIDENT[bx],ax ; set end of device driver mov es:RH0_RESIDENT+2[bx],ds mov ax,CG:bpbtbl mov es:RH0_BPBOFF[bx],ax ; set BPB table array mov es:RH0_BPBSEG[bx],ds sub ax,ax ; initialization succeeded ret ; (BIOS init always does...) floppy_init: ;----------- mov nunits,0 ; floppies start at drive A: mov ah,ROS_RESET ; reset the disk system xor dx,dx ; for NEAT hard disk boot bug int_____DISK_INT int EQUIPMENT_INT ; determine equipment status mov cl,6 shr ax,cl ; shift down floppy bits and ax,03h ; mask for floppy inc ax ; correct 0 based code mov nfloppy,al cmp al,1 ; if there is only one floppy jne equip_check_des ; then use 2 designators inc ax ; this fakes a B: drive equip_check_des: mov cx,ax ; CX = # of units to set up xor dx,dx ; DL = physical drive equip_loop: push cx call new_unit ; ES:DI -> UDSC mov es:UDSC_RUNIT[di],dl ; set physical drive (ROS code) call floppy_type ; determine type, build default BPB cmp nfloppy,1 ; do we only have single drive? je equip_single ; yes, use same physical drive for all inc dx ; else use new drive for each unit equip_single: ; we only have one physical drive call add_unit ; add ES:DI to list of UDSC_'s pop cx loop equip_loop ; repeat for all logical floppies pushx push ds ; DS -> i13_trap segment mov di,ds mov es,di sub si,si mov ds,si lds si,78h[si] mov di,CG:local_parms ; copy parameters to template mov cx,11 rep movsb pop es ; now ES -> i13_trap segment Assume ES:CGROUP sub ax,ax mov ds,ax ; DS -> interrupt vectors Assume DS:IVECT mov ax,CG:Int2FTrap ; hook Int 2F mov i2Foff,ax mov i2Fseg,es mov ax,CG:Int13Trap ; hook Int 13 xchg ax,i13off mov es:i13off_save,ax mov ax,es xchg ax,i13seg mov es:i13seg_save,ax mov di,500h ; dual drive byte & friends live here mov cx,20h/2 ; zero some bytes at 50h:0 sub ax,ax ; get a quick zero mov es,ax ; ES:DI -> 0:500h rep stosw ; setup dual drive byte & friends Assume DS:CGROUP, ES:Nothing popx ret floppy_type: ;----------- ; entry: DI -> unit descriptor mov UDSC_TYPE[di],0 ; assume 360K 5.25" floppy mov UDSC_NCYL[di],40 ; 40 tracks only mov ah,ROS_GETTYPE ; "Read DASD type" int_____DISK_INT ; find out if disk change line available jc equip_no_chgline ; skip if function not supported cmp ah,2 ; floppy with disk change line? jne equip_no_chgline ; no, must be old 360K or es:UDSC_FLAGS[di],UDF_CHGLINE mov es:UDSC_TYPE[di],1 ; assume 1.2Mb floppy mov es:UDSC_NCYL[di],80 ; 80 tracks equip_no_chgline: pushx ; save our registers mov ah,ROS_PARAM ; read drive parameters int_____DISK_INT ; find out floppy type popx jc equip_no_type ; skip if PC,XT,jr,AT before 10 Jan 84 dec bx ; make values 0 based jns equip_type ; (CMOS invalid - type = 0) xor bx,bx ; assume 360K cmp ch,4fh ; is it 80 track ? jne equip_no_type ; if not forget it mov bl,1 ; BL = 1 (ie. 1.2M) cmp cl,15 ; 15 spt ? je equip_type inc bx ; BL = 2 (ie. 720k) cmp cl,9 ; 9 spt ? je equip_type inc bx ; BL = 3 (ie. 1.4M) cmp cl,18 ; 18 spt ? je equip_type inc bx inc bx ; BL = 5 (ie. 2.8M) cmp cl,36 ; 36 spt ? jne equip_no_type ; don't recognise anything equip_type: cmp bl,3 ; is it 1.44 Mb 3.5" type? jb equip_type_ok ; skip if 360K, 1.2Mb, 720K (0, 1, 2) mov bl,7 ; use reserved "Other" type je equip_type_ok inc bx ; else make it 2.88 Mb type 9 inc bx equip_type_ok: mov es:UDSC_TYPE[di],bl ; set the default drive type for format equip_no_type: mov al,es:UDSC_TYPE[di] ; AL = 0, 1, 2 or 7 (360/1.2/720/1.44) cbw ; make it a word xchg ax,si ; SI = drive type shl si,1 ; SI = drive type * 2 mov si,bpbs[si] ; get default BPB for drive cmp si,CG:bpb360 ; is this is a 360 K drive? jne equip_360 ; skip if any other type mov bpbtbl[bx],CG:bpb720 ; use larger default BPB equip_360: mov cx,UDSC_BPB_LENGTH ; CX = size of BPB pushx lea di,es:UDSC_BPB[di] mov ax,ds mov es,ax ; ES = DS rep movsb ; make default BPB current BPB in UDSC popx pushx lea di,es:UDSC_DEVBPB[di] rep movsb ; copy default BPB device BPB in UDSC popx ret page LOG_PRIM equ 01h ; log in primary partitions LOG_EXTD equ 02h ; log in extended partitions log_flag dw LOG_PRIM ; scan for primary only initially hard_init: ; setup all hard disk units ;--------- ; mov log_flag,LOG_PRIM ; log in primary only initially call hardi0 ; C: & D: mov log_flag,LOG_EXTD ; log in extended only ; call hardi0 ; ret hardi0: mov ah,ROS_PARAM ; get hard disk parameters mov dl,80h int_____DISK_INT ; get # of hard disks we have jc hardi9 ; skip if hard disks not supported test dl,dl ; test if any hard disks found jz hardi9 ; skip if there weren't any mov al,dl cbw xchg ax,cx ; CX = # of hard disks mov dl,80h ; start with first hard disk hardi1: pushx ; save drive count, physical drive call login_hdisk ; find all partitions on hard disk popx ; restore physical drive, drive count inc dx ; next physical hard disk loop hardi1 ; next physical hard disk hardi9: ; all hard disks done ret login_hdisk: ; find all partitions on a hard disk ;----------- ; entry: DL = 80h, 81h for 1st/2nd hard disk push log_flag ; save state for next drive mov p_unit,dl ; save physical drive mov cx,0001h ; track 0, sector 1 mov dh,0 ; partition tables start on head 0 log_h1: mov dl,p_unit ; get physical unit call login_read_dx jnc log_h1a jmp log_h9 ; give up if disk error log_h1a: push cx push dx mov ah,ROS_PARAM int_____DISK_INT ; return disk drive parameters inc dh ; DH = number of heads mov nhead,dh ; set # of heads on drive and cl,3Fh ; isolate sector count mov nsect,cl ; set sectors per track pop dx pop cx ;; cmp local_id,0AA55h ;; jne log_h9 ; give up if not initialized test log_flag,LOG_PRIM ; scanning for primary? jz log_h5 ; no, ignore all primary partitions mov si,CG:local_pt ; point to partition table log_h2: ;** SECURE PARTITIONS ** mov al,init_runit test al,al ; booting from a hard disk ? ;** SECURE PARTITIONS ** mov al,4[si] ; get system ID ;** SECURE PARTITIONS ** jns log_h2a ; booting from a hard disk ? mov ah,al ; yes, allow secure partitions and ah,0F0h cmp ah,SEC_ID je log_h02 cmp ah,SEC_ID2 jne log_h2a log_h02: sub al,ah ; turn into a sensible partition ID log_h2a: ;** SECURE PARTITIONS ** cmp al,DOS20_ID ; is this a DOS 2.x partition? je log_h3 ; yes, try to log it in cmp al,DOS30_ID ; is this a DOS 3.0/3.1/3.2 partition? je log_h3 ; yes, try to log it in cmp al,DOS331_ID ; is this a DOS 3.31/4.0 partition? jne log_h4 ; skip if not a good partition log_h3: push si ; save partition table index pushx ; save partition table address call login_primary ; login primary partition popx ; get partition table address call login_read_dx ; re-read partition table pop si ; get partition table index jc log_h9 ; give up if error log_h4: add si,16 ; next partition table entry cmp si,CG:local_id ; all partitions checked? jb log_h2 ; loop back if more log_h5: ; primary partitions done test log_flag,LOG_EXTD ; scanning for extended? jz log_h9 ; skip if no extended scan or log_flag,LOG_PRIM ; scan for both types now mov si,CG:local_pt ; SI -> partition table ; RG-01 log_h6: ;** SECURE PARTITIONS ** mov al,init_runit test al,al ; booting from a hard disk ? ;** SECURE PARTITIONS ** mov al,4[si] ; get system ID ;** SECURE PARTITIONS ** jns log_h6a ; booting from a hard disk ? mov ah,al ; yes, allow secure partitions and ah,0F0h cmp ah,SEC_ID je log_sec2 cmp ah,SEC_ID2 jne log_h6a log_sec2: sub al,ah log_h6a: ;** SECURE PARTITIONS ** cmp al,DOSEX_ID ; DOS 3.3 extended partition found? jne log_h7 log_h6b: mov dh,1[si] ; get head # for next table mov cx,2[si] ; get cylinder, sector for next table jmp log_h1 ; read & scan next partition table log_h7: ; entry not an extended partition add si,16 ; next partition table entry cmp si,CG:local_buffer+IDOFF; all partitions checked? jb log_h6 ; loop back if more log_h9: ; drive login done pop log_flag ; restore state for next drive ret login_primary: ;------------- ; entry: SI -> partition table entry mov ax,12[si] ; get size of partition (low) mov part_size,ax mov ax,14[si] ; get size of partition (high) mov part_size+2,ax mov cl,2 mov bx,5[si] ; get last head/sector and bx,1100000011000000b ; isolate cylinder bits 10..11,8..9 rol bl,cl ; bits 10..11 from head into position or bh,bl ; or in bits 8..9 rol bh,cl ; bits 8..11 into place mov bl,7[si] ; get cylinder bits 0..7 mov dh,1[si] ; get head of DOS partition mov cx,2[si] ; get cylinder, sector of DOS partition pushx call login_read_dx ; try to read the partition boot popx jc login_p9 ; skip if partition not readable ; CX, DX = disk addr of 1st sector ; SI -> boot sector ; PART_SIZE = 32 bit partition address cmp nunits,MAXPART ; do we already have the maximum? jb log_p0 ; skip if space for more units login_p9: ret ; else ignore this partition log_p0: call new_unit ; ES:DI -> new UDSC mov es:UDSC_FLAGS[di],UDF_HARD mov es:UDSC_RUNIT[di],dl ; set physical drive (ROS code) mov es:UDSC_TYPE[di],5 ; set type = hard disk mov al,dh ; copy head byte and al,11000000b ; cylinder # bits 10..11 are in 6..7 rol al,1 ; shift bits to bottom of word rol al,1 mov ah,cl ; cylinder # bits 8..9 are in 6..7 and ah,11000000b ; strip off non-cylinder # bits or ah,al ; combine the bits rol ah,1 ; shift the bits into place rol ah,1 mov al,ch ; cylinder # bits 0..7 sub bx,ax ; bx = # cylinders inc bx ; make it inclusive mov es:UDSC_NCYL[di],bx ; and save it push ax ; save # CYLINDERS mov al,nsect and dh,00111111b ; DH = head offset mul dh ; AX = HEAD_OFF * NSECT xchg ax,bx ; keep in BX mov al,nsect mul nhead ; AX = HEADS * NSECT pop dx ; recover # CYLINDERS mul dx ; DX:AX = CYLINDERS * HEADS * NSECT add ax,bx adc dx,0 ; DX:AX = (CYL*HEADS + HEAD_OFF)*NSECT and cx,00111111b ; isolate bottom 6 bits (sector #) dec cx ; sector numbers are one-relative add ax,cx ; add in non-partition sectors adc dx,0 ; (usually 2.x partition table) lea bx,UDSC_BPB[di] ; BX -> BPB to build add si,11 ; skip JMP + OEM name in boot sector mov es:word ptr BPB_HIDDEN[bx],ax ; set the partition address mov word ptr BPB_HIDDEN+2[bx],dx ; (32 bit sector offset) mov ax,part_size mov dx,part_size+2 mov word ptr BPB_SIZE[bx],ax ; set partition size in sectors mov word ptr BPB_SIZE+2[bx],dx mov BPB_TOTSEC[bx],ax ; set partition size for < 32 Mb ; we'll zero this later if > 32 Mb push es push di push ax push dx push si call hd_bpb ; build BPB from scratch pop si pop dx pop ax pop di pop es cmp byte ptr -11[si],0E9h ; look for a jmp jz log_p1a cmp word ptr -11[si],0EB90h ; look for a nop!jmps jz log_p1a cmp byte ptr -11[si],0EBh ; look for a jmps jnz log_p1 ; at the start of the boot sector. cmp byte ptr -9[si],90h ; EJH 7-1-91 jnz log_p1 log_p1a: test BPB_SECSIZ[si],SECSIZE-1; not a multiple of 512 byte? jnz log_p1 cmp BPB_FATID[si],0F8h ; is this a good hard disk? jne log_p1 cmp BPB_NFATS[si],2 ; too many FATs? ja log_p1 cmp BPB_NFATS[si],1 ; no FATs at all? jae log_p2 ; continue if BPB is valid ; elsa build new BPB log_p1: ; any of the above: BPB invalid ; (propably FDISKed, not FORMATted yet) jmps log_p9 log_p2: ; valid BPB for partition, AX/DX = size push ax mov al,BPB_ALLOCSIZ[si] ; copy a few parameters from the mov BPB_ALLOCSIZ[bx],al ; Boot Sector BPB to our new BPB mov ax,BPB_DIRMAX[si] ; EJH 7-1-91 mov BPB_DIRMAX[bx],ax mov ax,BPB_FATSEC[si] mov BPB_FATSEC[bx],ax mov ax,BPB_SECSIZ[si] mov BPB_SECSIZ[bx],ax mov ax,BPB_FATADD[si] mov BPB_FATADD[bx],ax mov al,BPB_NFATS[si] mov BPB_NFATS[bx],al pop ax cmp BPB_TOTSEC[bx],0 ; is it an 32 bit sector partition ? jne log_p3 ; no, carry on test dx,dx ; would it fit in 16 bit sector sizes ? jnz log_p3 ; yes, then make BPB_TOTSEC mov BPB_TOTSEC[bx],ax ; a valid 16 bit value too log_p3: ; valid BPB for partition, AX/DX = size cmp BPB_SECSIZ[bx],SECSIZE jbe log_p9 ; skip if no large sectors shr BPB_SECSIZ[bx],1 ; halve the sector size shl BPB_ALLOCSIZ[bx],1 ; double the cluster size shl BPB_FATSEC[bx],1 ; double the FAT size shl BPB_FATADD[bx],1 ; double the FAT address shl BPB_TOTSEC[bx],1 ; double # of sectors jnc log_p3 ; skip if still < 65536 sectors mov BPB_TOTSEC[bx],0 ; else indicate large partition jmps log_p3 ; try again ; we've adjusted the sector size log_p9: pushx push es pop ds lea si,UDSC_BPB[di] ; DS:SI -> new BPB lea di,UDSC_DEVBPB[di] ; ES:DI -> fixed BPB mov cx,UDSC_BPB_LENGTH rep movsb ; make this the fixed BPB popx call add_unit ; register this DDSC_ inc nhard ; yet another hard disk ret hd_bpb: ;------ mov BPB_SECSIZ[bx],SECSIZE ; set standard sector size mov BPB_FATADD[bx],1 ; one reserved (boot) sector mov BPB_NFATS[bx],2 ; two FAT copies mov BPB_DIRMAX[bx],512 ; assume 512 entry root directory ; BPB_TOTSEC set up already mov BPB_FATID[bx],0F8h ; standard hard disk ID mov al,nsect mov ah,0 mov BPB_SPT[bx],ax ; set sectors/track mov al,nhead mov BPB_HEADS[bx],ax ; set # of heads ; determine FAT size: mov BPB_ALLOCSIZ[bx],2*2 ; assume 2 K clusters mov ax,word ptr BPB_SIZE[bx]; AX/DX = 32 bit sector count mov dx,word ptr BPB_SIZE+2[bx] test dx,dx ; have we got huge partition (type 6)? jnz hd_bpb10 ; yes, it's 16-bit cmp ax,7FCEh ; more than 16 Mb? jae hd_bpb20 ; yes, use 16 bit FAT mov cx,4*2 ; else we've got old 12-bit FAT mov BPB_ALLOCSIZ[bx],cl ; we use 4 K clusters add ax,cx ; adjust DX:AX for rounding dec ax ; when we work out num clusters div cx ; AX = # of clusters mov cx,ax add ax,ax ; * 2 add ax,cx ; * 3 shr ax,1 ; AX = num clus * 3/2 = bytes adc ax,512-1 ; allow for rounding xor dx,dx mov cx,512 div cx ; AX = # fat sectors mov BPB_FATSEC[bx],ax ; remember FAT size ret hd_bpb10: mov BPB_TOTSEC[bx],0 ; zero this if BPB_SIZE is required cmp dx,2 ; less than 2*65536 sectors (64 Mb)? jb hd_bpb20 ; yes, leave cluster size the same mov BPB_ALLOCSIZ[bx],4*2 ; use 4 K clusters if 64-128 Mb cmp dx,4 ; less than 4*65536 sectors (128 Mb)? jb hd_bpb20 ; yes, leave cluster size the same mov BPB_ALLOCSIZ[bx],8*2 ; use 8 K clusters if 128-512 Mb cmp dx,16 ; less than 16*65536 sectors (512 Mb)? jb hd_bpb20 ; yes, leave cluster size the same mov BPB_ALLOCSIZ[bx],16*2 ; use 16 K clusters if 512-1024 Mb cmp dx,32 ; less than 32*65536 sectors (1 Gb)? jb hd_bpb20 ; yes, leave cluster size the same mov BPB_ALLOCSIZ[bx],32*2 ; use 32 K clusters if 1-2 Gb hd_bpb20: ; cluster size determined sub ax,1+(512*32/SECSIZE) ; subtract reserved+root directory sbb dx,0 ; (note: 32 bytes per entry) xor cx,cx mov ch,BPB_ALLOCSIZ[bx] ; CX = (256 * # of clusters on drive) dec cx add ax,cx ; add in for rounding error adc dx,0 inc cx div cx ; AX = # of fat sectors mov BPB_FATSEC[bx],ax ; remember FAT size mov es:UDSC_FSTYPE+4[di],'6'; change "FAT12" to "FAT16" ret new_unit: push ds push ax push bx push cx push dx push si mov es,cs:DataSegment mov di,endbios ; get next unit descriptor mov cx,UDSC_LENGTH add endbios,cx ; grow the BIOS size xor ax,ax push di rep stosb ; zero the UDSC pop di xor bx,bx mov bl,nunits ; BX = unit we are working on mov es:UDSC_DRIVE[di],bl ; make that our logical unit shl bx,1 ; make it a BPB index lea ax,es:UDSC_DEVBPB[di] ; get storage area for device BPB mov bpbtbl[bx],ax ; update entry in BPB table shr bx,1 ; get the latest drive inc bx ; onto next unit cmp bl,2 ; 3rd floppy ? jne new_unit10 add bl,nhard ; yes, skip past hard disks new_unit10: mov nunits,bl ; store ready for next time mov es:UDSC_RUNIT[di],0FFh ; set physical drive (ROS code) push cs pop ds ; DS:SI -> our dummy value mov si,offset CGROUP:dummyMediaID call UpdateMediaID ; update UDSC_ with media info pop si pop dx pop cx pop bx pop ax pop ds ret ICODE ends RESBIOS segment 'RESBIOS' ; NOTE: we extend the resident BIOS size by the amount of ; memory required by the disk driver. db MAXPART*UDSC_LENGTH dup (?) RESBIOS ends IDATA segment p_unit db ? ; 80h, 81h for hard disks nsect db ? ; # of sectors per track nhead db ? ; # of heads on disk part_size dw 2 dup (?) ; temporary save address for size nunits db 2 ; start with driver C: nhard db 0 ; # of hard disk partitions nfloppy db 0 ; # of floppy drives Public init_runit init_runit db 0 ; poked with ROS Unit at boot IDATA ends end