Page 1 of 1

Read sectors from FAT16

Posted: Thu Sep 17, 2015 9:38 pm
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

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 12:16 am
by iansjack
I'd suggest that you run your code under a debugger to check exactly what parameters you are passing to the interrupt.

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 1:47 am
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

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 3:56 am
by Techel
Moving ax to dx and then setting dx to 0 rarely makes sense ;)

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 5:31 am
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. ;)

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 7:33 am
by vbguyny
Yes, this is Intel syntax. I will get the parameters for the interrupt call.

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 8:41 am
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

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 11:08 am
by TightCoderEx
Assuming FFH Heads, FFH Cylinders and 3FH Sectors / Track

Is the value in DX:AX passed to routine 0:412AH ?

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 11:44 am
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.

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 12:16 pm
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.

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 1:00 pm
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).

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 4:30 pm
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)

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 6:17 pm
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

Re: Read sectors from FAT16

Posted: Fri Sep 18, 2015 6:25 pm
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!!!