2

I have been attempting to test a simple bootloader on my computer (i.e. physical machine) for a while, and I've run into some very odd behavior.

My bootloader is in a file named "boot.asm" and contains the following code:

[ORG 0x7c00]

start:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov sp, 0x9000

    mov si, mesg1
    call print

    mov ax, 0x0201      ;ah=0x02, al=0x01
    mov bx, 0x5000      ;address = 0x5000 (I have also attempted this at 0x8000)
    mov cx, 0x0002      ;ch=0x00, cl=0x02
    mov dh, 0x0
    int 0x13

    mov si, mesg2
    call print

    jmp $

print:
    pusha
    mov ah, 0xe
    xor bl, bl
.loop:
    lodsb
    test al, al
    jz .end
    int 0x10
    jmp .loop
.end:
    popa
    ret

mesg1 db "Msg1", 0x0
mesg2 db "Tst1", 0x0

times 510 - ($ - $$) db 0x0
db 0x55, 0xaa

I compile it with a makefile that contains the following commands (kernel.asm contains nothing but junk and was only added for earlier testing):

nasm -f bin -o bin/boot.bin src/boot.asm
nasm -f bin -o bin/kernel.bin src/kernel.asm
cat bin/boot.bin bin/kernel.bin > boot.img

When I boot "boot.img" as a floppy in Virtualbox, I get the expected output on screen:

Msg1Tst1

The problem occurs when I attempt to run this on my physical computer. I copy the boot image to my flash drive with the following command:

sudo dd if=boot.img of=/dev/sdb

I then reboot my computer, and I get the most bizarre output:

Msg1Msg1

I can't figure out what's going on no matter how hard I try. After a little tinkering, I discovered that if I move the code,

mov si, mesg2
call print

before the read operation, it will give the expected output. Can somebody explain to me what I'm doing wrong? I've been at this for a day, and I can't figure out the problem.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
DrCereal
  • 85
  • 8
  • 2
    You should use `xor ax, ax` not `mov ax, cs` at the beginning to set the segments to zero because you used `org 0x7c00`. – Jester Nov 25 '18 at 23:48
  • I have edited my post, but the cs is set to 0 on my machine anyway so it doesn't affect the problem. – DrCereal Nov 25 '18 at 23:50
  • 3
    Kindly do not edit the code in your question. It is really hard to tell what is wrong with a piece of code if it constantly changes. If there are any changes you found to make a difference, add a section to your question or just leave a comment. But do not change the code around. Also, I bet you haven't tested the code with the change made to see if it still yields the same error. – fuz Nov 25 '18 at 23:51
  • 1
    Or to sum it up: your question should show the defective code, not the code after you fixed all the issues. – fuz Nov 25 '18 at 23:52
  • 1
    Your code assumes that DF=0. I've read that some real BIOSes don't do that. – Peter Cordes Nov 26 '18 at 00:21
  • @PeterCordes I modified the print function to clear the DF after `pusha`, but unfortunately, it did not solve the problem. @Jester, earlier, I also modified the code to use `xor ax, ax`, and the problem still occurred. (I have reverted both changes to ensure consistency with this question.) – DrCereal Nov 26 '18 at 00:32
  • Did you try with *both* changes at once? They're both necessary for portable correctness. (But neither of them would cause the `Msg1Msg1` output you describe, and I can't see anything that would.) What is that `int 13h` call doing? You call it a "read"; are you sure it's not overwriting your code in memory with another copy of the boot sector, offset slightly? Or overwriting the code? You didn't comment that at all, and you say that taking it out or moving it later fixes the problem. So presumably your real machine is loading our code at a different linear address than VBox. – Peter Cordes Nov 26 '18 at 00:52
  • @PeterCordes Yes, I had applied both changes at once, and there was no change in the output. `int 0x13` with `ah=0x02` is reading the second sector of the drive BIOS loads the bootsector from, my usb, into the address 0x0000:0x5000. I initially set the BX register to 0x8000 so, unless BIOS is ignoring the value I give it entirely (which I admittedly don't know), the location I'm reading into memory shouldn't be an issue. – DrCereal Nov 26 '18 at 01:05
  • 4
    If you are booting from USB and using USB Floppy Disk emulation you likely need a BIOS Parameter Block since it is very likely the BIOS is overwriting this area at the beginning of the bootloader with drive geometry.. This is done by many BIOSes after your bootloader is loaded in memory and before control is transferred to it. This can cause the code to run in abnormal ways. You may wish to look at this SO Answer I wrote: https://stackoverflow.com/a/47320115/3857942 – Michael Petch Nov 26 '18 at 01:18
  • Oh right, I had it backwards. The BIOS is required to load your code at linear address `0x7c00`, but can set CS:IP to `00:7c00` or `7c0:0`. I was mixing it up and thinking that maybe the MBR could be loaded somewhere that overlaps with your load destination, but that's not actually possible. `0x5000` is low enough, and `0x8000` is high enough, that I don't think that can be the problem. – Peter Cordes Nov 26 '18 at 01:18
  • 1
    @MichaelPetch You have correctly pointed out the problem I was having, and I managed to add padding to the code that corrects the problem. (In retrospect, the problem makes a lot more sense now.) Thank you. (EDIT: I'm just dumb. Don't mind the original comment.) – DrCereal Nov 26 '18 at 02:15

0 Answers0