; 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 DPTSIZE equ 11 DPTSPT equ 4 section .bss ; bios drive we end up using biosnum resb 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 .data ; drive actually selected for I/O dsknum db 0xFF dap db 0x10, 0 dw 1 dapbuf dw dskbuf, 0 dskseek dw 0,0,0,0 section .text ; 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 ; set segment addr in DAP mov [dapbuf+2], cs ret ; int 13 stub ; this is meant as a central place to implement ; stack switching or some other safeguards int13 int 0x13 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 call lodfar .ret ret ; log in drive ; IN dl drive number ; dont do anything if drive already selected logdrv 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 ; save info for bios logfdd 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 call rstseg 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) ; 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 word [bpb+BPBHS] pop word [bpb+BPBHS+2] ;pop [bpb+BPBNOS] ;pop [bpb+BPBSPT] ret logerr stc ret ; counting from begin of cluster area mapclu mov bx, [bpb+BPBRDE] mov cl, 4 shr bx, cl ; 32 bytes per entry add ax, bx adc dx, 0 ; counting from beginning of dir ; add fat table sizes maprd xor ch, ch mov cl, byte [bpb+BPBFN] .loop add ax, [bpb+BPBFS] adc dx, 0 loop .loop ; counting from beginning of FAT ; add reserved sector count mapfat add ax, [bpb+BPBRSC] adc dx, 0 ; count from partition start map add ax, [bpb+BPBHS] add dx, [bpb+BPBHS+2] ; absolute sector count ; skip doing a read if sector number matches mapabs 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 [cs:dskseek], ax mov [cs: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 ; DS := ES := CS mov ax, cs mov ds, ax mov es, ax ; 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 [cs:dskflag], 1 ; dirty ret ; flush buffer if dirty flush test byte [cs:dskflag], 1 jz .ret ; TODO: error handling & retries jmp _write .ret ret