317 lines
6.5 KiB
NASM
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 $
|