; 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 dap: resw 2 .buf: resw 2 ; current sector number ; absolute, ignores partition dskseek: resd 2 ; 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 ; set to invalid drive mov byte [dsknum], 0xFF ret ; int 13 stub 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 "DISK ERR AX=",2,0x0A,0x0D,0 ;stc ;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 .nodpt ; do the query mov ah, 8 mov dl, [biosnum] call int13 ; bail out if error jc .nodpt ; 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 .nodpt ; 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 .nodpt: ; restore segment registers xor ax, ax mov ds, ax mov es, ax ret ; log in drive ; IN dl drive number logdrv: ; dont do anything if drive already selected cmp dl, [dsknum] 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: ; save info for bios mov [biosnum], dl ; reset dpt to defaults call getprm ; 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 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 [dskflag], (2+4) ; initialize DAP mov word [dap], 0x10 mov word [dap+2], 1 mov word [dap+4], dskbuf xor ax, ax mov word [dap+6], ax mov word [dap+12], ax mov word [dap+14], ax ; 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, BPBSIZ4 rep movsb ; fix CHS data call getprm ; fix partition offset pop ax pop dx mov [bpb+BPBHS], ax mov [bpb+BPBHS+2], dx ret logerr: stc 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 ; check if ebios supported test byte [dskflag], 4 jz .l00 mov ax, cx or ah, 0x40 mov si, dap jmp .do ; check if we can skip controller reset .l00: 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 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 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