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