assemblyx86-16osdevbios

INT 13h cannot read beyond specific sector


I am writing a kernel for my operating system and I have ran into a problem while loading a sector of a disk to memory.

Here is the portion of the code of the function that loads the sector from the disk:

        mov     ax, #0x3000
        mov     es, ax

        mov     ax, #0x0201
        mov     bx, word ptr [bp - 6]   ; bx = 0x000, 0x200, 0x400, ...
        xor     ch, ch
        mov     cl, byte ptr [bp - 8]   ; cl = target cluster to read
        xor     dx, dx
        int     0x13

        jc      load_cluster_carry      ; carry == 1 when error            
        xor     ax, ax
        inc     ax
        jmp     load_cluster_end
load_cluster_carry:
        xor   ax, ax
load_cluster_end:
        mov     word ptr [bp - 10], ax

And the function cannot read from sector 19. So I checked AH register and its value was 0x01.


Solution

  • mov     ax, #0x0201
    mov     bx, word ptr [bp - 6]   ; bx = 0x000, 0x200, 0x400, ...
    xor     ch, ch
    mov     cl, byte ptr [bp - 8]   ; cl = target cluster to read
    xor     dx, dx
    int     0x13
    

    The comment ; bx = 0x000, 0x200, 0x400, ... tells us that you want to read multiple sectors. From xor ch, ch (CH for Cylinder) and xor dx, dx (DH for Head), we can conclude that you seem to expect to find all of those sectors on the same Cylinder (0) and Head (0). This will not be the case: at some point you will have read all the available sectors on the current track and you will need to advance to the next track.

    The answer at How are all disk sectors iterated in assembly? has a working code that shows how to do that correctly. The code does not guess about the disk's geometry and asks BIOS for these values.

    Your xor dx, dx also zeroes the DL register that specifies the drive to use. Unless you are very sure that it needs to be 0, I would advice that instead you use the value that BIOS gave you when your bootloader got started. The drive id is passed to you in the DL register, and BTW it's the only information that BIOS provides when your bootloader begins.

    Extra trick

       jc      load_cluster_carry      ; carry == 1 when error            
       xor     ax, ax
       inc     ax
       jmp     load_cluster_end
    load_cluster_carry:
       xor   ax, ax
    load_cluster_end:
       mov     word ptr [bp - 10], ax
    

    You can replace the above by next simpler/cleaner code:

    cmc
    sbb  ax, ax
    neg  ax
    mov  [bp - 10], ax