; 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 (1) - dirty flag for dskbuf ; bit 1 (2) - controller configured ; bit 2 (4) - EBIOS supported 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 ; int 13 stub ; place reserved for stack switches int13: ; debug output push bx push dx push cx push ax call printf db "int13 CALL AX=",2," CX=",2," DX=",2," BX=",2,0x0A,0x0D,0 ; do the call int 0x13 jc .err ret .err: push ax call printf db "int13 ERR AX=",2,0x0A,0x0D,0 stc 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 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 ; low level read and write ; call again to retry, no input registers ; read or write is configured in cx _read: mov ch, 2 db 0x3D ; cmp ax, imm16: causes next instr to be skipped _write: mov ch, 3 mov cl, 1 ; read len ; TODO: do ebios i/o ; check if we can skip controller reset test byte [dskflag], 2 jnz .l01 ; do controller reset mov dl, [biosnum] mov ah, 0 call int13 or byte [dskflag], 2 .l01: ; 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 ; shuffle values around for bios xchg ax, cx xchg cx, dx xchg dh, dl mov dl, [biosnum] mov bx, dskbuf ; 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 byte [dskflag], 0xFE clc ret .err: ; assume controller is misconfigured and byte [dskflag], 0xFD ; exit with carry flag set stc 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 ; TODO: error handling & retries jmp _write .ret: ret