Read sectors from FAT16

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
vbguyny
Member
Member
Posts: 48
Joined: Wed Aug 12, 2015 6:30 pm

Read sectors from FAT16

Post by vbguyny »

Ok, I have hit the wall on this. Basically, I have implemented a routine that is supposed to read sectors from the hard disk into memory based on the input sector number that is pasted into the routine. The issue is that when the sector number gets large (for example, 12,700), the routine returns data from the wrong sector on disk.

Below is my code:

Code: Select all

File.ReadSectors.Params.DestinationSegment: equ 10 ; 2 bytes
File.ReadSectors.Params.ReadSector: equ 8 ; 2 bytes
File.ReadSectors.Params.SectorCount: equ 6 ; 2 bytes
File.ReadSectors.Params.MemoryOffset: equ 4 ; 2 bytes
File.ReadSectors.ParamsSize: equ 8
File.ReadSectors.LocalsSize: equ 0
File.ReadSectors:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
; name:			File.ReadSectors.read_sectors 
; action:		reads one or more disk sectors into memory 
; in:			ES:0=address of memory where sectors should be read 
;			DX:AX=first sector to read 
;			CX=number of sectors to read 
; out (disk error):	CY=1 
; out (success):	CY=0 
; modifies:		DX, CX, AX 
; minimum CPU:		8088 
; notes: 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
	; Prologue
	enter File.ReadSectors.LocalsSize, 0
	pushad
	call Memory.ClearGeneralRegisters
	
	mov word ax, word [ebp + File.ReadSectors.Params.DestinationSegment]
	mov word es, word ax
	
	mov word cx, word [ebp + File.ReadSectors.Params.SectorCount]
	mov word ax, word [ebp + File.ReadSectors.Params.ReadSector]
	xor word dx, word dx
	
File.ReadSectors.read_sectors: 
	push es 
	push bx 
		;xor bx,bx 
		mov bx, word [ebp + File.ReadSectors.Params.MemoryOffset]
 
; add partition start 
 ;xchg bx, bx
		add ax, word [File.FatInfo.HiddenSectors + 0] ; [bp - (over - hidden_sectors) + 0] 
		adc dx, word [File.FatInfo.HiddenSectors + 2]
File.ReadSectors.read_sectors_1: 
		push dx 
		push cx 
		push ax 
 
; DX:AX=LBA sector number 
			push ax 
				mov ax,dx 
				xor dx,dx 
 
; divide LBA by sectors_per_track (spt). 
; Use a two-step 32-bit/16-bit divide to avoid overflow. 
; See "9.3.5 Extended Precision Division" in Randall Hyde's "Art of Assembly" 
				div word [File.FatInfo.SectorsPerTrack] ; [bp- (over - sectors_per_track)] 
 
; DX=high LBA % spt, AX=high LBA / spt 
				mov cx,ax 
			pop ax 
 
; DX=high LBA % spt, CX=high LBA / spt, AX=low LBA 
; New 32-bit dividend is DX:AX 
			div word [File.FatInfo.SectorsPerTrack] ; [bp- (over - sectors_per_track)] 
 
; CX:AX=LBA / spt, DX=LBA % spt 
			xchg cx,dx 
			inc cx			; CL=sector 
 
 
; DX:AX=LBA / spt, do normal one-step divide by heads value 
			div word [File.FatInfo.NumberOfHeads] ; [bp - (over - heads)] 
			mov dh,dl		; DH=head 
			mov ch,al		; CH=cylinder 7:0 
			mov dl, byte [File.FatInfo.DriveLetter] ; [bp - (over - boot_drive)] 
			shl cl,1 
			shl cl,1		; trying to use only... 
			shr ah,1		; ...8088 asm here 
			rcr cl,1 
			shr ah,1 
			rcr cl,1		; CL7:6=cyl 9:8, CL5:0=sector 
 
; now: read one sector 
			mov ax,0201h 
			int 13h 
			jnc File.ReadSectors.read_sectors_3 
 
; disk error; recalibrate/reset drive 
			mov ah,0 
			int 13h 
			jc File.ReadSectors.read_sectors_2 
; try the read again. If it fails a second time, give up. 
			mov ax,0201h 
			int 13h 
			jnc File.ReadSectors.read_sectors_3 
File.ReadSectors.read_sectors_2: 
		pop ax 
		pop cx 
		pop dx 
		jmp short File.ReadSectors.read_sectors_5 
 
; advance memory pointer 
File.ReadSectors.read_sectors_3: 
			mov ax,es 
			add ax, 32 ; [File.FatInfo.ParagrahsPerSector] ; [bp - (over - para_per_sector)] 
			mov es,ax 
		pop ax 
		pop cx 
		pop dx 
 
; increment DX:AX to advance to next sector 
		inc ax 
		jne File.ReadSectors.read_sectors_4 
		inc dx 
File.ReadSectors.read_sectors_4: 
		loop File.ReadSectors.read_sectors_1 
 
; clear CY bit to signal success 
		clc 
File.ReadSectors.read_sectors_5: 
	pop bx 
	pop es 
	
	;ret 
File.ReadSectors.End:
	; Epilogue
	popad
	leave
	ret File.ReadSectors.ParamsSize
User avatar
iansjack
Member
Member
Posts: 4952
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: Read sectors from FAT16

Post by iansjack »

I'd suggest that you run your code under a debugger to check exactly what parameters you are passing to the interrupt.
sebihepp
Member
Member
Posts: 256
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Read sectors from FAT16

Post by sebihepp »

I think you want to change the following instructions

Code: Select all

; DX:AX=LBA sector number 
         push ax 
            mov ax,dx 
            xor dx,dx
into this:

Code: Select all

; DX:AX=LBA sector number 
         push ax 
            mov dx,ax 
            xor dx,dx
Techel
Member
Member
Posts: 215
Joined: Fri Jan 30, 2015 4:57 pm
Location: Germany
Contact:

Re: Read sectors from FAT16

Post by Techel »

Moving ax to dx and then setting dx to 0 rarely makes sense ;)
sebihepp
Member
Member
Posts: 256
Joined: Tue Aug 26, 2008 11:24 am
GitHub: https://github.com/sebihepp

Re: Read sectors from FAT16

Post by sebihepp »

Roflo wrote:Moving ax to dx and then setting dx to 0 rarely makes sense ;)
Yeah, that's why I supposed it. This seems to be Intel Syntax. ;)
vbguyny
Member
Member
Posts: 48
Joined: Wed Aug 12, 2015 6:30 pm

Re: Read sectors from FAT16

Post by vbguyny »

Yes, this is Intel syntax. I will get the parameters for the interrupt call.
vbguyny
Member
Member
Posts: 48
Joined: Wed Aug 12, 2015 6:30 pm

Re: Read sectors from FAT16

Post by vbguyny »

Parameters (debugged in Bochs):

ah: 02h
al: 01h
bh: 80h
bl: 00h
ch: 24h
cl: 32h
dh: 01h
dl: 80h
es: 6000h
di: 0000h
si: 0000h

The carry flag is NOT set after executing the interrupt.

EDIT: my hard disk is 200 MB
User avatar
TightCoderEx
Member
Member
Posts: 90
Joined: Sun Jan 13, 2013 6:24 pm
Location: Grande Prairie AB

Re: Read sectors from FAT16

Post by TightCoderEx »

Assuming FFH Heads, FFH Cylinders and 3FH Sectors / Track

Is the value in DX:AX passed to routine 0:412AH ?
vbguyny
Member
Member
Posts: 48
Joined: Wed Aug 12, 2015 6:30 pm

Re: Read sectors from FAT16

Post by vbguyny »

The geometry of the disk is C=406, S=63, H=16.
Yes, DX:AX is correct, that is the physical cluster that I am trying to read from the disk.
User avatar
TightCoderEx
Member
Member
Posts: 90
Joined: Sun Jan 13, 2013 6:24 pm
Location: Grande Prairie AB

Re: Read sectors from FAT16

Post by TightCoderEx »

I seem to want to remember some sort of mention that AH=2 & DL=80H or 81H drops into some kind of emulation layer for larger disks and not guaranteed to work with all BIOS. Maybe function 42H (Extended Read) may be a better alternative.
vbguyny
Member
Member
Posts: 48
Joined: Wed Aug 12, 2015 6:30 pm

Re: Read sectors from FAT16

Post by vbguyny »

@TightCoderEx: Thanks for the suggestion. I have started to implement the extended functions which appear to be supported by the version of Bochs that I am using (2.6.8) however I think I am missing something. Below is the code that I am using to implement the extended read:

Code: Select all

; Format of disk address packet:

; Offset  Size    Description     (Table 00272)
; 00h    BYTE    size of packet (10h or 18h)
; 01h    BYTE    reserved (0)
; 02h    WORD    number of blocks to transfer (max 007Fh for Phoenix EDD)
; 04h    DWORD   -> transfer buffer
; 08h    QWORD   starting absolute block number
; (for non-LBA devices, compute as
; (Cylinder*NumHeads + SelectedHead) * SectorPerTrack +
; SelectedSector - 1
; 10h    QWORD   (EDD-3.0, optional) 64-bit flat address of transfer buffer;
; used if DWORD at 04h is FFFFh:FFFFh
File.ReadSector.DAP:
	.SizeOfPacket: db 0x10
	.Reserved: db 0x00
	.SectorCount: dw 0x0000
	.Buffer: dd 0x000000
	.SectorNumber: dq 0x000000000000

File.ReadSectors.Params.DestinationSegment: equ 10 ; 2 bytes
File.ReadSectors.Params.ReadSector: equ 8 ; 2 bytes
File.ReadSectors.Params.SectorCount: equ 6 ; 2 bytes
File.ReadSectors.Params.MemoryOffset: equ 4 ; 2 bytes
File.ReadSectors.ParamsSize: equ 8
File.ReadSectors.LocalsSize: equ 0
File.ReadSectors:
	; Prologue
	enter File.ReadSectors.LocalsSize, 0
	pushad
	push word es
	call Memory.ClearGeneralRegisters
	
	;Int 13/AH=42h
	; AH = 42h
	; DL = drive number
	; DS:SI -> disk address packet (see #00272)
	
	xchg bx, bx
	
	mov word ax, word [ebp + File.ReadSectors.Params.SectorCount]
	mov word [File.ReadSector.DAP.SectorCount], word ax
	
	movzx dword eax, word [ebp + File.ReadSectors.Params.ReadSector]
	add dword eax, dword [File.FatInfo.HiddenSectors]
	mov dword [File.ReadSector.DAP.SectorNumber], dword eax
	
	xor dword eax, dword eax
	mov word ax, word [ebp + File.ReadSectors.Params.DestinationSegment]
	shl dword eax, 4
	add word ax, word [ebp + File.ReadSectors.Params.MemoryOffset]
	mov dword [File.ReadSector.DAP.Buffer], dword eax
	
	mov byte ah, byte 0x42
	mov byte dl, byte [File.FatInfo.DriveLetter]
	mov word si, word File.ReadSector.DAP
	int 0x13
	
	
	
	; Epilogue
File.ReadSectors.End:
	pop word es
	popad
	leave
	ret File.ReadSectors.ParamsSize
I am not getting an errors (carry not set and ah register 00h upon return).
User avatar
TightCoderEx
Member
Member
Posts: 90
Joined: Sun Jan 13, 2013 6:24 pm
Location: Grande Prairie AB

Re: Read sectors from FAT16

Post by TightCoderEx »

Code: Select all

 xor dword eax, dword eax
   mov word ax, word [ebp + File.ReadSectors.Params.DestinationSegment]
   shl dword eax, 4
   add word ax, word [ebp + File.ReadSectors.Params.MemoryOffset]
   mov dword [File.ReadSector.DAP.Buffer], dword eax
Don't make these a 32 bit pointer.

Following excerpt from here.

segment:offset pointer to the memory buffer to which sectors will be transferred (note that x86 is little-endian: if declaring the segment and offset separately, the offset must be declared before the segment)
vbguyny
Member
Member
Posts: 48
Joined: Wed Aug 12, 2015 6:30 pm

Re: Read sectors from FAT16

Post by vbguyny »

Thanks for your help, I got it working below is the corrected code:

Code: Select all

; Format of disk address packet:

; Offset  Size    Description     (Table 00272)
; 00h    BYTE    size of packet (10h or 18h)
; 01h    BYTE    reserved (0)
; 02h    WORD    number of blocks to transfer (max 007Fh for Phoenix EDD)
; 04h    DWORD   -> transfer buffer
; 08h    QWORD   starting absolute block number
; (for non-LBA devices, compute as
; (Cylinder*NumHeads + SelectedHead) * SectorPerTrack +
; SelectedSector - 1
; 10h    QWORD   (EDD-3.0, optional) 64-bit flat address of transfer buffer;
; used if DWORD at 04h is FFFFh:FFFFh
File.ReadSector.DAP:
	.SizeOfPacket: db 0x10
	.Reserved: db 0x00
	.SectorCount: dw 0x0000
	.BufferOffset: dw 0x000000
	.BufferSegment: dw 0x000000
	.SectorNumber: dq 0x000000000000

File.ReadSectors.Params.DestinationSegment: equ 12 ; 2 bytes
File.ReadSectors.Params.ReadSector: equ 8 ; 4 bytes
File.ReadSectors.Params.SectorCount: equ 6 ; 2 bytes
File.ReadSectors.Params.MemoryOffset: equ 4 ; 2 bytes
File.ReadSectors.ParamsSize: equ 10
File.ReadSectors.LocalsSize: equ 0
File.ReadSectors:
	; Prologue
	enter File.ReadSectors.LocalsSize, 0
	pushad
	push word es
	call Memory.ClearGeneralRegisters
	
	;Int 13/AH=42h
	; AH = 42h
	; DL = drive number
	; DS:SI -> disk address packet (see #00272)
	
	mov word ax, word [ebp + File.ReadSectors.Params.SectorCount]
	mov word [File.ReadSector.DAP.SectorCount], word ax
	
	mov dword eax, dword [ebp + File.ReadSectors.Params.ReadSector]
	add dword eax, dword [File.FatInfo.HiddenSectors]
	mov dword [File.ReadSector.DAP.SectorNumber], dword eax
	
	mov word ax, word [ebp + File.ReadSectors.Params.MemoryOffset]
	mov word [File.ReadSector.DAP.BufferOffset], word ax
	mov word ax, word [ebp + File.ReadSectors.Params.DestinationSegment]
	mov word [File.ReadSector.DAP.BufferSegment], word ax
	
	mov byte ah, byte 0x42
	mov byte dl, byte [File.FatInfo.DriveLetter]
	mov word si, word File.ReadSector.DAP
	int 0x13
	
	; Epilogue
File.ReadSectors.End:
	pop word es
	popad
	leave
	ret File.ReadSectors.ParamsSize
vbguyny
Member
Member
Posts: 48
Joined: Wed Aug 12, 2015 6:30 pm

Re: Read sectors from FAT16

Post by vbguyny »

Ok, I was able to figure out my issue. The problem was that the physical cluster was greater than 65535 (0xFFFF) which caused my OS to load to wrong data if the data I was trying to load was located in disk at an offset that was greater than 65535.

By changing it to work with dwords and patching all my function calls to this routine and everything is good now on my end! Thanks for your help, everyone!!!
Post Reply