Star OS
Star OS

Reputation: 129

Bootloader not loading second sector

I wrote a bootloader in assembly. Here is how it works:

  1. First, the BIOS loads the bootloader as normal. It is instructed to go to 200h.

  2. At 200h, there is some code situating between 200h and 21Eh. It simply switches to VGA mode, draws a magenta pixel on the coordinates 1,1 , using VGA capabilities. It loops that code, forever.

However, when i load it, it just goes on the blinking cursor, which a normal VGA .bin file would NOT do, and would display a pixel. I checked for a pixel and i saw nothing. What i saw meant that the VGA code was not running, and the bootloader was simply loaded and nothing else.

Code of the bootloader:

 cli
 startload:
 push 0x00000200
 ret

 times 510-($-$$) db 0
 db 0x55
 db 0xAA

You can see it simply go to the next sector (starting at 200h) and the code at 200h-21Eh:

BITS 16
org 200h
data:
xcoord DB '1'
ycoord DB '1'
color DB 'D'
.code:
vga:
mov ax, 13h
int 10h
mov ax, ycoord
mov bx, xcoord
mov cx, 320
mul cx
add ax, bx
mov di, ax
mov dl, color
mov [es:di],dl
jmp vga

(Yes i do know this is not 230h bytes, that's the compiled output's size, which is 230h.)

What is the problem? Note: This is not discussing about how to make it go to the second sector. It's asking why it is not going there. I have not found any solutions.

Upvotes: 1

Views: 1804

Answers (2)

Michael Petch
Michael Petch

Reputation: 47603

In response to one of your questions. This code:

startload:
push 0x00000200
ret

Is pretty much the equivalent of a near absolute jump to CS:0x200. We don't know what the value in CS is, but many BIOSes will start with CS=0 and IP=0x7c00 (but that isn't always the case). You can't really rely on CS being a particular value unless you set it your self. In most cases CS is probably zero which means that you are likely jumping to physical memory address 0x00200 (0x0000:0x0200). This happens to be in the middle of the real mode interrupt table that runs from physical address 0x00000 to 0x003FF. Jumping to that location will likely result is some kind of undefined behavior.

You can load your bootloader into BOCHS which has a reasonable debugger that understands 16-bit real mode. You would be able to step through the code and determine exactly what CS is and where it jumps to.


What you are probably trying to accomplish can be done with the following code. This is a simple alteration of my previous Stackoverflow answer to a different question. To get an understanding of what this code is doing, please see my previous answer.

In a nutshell, the BIOS reads a single disk sector (512 bytes) from the disk starting at physical memory address 0x7C00. If you want other sectors to be loaded you must write code that loads them into memory, and then jump to that code after loading.

In this example the first stage is a bootloader that loads a single sector second stage from sector 2 of the disk (the sector right after the boot sector). In this example I am going to load the second sector right after the first at physical address 0x07e00 via segment:offset pair 0x7e0:0x0000 . (0x07e0<<4)+0x0000 = 0x07e00.

bootload.asm

bits 16
ORG 0x7c00      ; Bootloader starts at physical address 0x07c00

    ; At start bootloader sets DL to boot drive

    ; Since we specified an ORG(offset) of 0x7c00 we should make sure that
    ; Data Segment (DS) is set accordingly. The DS:Offset that would work
    ; in this case is DS=0 . That would map to segment:offset 0x0000:0x7c00
    ; which is physical memory address (0x0000<<4)+0x7c00 . We can't rely on
    ; DS being set to what we expect upon jumping to our code so we set it
    ; explicitly
    xor ax, ax
    mov ds, ax        ; DS=0

    cli               ; Turn off interrupts for SS:SP update
                      ; to avoid a problem with buggy 8088 CPUs
    mov ss, ax        ; SS = 0x0000
    mov sp, 0x7c00    ; SP = 0x7c00
                      ; We'll set the stack starting just below
                      ; where the bootloader is at 0x0:0x7c00. The
                      ; stack can be placed anywhere in usable and
                      ; unused RAM.
    sti               ; Turn interrupts back on

reset:                ; Resets floppy drive

    xor ax,ax         ; AH = 0 = Reset floppy disk
    int 0x13
    jc reset          ; If carry flag was set, try again

    mov ax,0x07e0     ; When we read the sector, we are going to read to
                      ;    address 0x07e0:0x0000 (phys address 0x07e00)
                      ;    right after the bootloader in memory
    mov es,ax         ; Set ES with 0x07e0
    xor bx,bx         ; Offset to read sector to
floppy:
    mov ah,0x2        ; 2 = Read floppy
    mov al,0x1        ; Reading one sector
    mov ch,0x0        ; Track(Cylinder) 1
    mov cl,0x2        ; Sector 2
    mov dh,0x0        ; Head 1
    int 0x13
    jc floppy         ; If carry flag was set, try again

    jmp 0x07e0:0x0000 ; Jump to 0x7e0:0x0000 setting CS to 0x07e0
                      ;    IP to 0 which is code in second stage
                      ;    (0x07e0<<4)+0x0000 = 0x07e00 physical address

times 510 - ($ - $$) db 0   ; Fill the rest of sector with 0
dw 0xAA55                   ; This is the boot signature

The second stage will be loaded by INT 13h/AH=2h right after the bootloader in memory, starting at physical address 0x07e00.

stage2.asm

BITS 16

; ORG needs to be set to the offset of the far jump used to
; reach us. Jump was 0x7e0:0x0000 so ORG = Offset = 0x0000.
ORG 0x0000

main:
    ; Set DS = CS
    mov ax, cs
    mov ds, ax

    ; Set to graphics mode 0x13 (320x200x256)
    mov ax, 13h
    int 10h

    ; Set ES to the VGA video segment at 0xA000
    mov ax, 0xa000
    mov es, ax

vga:
    ; Draw pixel in middle of screen
    mov ax, [ycoord]
    mov bx, [xcoord]
    mov cx, 320
    mul cx
    add ax, bx
    mov di, ax
    mov dl, [color]
    mov [es:di],dl

    ; Put processor in an endless loop
    cli
.endloop:
    hlt
    jmp .endloop

; Put Data after the code
xcoord DW 160
ycoord DW 100
color  DB 0x0D    ; One of the magenta shades in VGA palette

The code above is a slightly altered version of your VGA code, as yours had bugs. Your VGA code didn't properly set the ES segment to 0xA000 which is the start of the VGA graphics memory; it didn't properly de-reference the variables (you used their addresses and not the values);the size of the coordinates were a BYTE instead of a WORD; defined values in variables with ASCII character values. I also moved the data after the code.

I modified the code to draw a pixel in the middle of the 320x200 display and then loop infinitely to end the program.


On Linux (or on Windows with Chrysocome DD) using NASM to assemble, you could generate a 720K bootdisk with these commands:

dd if=/dev/zero of=disk.img bs=1024 count=720
nasm -f bin bootload.asm -o bootload.bin
dd if=bootload.bin of=disk.img conv=notrunc

nasm -f bin stage2.asm -o stage2.bin    
dd if=stage2.bin of=disk.img bs=512 seek=1 conv=notrunc

This builds bootload.bin and places it into the first sector of the disk image, and then builds stage2.bin and places it in sector 2 of the disk image. The disk image is called disk.img.

You should be able to test it with QEMU with something like:

qemu-system-i386 -fda disk.img

More information on DD usage can be found in one of my other Stackoverflow answers. Coincidentally that answer was for a question you had last year.

Upvotes: 5

David Hoelzer
David Hoelzer

Reputation: 16381

It appears that you are missing some pretty important code to actually read the second sector into memory. Something like this:

mov ah,0x02 ; read sectors into memory
mov al,0x10 ; number of sectors to read (16)
mov dl,[bootDrive]  ; drive number
mov ch,0    ; cylinder number
mov dh,0    ; head number
mov cl,2    ; starting sector number
mov bx,Main ; address to load to
int 0x13    ; call the interrupt routine

This assumes that you have saved the value of dl into a byte with label bootDrive. Obviously, you would need to change the address to which you load the code.

Using ORG 0x200 just configures the assembler to handle the generation of non-relocatable address references.

Upvotes: 2

Related Questions