assemblyx86nasmx86-16disk

I'm writing a 16-bit real mode Assembly x86 routine to read the disk, but it doesn't work properly


I'm writing a 16-bit real mode Assembly x86 routine to read the disk, but it doesn't work properly. It is for my Operative System, if it can help.

Here's my code, you can paste, compile with NASM and emulate:

mov bl, dl

mov ah, 0x2
mov al, 0xa
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, bl

mov bx, 0x0
mov es, bx
mov bx, 0x7e00

int 0x13
jc __end

mov bx, 0x7e00

__loop:
    mov ah, 0x0e
    mov al, [bx]
    
    int 0x10
    
    cmp bx, 0x7e00+0xa
    je __end
    
    inc bx
    jmp __loop

__end:
    mov ah, 0x0e
    mov al, "!"
    int 0x10
    
    jmp $

    times 510 - ( $ - $$ ) db 0x0
    dw 0xaa55

times 2048 db 'A'

As you can see in the code, I am trying to read 10 sectors (0xa). To test this I wanted to print out the rode bytes at 0x7e00, and at the end an exclamation mark ! to notice the end of the function. In the first test it wrote me only a few blank spaces and my !. To be certain if all worked, I filled 4 sectors with As. I re-tried, and Bochs printed only a few As and so !. The output fits in a single line, to give you an idea. So it didn't seem like it was reading 10 sectors.

What am I missing? Thank you for any answer.


Solution

  • A matter of bytes

    The output fits in a single line, to give you an idea. So it didn't seem like it was reading 10 sectors.

    A bit of a hasty conclusion this is. There's always the possibility that the outputting itself went wrong, no matter where the data came from.

    And indeed that is the case, but contrary to what everybody involved till now (including you yourself) have said about you having printed 10 characters, I see that your endeavors were actually printing 11 characters! Not important you say? It is the famous one-off error, well-known to anyone that programs.

    mov bx, 0x7e00
    __loop:
      mov ah, 0x0e
      mov al, [bx]
      int 0x10
      cmp bx, 0x7e00+0xa
      je __end
      inc bx
      jmp __loop
    __end:
    

    By the time your loop sees the address 0x7E0A, the char at that particular address has already been printed. This was indeed the eleventh byte from the range 0x7E00 .. 0x7E0A.

    The solution lies in where you place that inc bx instruction. And if you do it right you will also have removed one of those jumps:

      mov  bx, 0x7E00
    __loop:
      mov  al, [bx]
      mov  ah, 0x0E     ; BIOS.Teletype
      int  0x10
      inc  bx
      cmp  bx, 0x7E00 + 0x000A
      jb   __loop       ; For as long as we stay BELOW 0x7E0A
    __end:
    

    A matter of sectors

    Now that the one-off error has gone, printing whole sectors is a breeze. Just apply a factor of 512 (size of a sector):

      mov  bx, 0x7E00
    __loop:
      mov  al, [bx]
      mov  ah, 0x0E     ; BIOS.Teletype
      int  0x10
      inc  bx
      cmp  bx, 0x7E00 + 0x000A * 512
      jb   __loop       ; For as long as we stay BELOW 0x9200
    __end: