1

I'm playing around with making a bootloader to boot into an OS. When I'm using qemu, my binary loads but when I try to boot from a USB, I always get different results.

I use the following code to emulate my AMD ryzen 64 bit cpu:

qemu-system-x86_64 hello.bin

When I run this I get the output:

Hello world!
Goodbye world!

However, when I actually load this onto a USB and boot from here when I restart my PC I get just a blinking underscore:

_

Here is my assembly code that I am using

[org 0x7c00]
mov bx, HELLO_MSG
call print_string    
mov bx, NEWLINE
call print_string  
mov bx, GOODBYE_MSG
call print_string   
mov bx, NEWLINE
call print_string

jmp $

print_string:
    pusha
    mov ah, 0x0e        ; set ah to 0x0e so that int 0x10 will call BIOS teletype
    loop:
        mov al, [bx]    ;set al to character pointed by bx to print
        add bx, 1       ;increment to next char
        cmp al, 0       ;check if the char is NULL (null term string)
        je finish       
        int 0x10        ;call BIOS interrupt to print char to screen
        jmp loop
    finish:
        popa
        ret

HELLO_MSG:
db 'Hello world!', 0
GOODBYE_MSG:
db 'Goodbye world!', 0
NEWLINE:
db 0x0A,0x0D, 0

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

I followed some tips by other stack exchange posts and realized I should be setting up my segment registers so I added this to the top of my code:

[org 0x7c00]
mov ax, 0x00
mov es, ax
mov ss, ax
mov ds, ax
mov bp, 0x8000
mov sp, bp
cld

This seems to have helped a little bit because now when I boot using my USB I get the string Hello world! printed to the screen, but I do not get the second string. I am wondering if there's something I am missing in setting up the stack such that pusha and popa are messing something up to prevent the second call of print_string? Or maybe bx is loading the wrong address for the Goodbye world! portion? It doesn't make sense to my why the function would work the first time but not the second time.

It still confuses my why my code seems to work seamlessly on qemu but does not run as expected with my real CPU. Why did setting the segment registers to zero get the first call of the print_string working?

Solved:

For using a USB to boot on a real machine, I had to add the BIOS parameter block mentioned in Michael Petch's answer: stackoverflow.com/a/47320115/3857942

  • If using `org 0x7c00` you need to make sure the DS segment register is set to 0. You can do that with `xor ax,ax` `mov ds, ax` (I just noticed at the bottom you already did this). If booting with USB that is using Floppy disk emulation (FDD) then you will likely need a BIOS Parameter Block since the BIOS will likely overwrite some of your code with drive geometry. – Michael Petch Dec 17 '18 at 07:46
  • 1
    You can see this SO answer about the USB FDD emulation issue: https://stackoverflow.com/a/47320115/3857942 .I have general bootloader tips in this SO answer: https://stackoverflow.com/a/32705076/3857942 – Michael Petch Dec 17 '18 at 07:48
  • You should move `mov ss, ax` to just before the `mov sp, bp` .This is due to the fact that you want SS:SP updated without an interrupt occurring between the two. Interrupts get turned off for the during of the next instruction after updating the SS register. This isn't related to the problems that you are seeing. Just and FYI – Michael Petch Dec 17 '18 at 07:52
  • 1
    Thanks! I got it working now. Like you said, I need to make sure the segment register is set to 0. What really fixed it was adding the BIOS parameter block and that got the rest of the code working. – Christian Gabor Dec 17 '18 at 08:24

0 Answers0