2

I want to learn assembler to use the ultra-minimum bare code to get any x86 compatible machine running to display a hardcoded text on the screen on power on. I care about full compatibility with x86/IBM machines.

I followed tutorials on simple bootcodes and failed miserably to run them physically from my USB on my notebook. It was like my notebook wasn't even touching the MBR code. I spent two weeks reading tons of articles which led me nowhere. I learnt many things about BIOS Parameter Blocks, Partition entries, this old Phoenix BIOS specification, and UEFI.

My notebook boots correctly with my bootable FreeDOS USB. Here is the first sector of my FreeDOS USB with the MBR: Nice and full of real useful codey-code

I tried to boot it from a desktop computer with UEFI and it worked. I tried to boot it from a notebook with Phoenix BIOS and it worked.

Then I wiped ALL the boot instructions: Nice and full of NOTHING but one partition entry

I tried to boot it from the same desktop computer with UEFI and it failed as expected. I tried to boot it from a notebook with Phoenix BIOS and it worked. I swear.

I wanna know why is there a BIOS which takes matters on its own hands and skips the MBR instructions, if there are more like it and where can I find documentation on them so I can make my boot codes fully compatible with all BIOS.

The version of my notebook's BIOS is 1.08 and its EC version is 1.07. I don't want to update it. If FreeDOS boots fine with it, then I want my bootcode to boot fine with it too.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • I assume from what you are saying that you are booting this USB with Hard Disk Drive emulation (HDD) and not Floppy Disk Emulation (FDD). – Michael Petch Dec 18 '18 at 00:29
  • With HDD USB emulation some BIOSes will actually read the partition table from the first sector (MBR), find an active entry and then automatically load the first sector of the Volume (VBR) and start running it instead of the code in the MBR. It is likely with Freedos that the first sector of the partition is the actual code that gets run. This is just a wild guess. – Michael Petch Dec 18 '18 at 00:50
  • How BIOSes work is different among manufactures, and unfortunately the situation can be somewhat haphazard. One technique for increasing the chances of booting via USB HDD and having the code in the MBR run is to actually create a partition table where the active partition points back to the MBR itself rather than a volume boot record (VBR). – Michael Petch Dec 18 '18 at 00:56
  • Hi @MichaelPetch, you assume correctly. That BIOS can only emulate USB HDD, it isn't configurable. I also guessed that the VBR gets executed directly and also thought about fixing it with a jump to the MBR to maximize compatibility between BIOSs. But now I want to know if a list in a book or an article with all known BIOS booting behaviours exists, so I can make sure my code won't crash on the next machine I plug it in. How did FreeDOS devs knew those behaviours existed to make such a workaround? Or you think it was mere coincidence because of it being standard procedure to load first a VBR? – Antonio de las Carreras Dec 18 '18 at 01:17
  • When it comes to HDD media, people (including Freedos I believe) create a basic chainloading MBR (MBR bootstrap) that looks at the partition table, finds the active entry. It then copies itself (the 512 bytes at 0x0000:0x7c00) to low memory (often to 0x0000:0x0600). It then jumps to itself in the relocated memory. It then reads (using Int 13h) the Volume Boot record to memory address 0x0000:0x7c00. It sets DS:SI to the beginning of the active partition table that was being booted, sets DL to the bootdrive (passed by the BIOS) and then far JMPS to 0x0000:0x7c00 which is the VBR code. – Michael Petch Dec 18 '18 at 01:49
  • If you do this then if the BIOS reads the VBR of the active partition and jumps to it for you then the VBR is run directly. Otherwise your MBR discussed above is executed which loads the VBR from the active partition and transfers control to it. In both cases the main entry point is the VBR, and not the MBR for any of your kernel/OS related code. – Michael Petch Dec 18 '18 at 01:51
  • 1
    And no, this is not well documented all in one place and is based on info spread across the internet. http://osdev.org has some info on their Wiki pages and the forum but again not all consolidated in one place. It is why a lot of people use a bootloader like GRUB to take care of all these issues. – Michael Petch Dec 18 '18 at 01:54
  • Yeah, the osdev wiki is pretty swell but the closest BIOS issue it addresses regarding to booting problems is that it may perform "additional checks" besides checking for the 0x55AA (like checking for a valid partition table). But every source seemed to agree that sooner or later the MBR boot code was loaded and executed. GRUB rocks for loading OS but I just want to run a single assembly program, not a whole OS. Well I guess I'll keep going with that fix and hope I don't encounter another problem of the same nature. Thanks a lot @MichaelPetch for the help and attention. – Antonio de las Carreras Dec 18 '18 at 11:26

1 Answers1

1

This answer is based on experience and an educated guess. It is rather difficult to test without the actual hardware. I'm assuming that the OP is writing his boot sector to the MBR and not a VBR.


BIOSes have been created over the years that have been dumb and smart (some too smart IMHO). Some try to distinguish between media that might be a floppy disk drive or a hard drive based on the presence of a BIOS Parameter Block and/or a partition table with a bootable partition. Unfortunately this isn't standardized and many BIOS manufacturers only test their code against disks partitioned by Windows.

Some BIOSes that believe they see a valid partition table will assume HDD emulation and attempt to load the Volume Boot Record (VBR) from the active partition rather than execute the code on the MBR. I have a suspicion this is the case on the machine that seemed to still boot despite zeroing out the code and keeping a partition table. The code executed probably came directly from the VBR.

If using devices that are acting as hard drives (and not floppies) you can create an MBR with a partition table where the only active partition starts from the beginning of the drive (CHS=0,0,1 or LBA=0); is marked as bootable; and has a non-zero partition type. If you encounter a machine intent on loading a VBR directly then this method would trick it into loading the MBR as a VBR.

Example code that should boot on your USB as HDD media on the machines you seem to be testing could look like:

bits 16
org 0x7c00

boot_start:
    xor ax, ax                  ; DS=0 since we use ORG 0x7c00. 0x0000<<4+0x7c00=0x7c00
    mov ds, ax
    mov es, ax

    ; If you will be reading data into memory outside of 0x7c00 to 0x7dff
    ; then you want to set the stack SS:SP - uncomment these lines
    ; mov ss, ax                ; Stack at 0x0000:0x7c00
    ; mov sp, 0x7c00            ;     Just below bootloader

    cld                         ; Forward movement of string instructions
                                ;     (MOVSB, SCASB, etc)

    mov si, HelloWorldMsg       ; Print hello world
    call print_string

end_loop:                       ; Loop forever to terminate
    hlt
    jmp end_loop

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string:
    mov ah, 0x0e                ; BIOS tty Print
    xor bx, bx                  ; Set display page to 0 (BL)
    jmp .getch
.repeat:
    int 0x10                    ; print character
.getch:
    lodsb                       ; Get character from string
    test al,al                  ; Have we reached end of string?
    jnz .repeat                 ;     if not process next character
.end:
    ret

HelloWorldMsg:   db "Hello, world!", 0x0d, 0x0a, 0

times 446-($-$$) db 0   ; Pad with 0s up until first partition entry
part1_entry:
db 0x80                 ; 0x80 = Active boot partition, 0x00=inactive
db 0x00, 0x01, 0x00     ; CHS of first absolute sector (MBR) of hard drive
                        ;     Head=0, Sector=1, Cylinder=0
db 0x0c                 ; Partition type (has to be non-zero)
                        ;     0x0c = Win 95 FAT32 (LBA)
db 0x00, 0x01, 0x00     ; CHS of last absolute sector (MBR) of hard drive
                        ;     Head=0, Sector=1, Cylinder=0
                        ;     We are effectively saying Size of partition is 1 sector
dd 0x0                  ; LBA of first absolute sector (0=MBR)
dd 0x1                  ; Number of sectors in partition. We set it to 1 but if you
                        ;     wish you could set it to the number of sectors on the disk

times 510-($-$$) db 0   ; Pad remainder of boot sector up to boot signature. This zeroes
                        ;     partition entries 2,3,4 effectively making them inactive

dw 0xAA55               ; The standard PC boot signature after partition table

You can build the MBR with:

nasm -f bin boot.asm -o boot.bin

If successfully placed on the media this code should print:

Hello, world!

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • Hi again Michael, I want to let you know I got to run my assembly code by writing it to the VBR. But it wasn't so simple. In fact the reason it wasn't so simple is the same reason your code above still wouldn't work on my notebook. The reason is: My BIOS doesn't only check for a valid partition table, it also loads the first sector of the first active partition and checks for a VALID FS HEADER for the partition (too smart indeed >:C). Only THEN it deigns itself to run the boot code on the VBR (which is where I wrote my program) Src: [link](https://neosmart.net/wiki/mbr-boot-process/) – Antonio de las Carreras Dec 20 '18 at 13:00
  • In case you're interested: My program had to be loaded at memory address 0x7C5A (if I remember well), which meant I had to write "org 0x7C5A" first. Then I had to copy the compiled binary right after the FS header on my usb VBR and that was it. Got it running boiii :D – Antonio de las Carreras Dec 20 '18 at 13:10
  • @AntonioDelasCarreras That sounds like your BIOS also expects a BIOS Parameter Block as well as a partition table (if one tries to use a self referencing method). That is a real possibility, but I wanted to try some things through process of elimination to improve the answer. Can you download these two files from here: http://www.capp-sysware.com/misc/osdev/test1/ . Then assemble with `nasm -f bin boot.asm -o boot.bin` and try writing it to the MBR of your drive? Thanks – Michael Petch Dec 20 '18 at 14:23
  • Do not worry about the code in your answer. My question was not so much an "I want a solution to my specific problem." but a "I want to know where to find documentation on the complete range of BIOS behaviours there are out there". You already gave me the answer I wanted when you answered "This isn't standardized and many BIOS manufacturers only test their code against disks partitioned by Windows". (Plus one of your comments to my question: "this is not well documented all in one place and is based on info spread across the internet") – Antonio de las Carreras Dec 20 '18 at 15:14
  • @AntonioDelasCarreras :I understand that, I'd just like to know what happens for my own records. – Michael Petch Dec 20 '18 at 15:15
  • Oh, sure. I'll comment the results here once I run your code then. If some day you happen to find documentation on Phoenix BIOS v1.08, please share it with me too. – Antonio de las Carreras Dec 20 '18 at 15:25
  • And the results aaare... : It was completely ignored by the notebook: "Operating System not found". – Antonio de las Carreras Dec 20 '18 at 21:54