Second-Stage Bootloader Bug

If you are needing help, or have questions for the different assembly languages available.

Moderator:Moderators

Post Reply
Richy
Posts:3
Joined:Thu Dec 11, 2008 4:24 pm
Second-Stage Bootloader Bug

Post by Richy » Thu Dec 11, 2008 5:00 pm

Hi. I'm building a second-stage bootloader. I want it to display a "hello world" message. My problem is that it doesn't. I've made two versions of the program. The first displays the message with some garbage characters before it, the second does not display it at all. Both programs are fairly simple and straightforward, so I can't see what's going wrong.

Here's the first version, which displays garbage before the Hello World message:

Code: Select all

; Update the segment registers
mov ax, cs
mov ds, ax
mov es, ax

HelloString db "Hello World",0
MOV SI, HelloString ;Store string pointer to SI

print:
LODSB		;AL=memory contents at DS:SI
OR AL, AL	;Check if value in AL is zero (end of string)
JZ loop 	;If end then return

MOV AH, 0x0E	;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00	;Page no.
MOV BL, 0x07	;Text attribute 0x07 is lightgrey font on black background
INT 0x10	;Call video interrupt
JMP print       ; Print next character

loop:
MOV AL, 'Z'	;I'll print a char to see that it ended
MOV AH, 0x0E	;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00	;Page no.
MOV BL, 0x07	;Text attribute 0x07 is lightgrey font on black background
INT 0x10	;Call video interrupt
JMP $ 		;Infinite loop
And here's the second version, that doesn't display the message at all (it does display the final 'Z' char though).

Code: Select all

JMP beginningofprogram

print:
LODSB		;AL=memory contents at DS:SI
OR AL, AL	;Check if value in AL is zero (end of string)
JZ loop 	;If end then return

MOV AH, 0x0E	;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00	;Page no.
MOV BL, 0x07	;Text attribute 0x07 is lightgrey font on black background
INT 0x10	;Call video interrupt
JMP print       ; Print next character


beginningofprogram:

; Update the segment registers
mov ax, cs
mov ds, ax
mov es, ax

MOV SI, HelloString ;Store string pointer to SI
JMP print       ; Print next character

loop:
MOV AL, 'Z'	;I'll print a char to see that it ended
MOV AH, 0x0E	;Tell BIOS that we need to print one charater on screen.
MOV BH, 0x00	;Page no.
MOV BL, 0x07	;Text attribute 0x07 is lightgrey font on black background
INT 0x10	;Call video interrupt
JMP $ 		;Infinite loop

HelloString db "Hello World",0
The first-stage bootloader itself is fairly simple. And it works: In both cases the program is loaded and displays the final Z character, and I've also used it to load another hello world program that displayed the message character by character without using strings and SI, and that one worked fine.

Can anyone pick out the bug in either one of these programs? Thanks!

Andyhhp
Moderator
Posts:387
Joined:Tue Oct 23, 2007 10:05 am
Location:127.0.0.1
Contact:

Post by Andyhhp » Fri Dec 12, 2008 12:46 am

Both of these errors are probably the same bug.

Is what you have posted your entire source code?
If not, please post.

If that is your entire source code, try putting

Code: Select all

org 0x0000
as the top line of code

~Andrew
Image

Richy
Posts:3
Joined:Thu Dec 11, 2008 4:24 pm

Post by Richy » Fri Dec 12, 2008 3:15 pm

I can't put in an org statement. I already have one in the first-stage bootloader. I'm assembling both the first and second stage into a single binary*, and NASM won't accept two org statements.

I've tried replacing the cs with the right memory segment value:

Code: Select all

mov ax, 1000h
Since that's where the first-stage bootloader loads the code to**.


*This version is meant to be the simplest possible two-stage bootloader, with no file system support, so I need to have both bootloaders in the same binary with the second one right after the first one, so that I know exactly where it is copied on the disk and I can load it with Int 13h.

**Here's the first-stage bootloader's code:

Code: Select all

    [BITS 16]
    [ORG 0x7C00]	

    ; Update the segment registers
    mov ax,  cs
    mov ds, ax
    mov es, ax

    reset:                      ; Reset the floppy drive
            mov ax, 0           ;
            mov dl, 0           ; Drive=0 (=A)
            int 13h             ;
            jc reset            ; ERROR => reset again

    read:
            mov ax, 1000h       ; ES:BX = 1000:0000
            mov es, ax          ;
            mov bx, 0           ;

            mov ah, 2           ; Load disk data to ES:BX
            mov al, 1          ; Load 1 sector
            mov ch, 0           ; Cylinder=0
            mov cl, 2           ; Sector=2
            mov dh, 0           ; Head=0
            mov dl, 0           ; Drive=0
            int 13h             ; Read!

            jc read             ; ERROR => Try again
            jmp 1000h:0000      ; Jump to the program

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

Andyhhp
Moderator
Posts:387
Joined:Tue Oct 23, 2007 10:05 am
Location:127.0.0.1
Contact:

Post by Andyhhp » Fri Dec 12, 2008 5:51 pm

That would be your problem.

If you are not loading the second stage bootloader to directly after the first stage one i.e. 0x7E00, then you cant have the source for each in the same file.

What is happening is this:

You set your segment registers up to all be 0x07C0. However, you also use [org 0x7C00] so the assembler adds 0x7C00 to the base of each of your lables. (this is the reason for the bug in the first stage loader that posted above)

After all that, when you get onto your second stage bootloader, you still have your labels acting up incorrectly.

Currently, this is what you have for your 2nd stage:
The code itself loaded at 1000:0000
The ORG of the code is 0x7C00 and following on from the 1st stage loader
Therefore, the value of the HelloString label is 0x7900 + the number of bytes that you use to print your message.

However, at the same time, your segment selectors are set to 0x1000 from the jump. Therefore, when you try to read from ds:si, you are trying to read from 0x1000:0x7900 (and a little bit) which is the linear address 0x17900 (+bit).


There are 2 solutions to your problem.

Either way, you will HAVE to split the 1st and 2nd stages into 2 separate files and assemble them separately.

As for your two options. You can either update the segment registers in the way you have and put the ORG at 0. This is the method I would recomend

Code: Select all

;First Stage Loader
[BITS 16]
[ORG 0x0000]   

;Update the segment registers
mov ax, cs
mov ds, ax
mov es, ax

Code: Select all

;Second Stage Loader
[BITS 16]
[ORG 0x0000]   

;Update the segment registers (jumped to 0x1000:0000)
mov ax, cs
mov ds, ax
mov es, ax

Or, you can put the ORG at the base address for that stage (first is 0x7C00 and second is 0x10000) and set all the segment registers except for cs to be 0. (if you also want to set cs to be 0 - dont! it will cause all your jumps to be going to the wrong location)

Code: Select all

;First Stage Loader
[BITS 16]
[ORG 0x7C00]   

;0 the segment registers
xor ds, ds
xor es, es

Code: Select all

;Second Stage Loader
[BITS 16]
[ORG 0x10000]   

;Update the segment registers (jumped to 0x1000:0000)
;0 the segment registers
xor ds, ds
xor es, es
Hope this helps,

~Andrew
Image

Post Reply