rdos/kernel/drive.asm

199 lines
3.9 KiB
NASM

; code for disk i/o
; from outside, only the following methods are meant to be called:
; - logdrv: select drive DL to operate on
; - map*: map sector DX:AX into dskbuf
; - dirty: inform that dskbuf has been written to
; access is done by accessing dskbuf directly
section .bss
; drive actually selected for I/O
dsknum: resb 1
; bios drive we end up using
biosnum: resb 1
; current sector number
; absolute, ignores partition
dskseek: resd 1
; bit 0 - dirty flag for dskbuf
dskflag: resb 1
; disk buffer for I/O operations
dskbuf: resb 512
dpt: resb DPTSIZE
bpb: resb BPBSIZ4
section .text
; initial setup for disk i/o
dinit: ; copy previously set DPT to our data area
lds si, [4*0x1E]
mov di, dpt
mov cx, 11
rep movsb
; restore DS
xor ax, ax
mov ds, ax
; set vector
mov word [4*0x1E], dpt
mov word [4*0x1E+2], ds
ret
; restore DPT to default values if possible
; IN dl bios drive number
rstdpt: mov ah, 8
int 0x13
jc .ret
mov ax, es
or ax, di
test ax, ax
jz .ret
; DS:SI = ES:DI
mov ax, es
mov ds, ax
mov si, di
; ES:DI = dpt
xor ax, ax
mov es, ax
mov di, dpt
; do the copy
mov cx, 11
rep movsb
.ret: ; restore DS to 0
xor ax, ax
mov ds, ax
ret
; log in drive
; currently only supports 18 sectors 2 heads floppies
; IN dl drive number
logdrv: ; dont do anything if drive already selected
cmp dl, [dsknum]
je .ret
; clear out current contents
.force: push dx
call flush
mov ax, 0xFFFF
mov [dskseek], ax
mov [dskseek+2], ax
mov byte [dskflag], 0
pop dx
; set current drive number
mov [dsknum], dl
; save info for bios
mov [biosnum], dl
; reset dpt to defaults
call rstdpt
; set default geometry (1.44 MB floppy)
mov word [bpb+BPBNOS], 2
mov word [bpb+BPBSPT], 18
; load boot sector
xor ax, ax
xor dx, dx
call mapabs
; copy bios parameter block
; TODO: guess from first byte of FAT if BPB invalid
mov si, dskbuf+BPBOFF
mov di, bpb
mov cx, BPBSIZ4
rep movsb
; copy SPT to DPT
mov al, [bpb+BPBSPT]
mov [dpt+4], al
; make sure partition offset is forced zero
xor ax, ax
mov [bpb+BPBHS], ax
mov [bpb+BPBHS+2], ax
.ret: ret
calchs: ; put sectors per cylinder into bx
mov ax, [bpb+BPBSPT]
mul word [bpb+BPBNOS]
mov bx, ax
; put linear sector num into dx:ax
mov ax, [dskseek]
mov dx, [dskseek+2]
; dx:ax = linear count, bx = sectors / cylinder
div bx
xchg ax, dx
; dx = cylinder, ax = head * spt + sector
mov bl, [bpb+BPBSPT]
div byte bl
; dx = cylinder, al = head, ah = sector
xchg dl, dh
ror dl, 1
ror dl, 1
or dl, ah
inc dx
; al: head number
; dh bit 0-7: cylinder bits 0-7
; dl bit 0-5: sector bits 0-5
; dl bit 6-7: cylinder bits 8-9
mov cx, dx
mov dh, al
mov dl, [biosnum]
mov bx, dskbuf
ret
mapclu: ; counting from begin of cluster area
mov bx, [bpb+BPBRDE]
mov cl, 4
shr bx, cl ; 32 bytes per entry
add ax, bx
adc dx, 0
maprd: ; counting from beginning of dir
; add fat table sizes
xor ch, ch
mov cl, byte [bpb+BPBFN]
.loop: add ax, [bpb+BPBFS]
adc dx, 0
loop .loop
mapfat: ; counting from beginning of FAT
; add reserved sector count
add ax, [bpb+BPBRSC]
adc dx, 0
map: ; count from partition start
add ax, [bpb+BPBHS]
add dx, [bpb+BPBHS+2]
mapabs: ; absolute sector count
; skip doing a read if sector number matches
cmp ax, [dskseek]
jne read
cmp dx, [dskseek+2]
jne read
ret
read: push ax
push dx
call flush
pop dx
pop ax
; store the sector number
mov [dskseek], ax
mov [dskseek+2], dx
; do the actual read
; TODO: error handling & retries
call calchs
mov ax, 0x0201
int 0x13
ret
; mark dskbuf as containing unwritten changes
dirty: or byte [dskflag], 1 ; dirty
ret
; flush buffer if dirty
flush: test byte [dskflag], 1
jz .ret
; do the write
; TODO: error handling & retries
call calchs
mov ax, 0x0301
int 0x13
; remove dirty flag
and byte [dskflag], 0xFE
.ret: ret