1

I am trying to write a simple bootloader in assembly. What I am trying to do is print a few strings, then wait for a keypress. This is my code:

[org 0x7c00]
mov al, 0
mov ah, 0
mov ax, 0
mov bx, 0
mov bp , 0x8000 ; Set the base of the stack a little above where BIOS
mov sp , bp ; loads our boot sector - so it won ’t overwrite us.
jmp main
message:
    db 0x0a,0x0d,'Enderbyte Programs',0x0a,0x0d,0
bmessage:
    db 0x0a,0x0d,'Booting...',0x0a,0x0d,0
cmessage:
    db 0x0a,0x0d,'Just kidding this does nothing ',0
kmessage:
    db 'Or does it?',0
print_string:
    mov al, [bx]
    inc bx
    cmp al, 0 
    jne print_char
    mov bx, 0
    ret
print_char: 
    int 0x10; Print AL register
    jmp print_string
main:
    mov ah, 0x0e ; int 10/ ah = 0 eh -> scrolling teletype BIOS routine
    mov bx, message
    call print_string
    mov bx, bmessage
    call print_string
    mov bx, cmessage,
    call print_string
    mov ah, 00h
    int 16h; Wait for keypress?
    mov ah, 0x0e
    mov bx, kmessage
    call print_string
    ;jmp $

This code works perfectly on VirtualBox, but when I try to run it on my PC (by copying it into a flash drive and booting from it), something very strange happens. The cursor is moved to the bottom-right area of the screen and prints off a strange character (An O but with an arrow coming off at the NE corner). Pressing a key causes it to clear the screen, the cursor jumps around before printing off another one of these characters. What is going on?

I have tried to follow the advice in Assembly boot loader working on virtual PC, not on real PC (setting all registers to zero, validating the stack) but it still did not work on the real hardware. (Still works fine on Virtualbox).

B0 00 B4 00 B8 00 00 BB 00 00 BD 00 80 89 EC EB
63 0A 0D 45 6E 64 65 72 62 79 74 65 20 50 72 6F
67 72 61 6D 73 0A 0D 00 0A 0D 42 6F 6F 74 69 6E
67 2E 2E 2E 0A 0D 00 0A 0D 4A 75 73 74 20 6B 69
64 64 69 6E 67 20 74 68 69 73 20 64 6F 65 73 20
6E 6F 74 68 69 6E 67 20 00 4F 72 20 64 6F 65 73
20 69 74 3F 00 8A 07 43 3C 00 75 04 BB 00 00 C3
CD 10 EB F1 B4 0E BB 11 7C E8 E9 FF BB 28 7C E8
E3 FF BB 37 7C E8 DD FF B4 00 CD 16 B4 0E BB 59
7C E8 D1 FF 25 00 00 00 00 00 00 00 00 00 00 00
*
00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 AA
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Enderbyte09
  • 409
  • 1
  • 5
  • 11
  • 2
    Did not read the full question. No segments initialization, no BPB -> Some old song. Keep searching on this site. – Margaret Bloom Oct 19 '22 at 15:21
  • Segment registers you use need to be set appropriately (in your case set to 0), see my bootloader tips: https://stackoverflow.com/a/32705076/3857942. If you are booting USB and the BIOS is set for Floppy Drive Emulation, more modern BIOses with legacy support blindly assume there is a BPB in the boot sector and will overwrite what it thinks is the drive geometry after being read into memory This of course can cause code not to work as expected. This is actually a common problem: https://stackoverflow.com/a/47320115/3857942 – Michael Petch Oct 19 '22 at 16:03
  • If booting as USB floppy add a BPB (one example is in the linked question in my previous comment (which includes a tool to see if your BIOS overwrites memory it thinks is a BPB). Your main boot code should begin with something like `xor ax, ax` `mov ds, ax` `mov ss, ax` `mov sp, 0x8000` . This sets DS to 0 and sets SS:SP to 0x0000:0x8000. Setting just SP is problematic because you can't guarantee SS is zero when reaching your code. – Michael Petch Oct 19 '22 at 16:36
  • @MichaelPetch: This should get closed as a duplicate of both of those possible duplicates. Either or both of those things need to get fixed. If it turns out that some other problem also exists, it can get reopened if necessary. Since you complained loudly last time this happened, I'm going to leave that up to you, but as asked it looks like a duplicate of those canonical Q&As, with nothing to suggest that a different answer would be useful. Therefore it should be closed; that's how Stack Overflow is designed to work. – Peter Cordes Oct 19 '22 at 16:47
  • I'd prefer leaving it open until the OP is given time to look into it and if a solution is found in one of the answers I'm more than happy to come back and mark this as a duplicate. The OP should be given a chance to respond and/or update the question or leave a comment about their findings. I generally come back a couple days later, if there is no response and ask again in the comments, and if time passes after that I may be inclined to close as a duplicate. But I think the OP is entitled to a chance to respond. – Michael Petch Oct 19 '22 at 17:51
  • @MichaelPetch: Your procedure is non-standard for SO. A response can happen after a question is closed as a duplicate. That doesn't disable comments. If you're worried about people abandoning questions because they're closed, maybe leave a comment explaining what to do in the unlikely event it wasn't a duplicate of the selected questions (i.e. edit with more debugging details and reply to the comment). Leaving near-certain duplicate questions open for longer doesn't benefit the site, it takes up people's time duplicating old effort. – Peter Cordes Oct 19 '22 at 22:56

1 Answers1

3

You should do a better setup and probably a somehow valid MBR. You cannot assume that BIOS set up the segments and registers for you. You have to initialize the most important things from the beginning. Maybe try something like:

This code assumes that your print_string is properly functional.

[ORG 0x7c00]
[BITS 16]

global boot

boot:
    jmp _start
    nop

    ; Here is a example BPB
    OEMname:           db    "mkfs.fat"  
    bytesPerSector:    dw    512
    sectPerCluster:    db    0
    reservedSectors:   dw    0
    numFAT:            db    0
    numRootDirEntries: dw    0
    numSectors:        dw    0
    mediaType:         db    0
    numFATsectors:     dw    0
    sectorsPerTrack:   dw    0
    numHeads:          dw    0
    numHiddenSectors:  dd    0
    numSectorsHuge:    dd    0
    driveNum:          db    0
    reserved:          db    0
    signature:         db    0
    volumeID:          dd    0
    volumeLabel:       db    "NO NAME    "
    fileSysType:       db    "FAT12   "

_start:
    cli
    xor ax, ax ; clear your segments
    mov es, ax
    mov ds, ax

    jmp 0x00:main ; clear code segment

main:
    sti
    mov bp , 0x8000 ; Set the base of the stack a little above where BIOS
    mov ss,  ax ; Setup Stack SS (AX=0) followed immediately by SP
    mov sp , bp 

    mov ah, 0x0e ; int 10/ ah = 0 eh -> scrolling teletype BIOS routine
    mov bx, message
    call print_string
    mov bx, bmessage
    call print_string
    mov bx, cmessage,
    call print_string
    mov ah, 00h
    int 16h; Wait for keypress?
    mov ah, 0x0e

    jmp $

print_string:
    mov al, [bx]
    inc bx
    cmp al, 0 
    jne print_char
    mov bx, 0
    ret
print_char: 
    int 0x10; Print AL register
    jmp print_string

message:
    db 0x0a,0x0d,"Enderbyte Programs",0x0a,0x0d,0
bmessage:
    db 0x0a,0x0d,"Booting...",0x0a,0x0d,0
cmessage:
    db 0x0a,0x0d,"Just kidding this does nothing ",0
kmessage:
    db "Or does it?",0

times 446 - ($ - $$) db 0x00 ; Padding to 512 bytes

; Partitions table for a MBR

partition_table_1:
    db 0x80 ; Status, 0x80 means active
    db 0x00 ; First Absolute Sector CHS
    db 0x00 ; 
    db 0x00 ;  
    db 0x00 ; Partition Type
    db 0x00 ; Last Absolute Sector CHS
    db 0x00 ; 
    db 0x00 ; 
    dd 0x00000001 ; First Absolute Sector LBA
    dd 0x00000200 ; Number of Sectors
    
partition_table_2:
    db 0x00 ; Status, 0x80 means active
    db 0x00 ; First Absolute Sector CHS
    db 0x00 ; 
    db 0x00 ;  
    db 0x00 ; Partition Type
    db 0x00 ; Last Absolute Sector CHS
    db 0x00 ; 
    db 0x00 ; 
    dd 0x00000000 ; First Absolute Sector LBA
    dd 0x00000000 ; Number of Sectors
    
partition_table_3:
    db 0x00 ; Status, 0x80 means active
    db 0x00 ; First Absolute Sector CHS
    db 0x00 ; 
    db 0x00 ;  
    db 0x00 ; Partition Type
    db 0x00 ; Last Absolute Sector CHS
    db 0x00 ; 
    db 0x00 ; 
    dd 0x00000000 ; First Absolute Sector LBA
    dd 0x00000000 ; Number of Sectors
    
partition_table_4:
    db 0x00 ; Status, 0x80 means active
    db 0x00 ; First Absolute Sector CHS
    db 0x00 ; 
    db 0x00 ;  
    db 0x00 ; Partition Type
    db 0x00 ; Last Absolute Sector CHS
    db 0x00 ; 
    db 0x00 ; 
    dd 0x00000000 ; First Absolute Sector LBA
    dd 0x00000000 ; Number of Sectors

dw 0xAA55 ; Boot signature required to boot

First, we cleared the segments to ensure that BIOS didn't give us junk values that will cause trouble later. We also cleared the code segments to make sure that we are in address 0x0000:0x7c00 instead of its equivalents such as 0x07c0:0x0000. I formatted the code, and move the data down so you can manipulate them easier. Your code also lacked padding and signature which were added. I also added some MBR partition tables in case you are booting from harddrive or equivalent system. You could also add a BPB, you can find the structure to that here:

https://wiki.osdev.org/FAT#BPB_.28BIOS_Parameter_Block.29

The above code can be compiled with

nasm -fbin bootloader.asm -o bootloader.bin

Write this to the first sector of a harddrive/USB/CD or equivalent.

Also check these out:

https://wiki.osdev.org/Bootloader

https://wiki.osdev.org/Rolling_Your_Own_Bootloader

https://wiki.osdev.org/MBR_(x86)

Edit:

The above code is compiled without errors and runs on QEMU.

Edit:

Also added a BPB

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Another serious problem that could be a problem is that the OP is booting off USB. If they are booting USB and the BIOS is set for Floppy Drive Emulation, more modern BIOses with legacy support blindly assume there is a BPB in the boot sector and will overwrite what it thinks is the drive geometry after being read into memory This of course can cause code not to work as expected. This is actually a common problem: https://stackoverflow.com/a/47320115/3857942 – Michael Petch Oct 19 '22 at 16:01
  • @MichaelPetch yeah maybe I should add a BPB to the answer. – Özgür Güzeldereli Oct 19 '22 at 16:05
  • @MichaelPetch Added a BPB. Also, does it actually matter what we write to the BPB? I have always left it all zeroes except for the names and bytes per sector. – Özgür Güzeldereli Oct 19 '22 at 16:11
  • I haven't heard of a situation (doesn't mean it doesn't exist) where a check is done by the BIOS for a BPB but there are some BIOSes that look for a jump instruction before the BPB and the 0xaa55 at the end of the sector to determine if it is bootable media. I generally fill in a BPB just in case that there is a BIOS that does something weird. BIOSes set to boot USB media as hard disk HDD media often times require a valid partition table with exactly one marked bootable for them to be detected as bootable media. – Michael Petch Oct 19 '22 at 17:48
  • 1
    Thank you so much! It now works perfectly both virtually and on real hardware! – Enderbyte09 Oct 19 '22 at 22:27
  • @MichaelPetch Would having them both necessarily have any negative side effects? As far as I know (incompletely), systems that look for a BPB won't look for a partition table and vice versa anyways. – Özgür Güzeldereli Oct 20 '22 at 05:18