int 0x13, AH 0x02

If you are new to OS Development, plan on spending some time here first before going into the other forums.

Moderator: Moderators

Post Reply
martin
Posts: 2
Joined: Tue Jul 24, 2018 11:14 pm

int 0x13, AH 0x02

Post by martin » Sun Aug 05, 2018 9:50 pm

Hi,

I ran into the issue I'm not able to find out the root cause of. I'd appreciate if somebody can help.
I'm reading the disk partition using the int 0x13 service 02 sector by sector.

Virtual disk parameters: 20MB, MBR layout with one FAT16 partition, start: 0/1/1, end: 318/01/63 , sectors per track: 63, heads per cylinder: 2, sector size: 512, cluster size: 4.

I noticed that if I specify to read only one sector (AX: 0x0201) and go beyond the disk offset 0xfe00 (first fail at CHS 1/0/1) I start seeing weird behavior. No error is indicated (AH = 0 after int 0x13) but I start reading back 0s. ES:BX is set to write to the same location all the time, buffer doesn't grow out of the segment, etc.

I'm attaching my code too:
Just before call AX = 0; CX = -1, ES:BX = 0x7c0:0200 (phys 0x7e00)

Code: Select all

        ; Reads sectors into the buffer. Requires following arguments:
        ;
        ;       AX:     starting sector for read
        ;       CX:     sectors to read
        ;       ES:BX   buffer to read to
        ;
read_sectors:
.freshread:
        mov di, HDD_RETRY_SECTOR_READ
.retry:
        inc cx          ; XXX: debug
        push cx
        push ax
        call lba2chs
                                                ; BIOS call 0x13, function: AH = 0x02 (read sectors from drive)
        mov ch, byte [chs_track]                ;       CH:     cylinder
        mov cl, byte [chs_sector]               ;       CL:     sector
        mov dh, byte [chs_head]                 ;       DH:     head
        mov dl, byte [bsDriveNumber]            ;       DL:     drive
        mov ax, 0x0201                          ;       AL:     sectors to read
        int 0x13

        jnc .ok
        xor ax,ax                               ; BIOS call 0x13, function: AH: 0 = reset disk
        int 0x13

        pop ax
        pop cx
        dec di                                  ; retry till we don't reach the HDD_RETRY_SECTOR_READ
        jnz .retry
.error:
        lea si, [rdf]
        call puts16
        jmp fatal                               ; fatal error, reboot
.ok:

        lea si, [msg_dot]
        call puts16

        pop ax
        pop cx
        inc ax
        ; debug
        ; add bx, word [bpbBytesPerSector]
        times 4 nop

        loop .freshread
        ret


Btw. many thanks for the OS dev series.

User avatar
Mike
Site Admin
Posts: 465
Joined: Sat Oct 20, 2007 7:58 pm
Contact:

Re: int 0x13, AH 0x02

Post by Mike » Sat Jun 01, 2019 3:01 am

Hi,

There is a known error in the original implementation of readSectors where BX may overflow. The following modifications will resolve it:

Code: Select all

ReadSectors:
   ;
   ; go to following instruction in your code:
   ;
   add bx, WORD [bpbBytesPerSector]
   ;
   ; ...and use the below code right after above instruction:
   ;
   jnc .ready
 .next_segment:
     mov dx, es
     add dh, 0x10
     mov es, dx
 .ready:
     inc ax
     loop .MAIN
     ret
However, for hard disks, you really should be using the extended read functions -- int 13h function 42h. Please see our fat32 boot record here for a working example of its use.

BoydBun
Posts: 2
Joined: Wed Aug 16, 2017 12:55 pm

Re: int 0x13, AH 0x02

Post by BoydBun » Fri Jul 12, 2019 2:34 pm

What does this modification do exactly, Mike? I always like to know the reasons behind stuff. haha

User avatar
Mike
Site Admin
Posts: 465
Joined: Sat Oct 20, 2007 7:58 pm
Contact:

Re: int 0x13, AH 0x02

Post by Mike » Sat Aug 10, 2019 2:16 am

Hi,

ES:BX is used as the location to where the sector is loaded into. If BX wraps back around from 0xfe00 to 0 (keep in mind BX is incremented by 0x200 bytes each time we read a sector), CF gets set if BX overflows. This tells us that ES:BX has reached the end of its segment. Since real mode segments are 64k in size, we need to adjust ES by 0x1000 such that:

0:0xfe00 + 0x200 = 0x1000:0.

The code is perhaps a little harder to read then it should be -- it adds 0x10 to the high byte of ES (which is the same as adding 0x1000 to ES.) BX will be 0 at this point since it has already overflowed.

In the original bug, the overflow would have resulted in readSectors overwriting beginning sectors (thereby corrupting them) when the number of sectors exceeded 128 (64k.)

Post Reply