351 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			NASM
		
	
	
	
	
	
			
		
		
	
	
			351 lines
		
	
	
	
		
			6.8 KiB
		
	
	
	
		
			NASM
		
	
	
	
	
	
| ; 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
 | |
| 		push cs
 | |
| 		pop es
 | |
| 		lds si, [es:4*0x1E]
 | |
| 		mov di, dpt
 | |
| 		mov cx, 11
 | |
| 		rep movsb
 | |
| 		; set vector
 | |
| 		mov word [cs:4*0x1E], dpt
 | |
| 		mov word [cs:4*0x1E+2], ds
 | |
| 		; set to invalid drive
 | |
| 		mov byte [cs: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:		; skip if EBIOS extension enabled
 | |
| 		test byte [cs:dskflag], 4
 | |
| 		jnz .ret
 | |
| 		; do the query
 | |
| 		mov ah, 8
 | |
| 		mov dl, [cs: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 [cs:bpb+BPBSPT], cx
 | |
| 		; get and store number of heads
 | |
| 		xchg dl, dh
 | |
| 		and dx, 0xFF
 | |
| 		inc dx
 | |
| 		mov [cs:bpb+BPBNOS], dx
 | |
| 		; test if DPT ptr is non-zero
 | |
| .nochs:		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:		ret
 | |
| 
 | |
| 		; log in drive
 | |
| 		; IN	dl	drive number
 | |
| logdrv:		; DS := CS
 | |
| 		push cs
 | |
| 		pop ds
 | |
| 		; dont do anything if drive already selected
 | |
| 		cmp dl, [dsknum]
 | |
| 		je logfdd.ret
 | |
| 		; clear out current contents
 | |
| 		push dx
 | |
| 		push ds
 | |
| 		call flush
 | |
| 		pop ds
 | |
| 		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
 | |
| 		push ds
 | |
| 		call getprm
 | |
| 		pop ds
 | |
| 		; set default geometry (1.44 MB floppy)
 | |
| 		mov word [bpb+BPBNOS], 2
 | |
| 		mov word [bpb+BPBSPT], 18
 | |
| 		; load boot sector
 | |
| 		push ds
 | |
| 		xor ax, ax
 | |
| 		xor dx, dx
 | |
| 		call mapabs
 | |
| 		pop ds
 | |
| 		; copy bios parameter block
 | |
| 		; TODO: guess from first byte of FAT if BPB invalid
 | |
| 		push ds
 | |
| 		pop es
 | |
| 		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
 | |
| 
 | |
| 		; assumes that DS == CS
 | |
| 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 [cs:dskbuf+0x1be+bx+4], 0
 | |
| 		je logerr
 | |
| 		; load partition offset
 | |
| 		mov ax, [cs:dskbuf+0x1be+bx+8]
 | |
| 		mov dx, [cs:dskbuf+0x1be+bx+8+2]
 | |
| 		; save to to stack
 | |
| 		push dx
 | |
| 		push ax
 | |
| 		; load vbr
 | |
| 		call mapabs
 | |
| 		; copy bpb
 | |
| 		mov ax, cs
 | |
| 		mov ds, ax
 | |
| 		mov es, ax
 | |
| 		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 [cs:bpb+BPBHS], ax
 | |
| 		mov [cs:bpb+BPBHS+2], dx
 | |
| 		ret
 | |
| 
 | |
| logerr:		stc
 | |
| 		ret
 | |
| 
 | |
| mapclu:		; counting from begin of cluster area
 | |
| 		mov bx, [cs: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 [cs:bpb+BPBFN]
 | |
| .loop:		add ax, [cs:bpb+BPBFS]
 | |
| 		adc dx, 0
 | |
| 		loop .loop
 | |
| mapfat:		; counting from beginning of FAT
 | |
| 		; add reserved sector count
 | |
| 		add ax, [cs:bpb+BPBRSC]
 | |
| 		adc dx, 0
 | |
| map:		; count from partition start
 | |
| 		add ax, [cs:bpb+BPBHS]
 | |
| 		add dx, [cs:bpb+BPBHS+2]
 | |
| mapabs:		; absolute sector count
 | |
| 		; skip doing a read if sector number matches
 | |
| 		cmp ax, [cs:dskseek]
 | |
| 		jne read
 | |
| 		cmp dx, [cs: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
 |