rdos/src/@bootsec.asm

317 lines
6.5 KiB
NASM

base equ 0x7C00
org base
; boot sector identification bytes:
jmp short init ; EB3C
nop ; 90
; OEM vendor field
vendor .space 8, 0x20
; bios parameter block
ss .space 2 ; word bytes per sector
sc .space 1 ; byte sectors per cluster
rsc .space 2 ; word reserved sector count
fn .space 1 ; byte number of fat tables
rde .space 2 ; word root disk entries
ts .space 2 ; word total sectors
md .space 1 ; byte media descriptor byte
fs .space 2 ; word sectors per fat
spt .space 2 ; word sectors per track
nos .space 2 ; word number of sides/heads
hs .space 4 ; dword partition offset
lts .space 4 ; dword large total sectors
; extended bios parameter block data
.space base+03Eh-$
init xor ax, ax ; ax as quick zero value
; initialize all segment registers
; the docs say we cant rely on them to have any sensible values
; we only work with the first 64kB per default
; so we set all of them to zero
mov ds, ax
mov es, ax
mov ss, ax
mov sp, ax
jmp main
; print AX in base BX
; destroys DX
putnum xor dx, dx
div bx
test cx, cx
loopnz .pad
test ax, ax
je .nib
.pad push dx
call putnum
pop dx
; print lower DL as digit
.nib add dl, 0x30
cmp dl, 0x3a
jl putc
add dl, 7
; print DL as char
putc mov ah, 0x0e
mov al, dl
int 0x10
ret
; print str at SI
; NUL-terminated
puts lodsb
test al, al
jz .l01
mov dl, al
call putc
jmp puts
.l01 ret
; print a newline
newlin mov dl, 0x0A
call putc
mov dl, 0x0D
jmp putc
; print register set
; order AX CX DX BX BP SI DI IP
; trashes flags
debug push di
push si
push bp
push bx
push dx
push cx
push ax
mov si, sp
call dump
pop ax
pop cx
pop dx
pop bx
pop bp
pop si
pop di
ret
; dump the 8 words at SI
dump lea di, [si+16]
mov bx, 0x10
.loop lodsw
mov cx, 4
call putnum
mov dl, 020h ; space char
call putc
cmp si, di
jc .loop
jmp newlin
; Advantages over using int13h directly:
; - Translation from linear sector number into CHS data
; - Reads across different tracks.
; Older BIOSes don't support that.
; - Nearby sector reads/writes are coalesced into one int 13h call
; This improves performance on real hardware.
; Only floppies are supported, and only variants with two sides
; This includes the standard 5 1/4 360kB and 3 1/2 1.44MB formats
; When a sector is requested, int13h is not called yet, the parameters
; for int 13h are recorded. When the next sector is requested, either
; it it suitable for coalescing, then the recorded parameters are
; adjusted. When the sector is not suitable for coalescing, the
; previous sector is read/written and the parameters for the new
; sector are put into the record. If the program is done with listing
; the wanted sectors, another function can be called to force doing
; the recorded io request
; Select a sector for a disk operation
; IN ax linear sector number
; cx number of sectors to read/write
; dl drive number
sector push ax
push cx
; split up sector number into CHS data
; divisor is sectors per track field in bios parameter block
; quotient al as track number
; remainder ah as sector number (starting with 0)
div byte [spt]
; phys sector number cl (starting with 1)
mov cl, ah
inc cl
; get head number dh from lowest bit of track number
; hard-coded assumption: two-sided floppy
mov dh, al
and dh, 1
; cylinder number ch is track number, except the head number shifted out
mov ch, al
shr ch, 1
; Unlucky if different head (dh)
cmp byte dh, [disk_dh]
jne .miss
; Unlucky if different track (ch) or not subsequent sector (cl + number of op sectors)
mov ax, cx
sub al, [disk_al]
cmp ax, [disk_cx]
jne .miss
; We are lucky! We can extend the current request by increasing the number of op sectors
; Do nothing else
inc byte [disk_al]
jmp .end
; The sector we just selected does not fit into the recorded io request
; So we submit that request first and then record a new io request
.miss: push cx
push dx
call diskio
pop dx
pop cx
; Record new io request
mov word [disk_cx], cx
mov byte [disk_dh], dh
mov byte [disk_al], 1 ; only one sector yet
.end pop cx
pop ax
inc ax
loop sector
ret
; Do the io request we recorded previously now
diskio mov ax, [disk_ax]
mov cx, [disk_cx]
mov dx, [disk_dx]
mov bx, [disk_bx]
; no-op if amount of sectors is zero
cmp al, 0
je .ret
; TODO: retry is needed for real floppies
call debug
int 0x13
jc .ret
; Advance read/write pointer in memory
mov ah, 0
mov cl, 9
shl ax, cl
add word [disk_bx], ax
; Reset op sector length, work done
mov byte [disk_al], 0
.ret ret
; Load a file from disk to 0x7C00
; calculate size of the directory in sectors
load: mov ax, [rde]
mov cl, 4 ; sector=512b, entry=32b -> shift 4 bits
shr ax, cl
mov cx, ax
push cx ; remember number of dir entries for later
; calculate start of second fat table
mov ax, [fs] ; read fat table length
push ax
add cx, ax ; make cx size of FAT + dir
inc ax ; add one for the boot sector
; Read second FAT and directory into buffer
mov byte [disk_ah], 2 ; set read OP
mov word [disk_bx], fatbuf ; write into our own buffer
call sector
call diskio
; get address of root directory in frame
pop bx
mov cl, 9
shl bx, cl
add bx, fatbuf
pop dx
mov word [disk_bx], 0x7C00
; search through root directory
.loop: mov si, bx
mov ax, [bx+0x1A]
mov di, fname
mov cx, 11
repe cmpsb
je .cloop
add bx, 0x20
or byte [bx], 0
jz .end
jmp .loop
; calculate sector offset in cluster area
.cloop push ax
sub ax, 2
mov ch, 0
mov cl, [sc]
push cx
mul cx
; calculate sector offset from root directory
mov dx, [rde]
mov cl, 4 ; sector=512b, entry=32b -> shift 4 bits
shr dx, cl
add ax, dx
; add length of fat tables
add ax, [fs]
add ax, [fs]
; plus 1 for the boot sector
inc ax
; load cluster
pop cx
call sector
; follow fat chain
pop ax
mov si, ax
shr si, 1
add si, ax
mov dx, [fatbuf+si]
; shift value if needed
test ax, 1
jz .noshr
mov cl, 4
shr dx, cl
.noshr and dx, 0x0FFF
mov ax, dx
cmp ax, 0xFF0
jc .cloop
.fend: call diskio
.end: ret
main: mov byte [disk_dl], dl
call load
db 0xEA ; jmp far 0:0x7C00
dw 0x7C00, 0
; register buffer for disk io
disk_al db 0 ; number of sectors to read
disk_ah db 2 ; 2=read op
disk_cl db 1 ; sector number
disk_ch db 1 ; cylinder number
disk_dl db 0 ; drive number
disk_dh db 0 ; head number
disk_bx dw fatbuf ; data buffer addr, advanced by 'diskio'
disk_ax equ disk_al
disk_cx equ disk_cl
disk_dx equ disk_dl
fname db "@RDOS ", "COM"
.space base+01FEh-$
dw 0xAA55
; buffer for fat table and root directory
; we load it together to save an int 13h read
; at the cost of having to calculate offset of rootdir
fatbuf equ $