Author Topic: bootloader - stage 2  (Read 7187 times)

Offline Edy

  • Jr. Member
  • *
  • Posts: 3
bootloader - stage 2
« on: September 17, 2015, 09:13:42 PM »
Hi everyone,
I'm new here but i've visited many times this site for searching info about nasm. I'm relatively a nasm apprentice, but now I've the necessity to ask you a question (sorry for my bad english).

I've done a bootloader, a little one, only 512 bytes that read in memory the "second stage" with int 0x13 and do a jmp to that portion of memory to give control to my second code, but when I am in the second part of the booting process, I can't print any string or variable settled there.
I'm writing nasm under OS X and trying my bootloader on Virtual Box as floppy bootload ( *.img) creating with dd comand, in conjunction with "nasm -f bin"

Just a  few lines of codes:

Code: [Select]
bits 16
mov ax, 0x07C0
mov ds, ax
; i've tried also ORG 0x7C00, with ax=0

%macro print 1
pusha
mov si, word %1
a:
lodsb
or al, al
jz done
mov ah, 0x0E
int 0x10
jmp a
done:
popa
%endmacro


reset:
mov ax, 0
mov dl, 0
int 0x13
jc reset


read:
mov ax, 1000h     ; I'd like to load the second stage on this memory address
mov es, ax
mov bx, 0
mov ah, 2
mov al, 5
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, 0
int 0x13

mov ah, 0x0E
mov al, 'a'
int 0x10        ; print char just to see if all is going well
jc read

jmp 1000h:0000   ; just to make sure of the address
; then I end up with
times 510-($-$$) db 0
dw 0AA55h
; continuing on the same file ...

jmp boot2

%macro printa
...
; the same macro as before, with only different name labels otherwise compiler don't let me create correctly the bin file
...
%endmacro

boot2:
printa message

times 1024-($-$$) db 0    ; just to align the size of my bin file for getting in the virtual machine
I compiled this with nasm -f bin.

I didn't write where is my variable message intentionally, because when I write the string in the first 512 bytes, everything is okay, but when I write it on the "second stage" , being careful not to write it in the code flow but separately with jump, nothing works: it doesn't print nothing at all.
I think it's a memory address problem but I cannot figure out how to resolve it (if I can).

Sorry if I wasn't very clear, I hope I didn't make so many mistakes.

Thanks in advance.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: bootloader - stage 2
« Reply #1 on: September 17, 2015, 10:24:37 PM »
Hi Edy,

That looks good. As I see it, you've loaded "stage2" to 1000h:0000... but ds is still set to 07C0h, where the bios loaded the first stage. I think you may need to...
Code: [Select]
; ...
; continuing on the same file ...

jmp boot2

%macro printa
...
; the same macro as before, with only different name labels otherwise compiler don't let me create correctly the bin file
...
%endmacro

boot2:
mov ax, 1000h
mov ds, ax
; ...

...just to get ds pointed to where "stage2" lives. That may be enough to fix it... or maybe not. The segment will be 1000h, but the "offset" part of the "message" may be off by the length of the first part of the bootsector. This may be a disadvantage of assembling the two parts as one. The simplest way to get around that may be to assemble "stage2" separately (with "org 0" stated or by default) and:
Code: [Select]
bits 16
mov ax, 0x07C0
mov ds, ax
; i've tried also ORG 0x7C00, with ax=0

%macro print 1
pusha
mov si, word %1
a:
lodsb
or al, al
jz done
mov ah, 0x0E
int 0x10
jmp a
done:
popa
%endmacro


reset:
mov ax, 0
mov dl, 0
int 0x13
jc reset


read:
mov ax, 1000h     ; I'd like to load the second stage on this memory address
mov es, ax
mov bx, 0
mov ah, 2
mov al, 5
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, 0
int 0x13

mov ah, 0x0E
mov al, 'a'
int 0x10        ; print char just to see if all is going well
jc read

jmp 1000h:0000   ; just to make sure of the address
; then I end up with
times 510-($-$$) db 0
dw 0AA55h

incbin "stage2.bin"

or you could "cat" the two parts together, which should do the same thing.

We can't use more than one "org" per file, but we can get the same effect by creating another "section"...
Code: [Select]
; ...
; then I end up with
times 510-($-$$) db 0
dw 0AA55h
; continuing on the same file ...

section stage2 vstart=0
; same as starting with "org 0" here

jmp boot2
;...

I've mostly done it by assembling the two parts separately and using "cat" to combine them, but the other methods should give the same result. You will need to get ds pointed to the segment where you've loaded stage2, in any case, I'm pretty sure.

Best,
Frank


Offline Edy

  • Jr. Member
  • *
  • Posts: 3
Re: bootloader - stage 2
« Reply #2 on: September 18, 2015, 07:16:26 AM »
Hi Frank,

Thank you so much for your reply.

I was thinking too of the DS register, but in my ignorance I was thinking that after the jump it would be adjusted automatic to the new code... my fault.

I tried all of what you suggested to me, but most didn't work, I don't know why. The only thing that worked for me, is to create another file with stage2, compile it with -f bin, and then cat the two bins in one single file. I was thinking that incbin would do the same, but not for my system.

So far I am able to load the second stage, where ds is pointed to the memory address where I load it, and all works good (for now.. is just a working progress so maybe I'll encounter other difficulties) ... but I am happy for now, thanks!

One more question about "theory", hope not too silly.
You said that my segment was 1000h, but my offset of my string was not correct because of the length of the first part of the bootsector; but if we know this length (it is always of 512 bytes, isn't it?), can we arrange DS in the previous code for pointing to the right place? I mean doing something directly to the register.

And the last question I have.
I understood that the "org 0xYZ" statement is equivalent to get DS=( YZ / 16 ) because of the rule of memory address: segm:offset= segm*16 + offset for the physical address (am I wrong?).
In my "stage2", I wrote org 0 and DS=1000h for getting things work, but why I cannot write no org statement and only DS=100h (1000h / 16 ) as I did in stage1? Aren't the two processes equivalent?

Thanks in advance for your courtesy,

Best,
Edy

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: bootloader - stage 2
« Reply #3 on: September 18, 2015, 05:42:43 PM »
Hi Edy,

You are correct - we do know the length of "stage1" and ought to be able to correct ds to account for that. I have tried this - "all in one file" as you were doing it, but I'm having problems. I'm doing it on a "real floppy" and the int 0x13 read is failing - carry set. From memory, your code looks right. I may have to look it up to see what's going wrong. I'm getting old - not in full-fledged senile dementia (I don't think - how would I know?) but my brain isn't as sharp as it was (or as I thought it was). I may get back into this, but right now I'm going to take a break. If you're making any progress at all... carry on.

Best,
Frank


Offline Edy

  • Jr. Member
  • *
  • Posts: 3
Re: bootloader - stage 2
« Reply #4 on: September 24, 2015, 06:58:53 PM »
Hi Frank,

Thank you for your reply! I need a break too, I don't understand the difference between incbin and cat at all...
By the way, I'd like to know how you can debug your bootloader using a real floppy? I mean, if the code goes wrong your pc would crash I suppose ... and if it's crashed, how can you detect whether the carry flag is set or not? I'm so curious ...

I have another question to ask, I've done a little progress in my bootloader and now I was entertaining my self with the "graphic" (in 16 bit mode), in particular with this one:

Code: [Select]
mov ax, 4f02h
mov bx, 107h
int 0x10
I took the right informations from the ctyme.com for a list with all the video modes, and I was wondering how to write directly into memory (this mode enables 1280x1024 pixels with 256 colors. I'm really interesting in more colors, like 0x10d or 0x116, but just to begin...). I know that you have to get es pointing to 0A000h and then you can use di for writing in the right places the right things. But with only es:di I think you cannot write in all the video memory due to register's size, but for only a portion... here banks come... But what if I would want to use only register? I was thinking like this:
Code: [Select]
^0xA000   es=0xA000; di=0x00      this way is like to point to 0xA000:0x0000 (isn't it?)
|                               di=0x01
|                               di=0x02
|                                 .. .. ..
|                               di=0xFFFF   here I point to the 0xA000:0xFFFF address, the problem is that di is at its maximum range so why i cannot do this way? ->
|        ->  es=0x19FFF di=0x00
|                              di=0x01
|                               .. .. .. and so on?
|
|
v
Is possible to use es in this way? Maybe I have to enable 32 bit mode and entering into protecting mode?

Thanks in advance,

Best,
Edy

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: bootloader - stage 2
« Reply #5 on: September 24, 2015, 09:39:47 PM »
Hi Edy,
All this is making me realize how badly my memory has gone to bit-rot! I don't know why incbin should differ from cat. It should be about the same. I was trying to set ds to a value to do it all in one file, but it wasn't getting far enough to determine if it worked or not. I could tell that the int 13h was leaving carry-flag set by the fact that it was printing "a" repeatedly. I modified what you posted slightly. This is where I left off. I don't think it'll be much help to you, but here it is.
Code: [Select]
bits 16
mov ax, 0x07C0
mov ds, ax
; i've tried also ORG 0x7C00, with ax=0

%macro print 1
pusha
mov si, word %1
a:
lodsb
or al, al
jz done
mov ah, 0x0E
int 0x10
jmp a
done:
popa
%endmacro


reset:
mov ax, 0
mov dl, 0
int 0x13
jc reset

mov ah, 0x0E
mov al, 'a'
int 0x10        ; print char just to see if all is going well

read:
mov ax, 1000h     ; I'd like to load the second stage on this memory address
mov es, ax
mov bx, 0
mov ah, 2
mov al, 5
mov ch, 0
mov cl, 2
mov dh, 0
mov dl, 0
int 0x13

jc reset

jmp 1000h:0000   ; just to make sure of the address
; then I end up with
times 510-($-$$) db 0
dw 0AA55h
; continuing on the same file ...

jmp boot2


boot2:
mov ax, 1000h - 20h
mov ds, ax

;printa message
mov si, message
top:
lodsb
or al, al
jz done
mov ah, 0x0E
int 0x10
jmp top
done:

blackhole:
jmp blackhole

message db "welcome to stage2", 0

times 1024-($-$$) db 0    ; just to align the size of my bin file for getting in the virtual machine

My memory of those "hi res" graphics modes is real shaky, too. 0x19FFF isn't going to fit in es, as it exceeds 16 bits. This is an example of doing "bank switching". I think it used to work, although there's some "dead code" in it. Posted by a guy who went by "Garfield" - the cat in the cartoon, not the president, I think. It's a dos .com file, but should be easy to adapt to a stage2 boot(?).
Code: [Select]
;"TB" <NO_acr872k_SPAM@HERE_hotmail.com>
;Wed, 1 Jan 2003 16:31:00 -0500
;alt.lang.asm
org 100h

section .text
    mov ax, 0A000h
    mov es, ax

    mov ax, $4F02            ; 4F02H=SET VESA VIDEO MODE
    mov bx, $0107            ; 107H=1280x1024x256
    int $10                  ; VIDEO INTERRUPT

    call set_palette

    xor ecx, ecx
    mov bh, 1 ; cl ; $0F

LoopBG:
    inc ecx
    mov eax,ecx
    call pp2 ; PutPixel
    cmp ecx, 1280 * 1024   ;0FFFFh
    jne LoopBG

    mov ah, 10h
    int 16h
    cmp al, 1Bh
    jz egress

    xor ecx, ecx
;    cmp bh, 4
;    jnz top

;    mov bh, 3
    inc bh
    jmp short LoopBG

egress:
;    mov ah, 0
;    int 16h
    mov ax, 3
    int 10h
    ret


PutPixel:                    ; EAX=LONG ADDRESS BH=COLOR

    push eax ; push used register
    push ecx ; push argument
    push edx ; push used register

    push ebx ; push used register

    xor ebx, ebx ; clear extended part of ebx
    mov bx, $0FFFF ; divisor

    xor edx,edx  ; clear extended part of edx

    div ebx         ; divide: remainder (local bank addr) in edx and
                    ; quotient (bank number) in eax
    xchg edx, eax  ; exchange them because SwitchBank needs bank number in
                    ; edx
    call SwitchBank ; switch bank, bank number=dx

    mov di,ax  ; prepare offset
    mov ax,$0a000       ; prepare segment
    mov es,ax  ; set segment
    pop ebx  ; pop argument
    mov [es:di], byte bh ; draw!

    pop edx
    pop ecx
    pop eax

    ret

pp2:
    push eax
    pop ax
    pop dx
    mov di, ax
    mov al, bh
    cmp [cur_bank], dx
    jz bank_okay
    mov [cur_bank], dx
    call SwitchBank
bank_okay:
    stosb
    ret


SwitchBank:
    push ax
    push bx
    mov ax,4f05h
    xor bx,bx
    int $10
    pop bx
    pop ax
    ret

;---------------
set_palette:
    mov si, my_pal
    xor cx, cx
.top:
    mov dx, 3c8h
    mov al, cl
    out dx, al
    inc dx
    lodsb
    shr al, 2
    out dx, al
    lodsb
    shr al, 2
    out dx, al
    lodsb
    shr al, 2
    out dx, al
    inc cx
    cmp cx, 100h
    jnz .top
    ret

;--------------
section .data
    cur_bank dw 0

my_pal:
    db 0, 0 ,0
    db 0, 255, 0
    db 0, 0, 255
    db 255, 0, 0
    db 128, 128, 0
    db 128, 0, 128
    db 0, 128, 128
    db 128, 0, 0
    db 0, 128, 0
    db 0, 0, 128
    db 255, 128, 0
    db 255, 0, 128
    db 255, 128, 128
    db 128, 255, 128
    db 128, 128, 255
    db 240, 240, 0
    db 240, 0, 240
    db 240, 100, 240
    db 240, 100, 64
    db 240, 0, 64
    db 230, 64, 64
    db 230, 128, 64
    db 230, 64, 128
    db 230, 230, 64
    db 230, 64, 230
    db 230, 180, 64
    db 230, 64, 180
    db 230, 0, 180
    db 230, 0, 64
    db 230, 255, 0
    db 220, 255, 64
    db 200, 255, 64
    db 155, 255, 0
    db 128, 255, 64
    db 100, 255, 128
    db 180, 0, 0
    db 220, 220, 64
    db 180, 220, 0
    db 180, 255, 128
    db 180, 0, 128
    db 180, 128, 64
    db 240, 0, 128
    db 240, 128, 0
    db 220, 240, 160
    db 255, 140, 120
    db 220, 128, 255
    db 200, 64, 255
    db 200, 255, 64
    db 240, 255, 0
    db 100, 0, 255
    db 64, 64, 240
    db 255, 0, 140
    db 220, 180, 100
    db 240, 200, 180
    db 64, 255, 255
    db 100, 255, 100
    db 100, 100, 255
    db 230, 200, 0
    db 180, 255, 64
    db 0, 255, 240
    db 200, 255, 180
    db 64, 0, 0
    db 200, 0, 100
    db 0, 220, 100
    db 0, 64, 0
    db 64, 240, 64
    db 64, 255, 64
    db 0, 64, 100
    db 220, 120, 120
    db 64, 255, 255
    db 255, 240, 0
    db 0, 240, 30
    db 128, 255, 64
    db 240, 0, 128
    db 255, 120, 255
    db 120, 0, 255
    db 64, 128, 255
    db 100, 0, 64
    db 0, 64, 128
    db 220, 55, 155
    db 64, 255, 220
    db 0, 230, 200
    db 128, 64, 255
    db 240, 64, 0
    db 128, 64, 255
    db 0, 64, 85
    db 80, 100, 0
    db 220, 200, 60
    db 160, 64, 40
    db 100, 240, 0
    db 100, 64, 255
    db 40, 100, 40
    db 220, 40, 255
    db 60, 155, 55
    db 205, 55, 105
    db 155, 85, 55
    db 85, 85, 135
    db 175, 155, 75
    db 85, 45, 55
    db 205, 55, 25
    db 25, 155, 185
    db 55, 185, 135
    db 205, 25, 25
    db 195, 55, 35
    db 25, 205, 155
    db 85, 55, 145
    db 125, 55, 155
    db 55, 155, 25
    db 225, 25, 25
    db 205, 125, 55
    db 25, 215, 55
    db 25, 225, 25
    db 55, 25, 155
    db 100, 55, 205
    db 0, 0, 255
    db 0, 0, 245
    db 0, 0, 235
    db 0, 0, 225
    db 0, 0, 215
    db 0, 0, 205
    db 0, 0, 195
    db 0, 0, 185
    db 0, 0, 175
    db 0, 0, 165
    db 0, 0, 155
    db 0, 0, 145
    db 0, 0, 135
    db 0, 0, 125
    db 0, 0, 115
    db 0, 0, 105
    db 0, 0, 95
    db 0, 0, 85
    db 0, 0, 75
    db 0, 0, 65
    db 0, 0, 55
    db 0, 0, 45
    db 0, 0, 35
    db 0, 0, 25
    db 0, 255, 0
    db 0, 245, 0
    db 0, 235, 0
    db 0, 225, 0
    db 0, 215, 0
    db 0, 205, 0
    db 0, 195, 0
    db 0, 185, 0
    db 0, 175, 0
    db 0, 165, 0
    db 0, 155, 0
    db 0, 145, 0
    db 0, 135, 0
    db 0, 125, 0
    db 0, 115, 0
    db 0, 105, 0
    db 0, 95, 0
    db 0, 85, 0
    db 0, 75, 0
    db 0, 65, 0
    db 0, 55, 0
    db 0, 45, 0
    db 0, 35, 0
    db 0, 25, 0
    db 255, 0, 0
    db 245, 0, 0
    db 235, 0, 0
    db 225, 0, 0
    db 215, 0, 0
    db 205, 0, 0
    db 195, 0, 0
    db 185, 0, 0
    db 175, 0, 0
    db 165, 0, 0
    db 155, 0, 0
    db 145, 0, 0
    db 135, 0, 0
    db 125, 0, 0
    db 115, 0, 0
    db 105, 0, 0
    db 95, 0, 0
    db 85, 0, 0
    db 75, 0, 0
    db 65, 0, 0
    db 55, 0, 0
    db 45, 0, 0
    db 35, 0, 0
    db 25, 0, 0
    db 25, 25, 25
    db 45, 35, 35
    db 65, 45, 45
    db 85, 55, 55
    db 105, 65, 65
    db 125, 75, 75
    db 145, 85, 85
    db 165, 95, 95
    db 185, 105, 105
    db 205, 115, 115
    db 225, 125, 125
    db 245, 135, 135
    db 205, 145, 145
    db 165, 155, 155
    db 125, 165, 165
    db 85, 175, 175
    db 75, 185, 165
    db 65, 195, 155
    db 55, 205, 145
    db 45, 215, 135
    db 35, 225, 125
    db 25, 235, 115
    db 15, 245, 105
    db 25, 255, 95
    db 25, 255, 85
    db 25, 255, 75
    db 25, 255, 65
    db 25, 255, 55
    db 25, 255, 45
    db 25, 255, 35
    db 35, 255, 25
    db 45, 245, 35
    db 55, 235, 45
    db 65, 225, 55
    db 75, 215, 65
    db 85, 205, 75
    db 95, 195, 85
    db 105, 185, 95
    db 115, 175, 85
    db 125, 165, 75
    db 135, 155, 65
    db 145, 145, 55
    db 155, 135, 45
    db 165, 125, 35
    db 175, 115, 25
    db 185, 105, 25
    db 195, 95, 25
    db 205, 85, 25
    db 215, 75, 25
    db 225, 65, 25
    db 235, 55, 25
    db 245, 45, 25
    db 255, 35, 25
    db 255, 25, 35
    db 255, 25, 45
    db 255, 25, 55
    db 255, 25, 65
    db 255, 25, 75
    db 255, 25, 85
    db 255, 25, 95
    db 255, 25, 105
    db 255, 25, 125
    db 255, 25, 135
    db 255, 25, 145
    db 255, 25, 155
    db 255, 25, 175
    db 225, 25, 195
    db 205, 25, 225
    db 165, 25, 245
    db 255, 255, 255
One alternative to bank switching is a tweaked CPU mode called "voodoo mode" or "real big mode". Herman Dullink wrote some code in Tasm to do it, and I converted it to Nasm. I think I have a version that will work from a bootsector, but I'll have to look for it. Don't hesitate to remind me if I don't come up with it. Meantime, try the bank switching method.

Best,
Frank