Files
DR-DOS-OpenDOS/IBMBIO/DISK.ASM
2020-11-04 23:59:28 +01:00

2619 lines
70 KiB
NASM

; 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 <es, bx, cx, si, di> ; save work registers
mov i13_dma_off,bx
mov i13_dma_seg,es
i13_deblock10:
pushx <cx, dx>
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 <dx, cx>
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 <di, si, cx, bx, es> ; 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 <ds, si>
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 <si, ds> ; 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 <es, di> ; 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 <di, es>
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 <ds, es, di>
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 <di, es, ds>
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 <di, ds, es>
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 <es, ds, di>
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 <bx, ds>
sub ax,ax
mov ds,ax
Assume DS:IVECT
lds bx,i1eptr
xchg al,9[bx]
Assume DS:CGROUP
popx <ds, bx>
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 <ax, bx, ds>
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 <ds, bx, ax>
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 <es, di>
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 <di, es>
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 <ds, es>
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 <es, ds>
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 <es, di, dx> ; save our registers
mov ah,ROS_PARAM ; read drive parameters
int_____DISK_INT ; find out floppy type
popx <dx, di, es>
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 <es, di, si, cx>
lea di,es:UDSC_BPB[di]
mov ax,ds
mov es,ax ; ES = DS
rep movsb ; make default BPB current BPB in UDSC
popx <cx, si, di, es>
pushx <es, di>
lea di,es:UDSC_DEVBPB[di]
rep movsb ; copy default BPB device BPB in UDSC
popx <di, es>
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 <cx, dx> ; save drive count, physical drive
call login_hdisk ; find all partitions on hard disk
popx <dx, cx> ; 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 <cx, dx> ; save partition table address
call login_primary ; login primary partition
popx <dx, cx> ; 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 <bx,cx,dx>
call login_read_dx ; try to read the partition boot
popx <dx,cx,bx>
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 <ds, di>
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 <di, ds>
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