1

I have a custom MBR that I compile with NASM to a binary and I can boot it just fine in qemu and bochs.

I am trying to boot this MBR in DosBox using boot mbr.bin. This works fine as long as the MBR is tiny and does not call int 13h to read sectors from the disk.

If I try to read the next sector, it fails, resulting in a disk read error of some sort. I imagine this has to do with the value of dl not being set to the "boot drive". I've tried manually setting dl to 0 and to 80h, but neither work.

Does anyone know what the value of dl should be to make int 13h work in DosBox using the boot command? Or if there is perhaps some other way to obtain that value dynamically?

Edit; here is the initial code:

CPU 386
BITS 16
org 7C00h

; Calculate the full size of the code from start of segment $$ to the end label at the bottom (end - $$)
%define SECTORS_TO_LOAD ((end - $$) / 512) + ((end - $$) % 512 > 0) - 1

main:
    jmp 0x0000:setcs
    ; Some error messages
    disk_err db "Error reading disk", 0
    sector_err db "Error reading sector", 0

setcs:
    ; Base setup

    cld

    xor ax, ax      ; Set ES=DS=0 - this can be changed later for rep operations
    mov ds, ax
    mov es, ax

    mov ss, ax
    mov sp, 0x7c00

    ; Clear the screen
    mov ah, 0x07    ; Function 0x07: scroll window
    mov al, 0x00    ; Clear entire window
    mov bh, 0x07    ; White on black
    mov cx, 0x00    ; Specifies top left of screen as (0,0)
    mov dh, 0x18    ; 18h = 24 rows of chars
    mov dl, 0x4f    ; 4fh = 79 cols of chars
    int 0x10    ; Video interrupt


    ; Move cursor to position 0,0
    mov ah, 0x02    ; Function 0x02: set cursor position
    mov dh, 0x00    ; Row 0
    mov dl, 0x00    ; Col 0
    mov bh, 0x00    ; Page 0
    int 0x10    ; Video interrupt


    ; Load the calculated numbers of sector from the disk
    ; For this function call, dl should be set to the disk number to read from. The BIOS sets dl to
    ; the disk number of the MBR and dl was not changed, so we don't have to set it.

    ;mov dl, 0x80

    mov ax, SECTORS_TO_LOAD ; Number of sectors to read
    mov bx, next_sector ; The memory address to read to
    xor ch, ch      ; Cylinder 0
    mov cl, 0x02        ; Start at sector 2 (sector 1 is the first part of the MBR)
    xor dh, dh      ; Head 0
    mov ah, 0x02        ; Read mode

    int 0x13        ; Read interrupt
    jc .disk_error      ; Check carry bit for error

    cmp al, SECTORS_TO_LOAD ; The interrupt sets 'al' to the number of sectors actually read
    jne .sectors_error

    ; After a succesful load, the layout in memory will be the same as the layout in our ASM files,
    ; so we can jump to a defined label without worrying about offsets and exact addresses.
    jmp after_load

.disk_error:
    push disk_err   ; Print a disk error message
    call print
    cli     ; Halt all interrupts and operations
    hlt

.sectors_error:
    push sector_err ; Same as above, different message
    call print
    cli
    hlt


; The print function prints a string onto the screen in text mode
print:
    push bp     ; Basic stack setup
    mov bp, sp
    pusha       ; Push everything - this function doesn't
            ; return anything

    mov si, [bp+4]  ; Grab the pointer to the data
    mov bh, 0   ; Page 0
    mov bl, 0   ; Foreground color, irrelevant - in text mode
    mov ah, 0x0E    ; Function 0x0E: print character in TTY
.char:
    mov al, [si]    ; Get current char from pointer position
    inc si      ; Keep incrementing si until a null char
    or al, 0
    je .return
    int 0x10    ; Video interrupt
    jmp .char
.return:
    popa        ; Restore registers
    mov sp, bp  ; Restore stack
    pop bp
    ret 2       ; Remove param from stack on return

times 510-($-$$) db 0

; MBR signature
db 55h, 0AAh

next_sector:

After that, under next_sector, it has some incbins and some other code, including the label after_load.

The error I get is the first one; "Error reading disk", which displays fine. So it seems that es, ds, etc. are set up right.

Note the commented out mov dl, 0x80 - that was one of my attempts to get this working.

Gerard
  • 107
  • 1
  • 7
  • dosbox seems to load `dl` with zero and set drive to `A` by default. Apparently you can specify a drive with `-l` but that won't reflect in `dl`. – Jester Jun 03 '21 at 14:35
  • @Jester yeah I already found the `-l` option, but that indeed does nothing it seems. – Gerard Jun 03 '21 at 14:46
  • 2
    What values do your other registers have. Perhaps the error is unrelated to the drive? – Sebastian Jun 03 '21 at 15:04
  • @Sebastian I've amended the question with some more code details, because posting that as a comment means I have no formatting. – Gerard Jun 03 '21 at 15:20
  • Probably not related to your question but `((end - $$) / 512) + ((end - $$) % 512 > 0) - 1` doesn't seem right. The first part correctly gets the amount of 512-byte pages. Then `% 512` is nonzero if a partial page is present. Then `> 0` returns 1 if any is present, 0 if none. Then you subtract `- 1` which is wrong. And an easier way, in my opinion, to convert nonzero values to 1 and zero values to 0 is double `!`. So `((end - $$) / 512) + !! ((end - $$) % 512)` – ecm Jun 03 '21 at 16:22
  • 1
    @ecm thanks for the tip. This does work out though, since the number of sectors I want to load is the total number of sectors - 1 (which is already loaded by the BIOS). I have not run into any problems using this method in Qemu or Bochs. Everything is loaded fine. Your method looks much cleaner, though, I'll play around with it :) – Gerard Jun 03 '21 at 16:27
  • You can also achieve the same effect by "rounding up" the integer division. That looks like `(end - $$ + 511) / 512` – ecm Jun 03 '21 at 16:29
  • 2
    Oh I missed that. Yes, your `end - $$` includes the first sector. So the minus one is actually justified. Sorry for the wrong nitpicking. – ecm Jun 03 '21 at 16:30

1 Answers1

3

Getting error "BIOS:Disk 0 is not active" in asm program

DOSBox uses the image size to determine the disk geometry.

Pad the bin file to 1440*1024 bytes.

Wot
  • 102
  • 11