rdos/src/@rdos.asm

325 lines
6.2 KiB
NASM

org 0x700
cseg equ 0x0
jmp init
%include "bpb.inc"
%include "farptr.inc"
; dflags bitfields
DRVLOG equ 0x01 ; bit 0 (1) - drive logged in (see drvnum)
DRVRST equ 0x02 ; bit 1 (2) - controller configured
DRVEXT equ 0x04 ; bit 2 (4) - EBIOS supported
DIRTY equ 0x08 ; bit 3 (8) - dskbuf dirty
; initial setup for disk i/o
; copy previously set DPT to our data area
dinit xor ax, ax
mov es, ax
les bx, [es:4*0x1E]
mov dx, dpt
mov cx, 11
call lodfar
; set interrupt vector
mov es, cx
mov word [es:4*0x1E], dpt
mov word [es:4*0x1E+2], ds
ret
align 16
; IBM Interrupt Sharing Protocol structure
i13isp jmp short int13i
dw 0,0xFFFF
dw 0x424B
db 0
iret
align 16
; Entry point when user program calls int13h
; TODO: flush buffer and log out drive
int13i call int13
iret
; Wrapper around int 13h
int13 pushf
call far [i13isp+2]
; TODO: on error: reset & retry
; TODO: record errors
; TODO: multi-track reading
; TODO: handle DMA boundaries
ret
; get drive parameters
; read BIOS int13h/AH=8 values
; DPT data gets copied over our dpt
; CHS data gets inserted into our bpb
getprm test byte [dskflag], 4
jnz .ret
; do the query
mov ah, 8
mov dl, [biosnum]
call int13
; bail out if error
jc .ret
; ignore CHS values if odd
test cl, cl
jz .nochs
; get and store sector number
and cx, 0x3F
mov word [bpb+BPBSPT], cx
; get and store number of heads
xchg dl, dh
and dx, 0xFF
inc dx
mov [bpb+BPBNOS], dx
; test if DPT ptr is non-zero
.nochs mov ax, es
or ax, di
test ax, ax
jz .ret
; copy BIOS dpt table over ours
mov dx, dpt
mov bx, di
mov cx, 11
call lodfar
.ret ret
; select a drive for io
; IN dl drive number
; dont do anything if drive already selected
select cmp dl, [drvnum]
je logfdd.ret
; clear out current contents
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
; fork off if hdd
cmp dl, 2
jnc loghdd
logfdd mov [biosnum], dl
; assume some default values
xor ax, ax
mov word [bpb+BPBHS], ax
mov word [bpb+BPBHS+2], ax
mov word [bpb+BPBNOS], 2 ; 5 1/4" 360kB
mov word [bpb+BPBSPT], 9
; reset dpt to defaults
call getprm
; 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
call rstseg
mov si, dskbuf+BPBOFF
mov di, bpb
mov cx, BPBSIZ
rep movsb
; copy SPT to DPT
mov al, [bpb+BPBSPT]
mov [dpt+4], al
.ret ret
loghdd sub dl, 2
cmp dl, 4
jnc logerr
push dx
mov dl, 0x80
mov byte [biosnum], dl
; test for ebios extensions
mov ah, 0x41
mov bx, 0x55AA
call int13
sbb bx, 0xAA55
jnz .chs
; enable ebios and assume controller is configured
or byte [dflags], DRVCTL | DRVEXT
; get chs data (needed or we cant load vbr)
.chs call getprm
; read mbr
xor ax, ax
xor dx, dx
call mapabs
; get partition number
pop bx
xor bh, bh
mov cl, 4
sal bx, cl
; bail out if no partition
cmp byte [dskbuf+0x1be+bx+4], 0
je logerr
; load partition offset
mov ax, [dskbuf+0x1be+bx+8]
mov dx, [dskbuf+0x1be+bx+8+2]
; save to to stack
push dx
push ax
; load vbr
call mapabs
; copy bpb
mov si, dskbuf+BPBOFF
mov di, bpb
mov cx, BPBSIZ
rep movsb
; fix CHS data
call getprm
; fix partition offset
pop word [bpb+BPBHS]
pop word [bpb+BPBHS+2]
;pop [bpb+BPBNOS]
;pop [bpb+BPBSPT]
ret
logerr stc
ret
; map sector into dskbuf
; skip doing a read if sector number matches
; IN dx:ax absolute sector number
map cmp ax, [drvseek]
jne .do
cmp dx, [drvseek+2]
jne .do
ret
; flush previous contents
.do push ax
push dx
call flush
pop dx
pop ax
; set sector number
mov [drvseek], ax
mov [drvseek+2], dx
; issue read cmd
mov ch, 2
jmp diskio
; mark dskbuf as containing unwritten changes
dirty or word [cs:dflags], DIRTY
l002 ret
; flush buffer if dirty
flush test word [cs:dflags], DIRTY
jz l002
; issue write cmd
mov ch, 3
; Do disk I/O
; IN ch 2 = read
; 3 = write
; Sector number is read from [drvseek]
diskio mov cl, 1 ; read len
; DS := ES := CS
call rstseg
; check if ebios supported
test word [dflags], DRVEXT
jz .noext
; set up regs for ebios call
mov ax, cx
or ah, 0x40
mov si, dap
jmp .do
; check if we can skip controller reset
.noext test word [dflags], DRVRST
jnz .norst
; do controller reset
mov dl, [biosnum]
mov ah, 0
call int13
or word [dflags], DRVRST
; put linear sector num into dx:ax
.norst mov ax, [drvseek]
mov dx, [drvseek+2]
; dx:ax = linear count
div word [drvspc]
; TODO: is it possible to get an overflow here?
xchg ax, dx
; dx = cylinder, ax = head * spt + sector
div byte [drvspt]
; 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
; shuffle values around for bios
xchg ax, cx
xchg cx, dx
xchg dh, dl
mov bx, dskbuf
.do mov dl, [biosnum]
; ah: subfunction selected via cx previously
; al: 1 = reading 1 sector
; cx: sector and cylinder number
; dh: head number
; dl: drive number
; bx: offset to disk buffer
call int13
jc .err
; clear dirty flag on success
and word [dflags], ~DIRTY
clc
ret
; assume controller is misconfigured
.err and word [dflags], ~DRVRST
; exit with carry flag set
stc
ret
init mov ax, cs
; boot failure if loaded wrong
cmp ax, cseg
je $+4
int 0x18
halt hlt
jmp halt
dap db 0x10, 0
dw 1
dw dskbuf, cseg
drvseek dw 0,0,0,0
; bit 0 (1) - drive logged in (see drvnum)
; bit 1 (2) - controller configured
; bit 2 (4) - EBIOS supported
; bit 3 (8) - dskbuf dirty
dflags dw 0
section .bss
; drive currently logged in
drvnum resb 1
; number, just for bios
biosnum resb 1
; DPT tables for 4 floppy drives
dpt resb 4*14
; Information for logged in drive
drvss resb 1 ; sector size, 2^(7+n) bytes
drvcs resb 1 ; cluster size, 2^(7+n) bytes
alignb 4
drvspt resb 2 ; sectors per track
drvspc resb 2 ; sectors per cylinder
drvoff resb 4 ; partition offset
drvend resb 4 ; first sector after the partition
drvfat resb 4 ; offset to fat table
drvrd resb 4 ; offset of root directory
drvcla resb 4 ; offset to cluster area
drvfn resb 2 ; sectors per fat table
; disk buffer for I/O operations
alignb 2
dskbuf resb 1024