Reputation: 129
I wrote a bootloader in assembly. Here is how it works:
First, the BIOS loads the bootloader as normal. It is instructed to go to 200h.
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
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
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