I am a beginner and pushing and popping off the stack isn't working for me for some reason. My bootloader:
org 0x7c00
bits 16
jmp main
print:
pop bx
mov al, [bx]
mov ah, 0eh
int 10h
ret
main:
mov bx, msg
push bx
call print
cli
hlt
msg: db 'Hello World!', 0
times 510 - ($-$$) db 0
dw 0xAA55
What this should do is, I believe, push the address msg
onto the stack from bx
and then retrieve it into bx
. However, that does not seem to be the case. 'H'
does not get printed. A '-'
is printed instead. It works if I use msg
as the effective address.
Edit : As Duncan pointed out, the call
instruction was pushing the return address at the top of my stack which made the above program use that return address for the BIOS interrupt! I now pop
the return address into dx
then pop
into bx
, use the value of bx
and jmp
to dx
after I am done!
org 0x7c00
bits 16
jmp main
print:
pop dx
pop bx
mov al, [bx]
mov ah, 0eh
int 10h
jmp dx
main:
mov bx, msg
push bx
call print
cli
hlt
msg: db 'Hello World!', 0
times 510 - ($-$$) db 0
dw 0xAA55
The call
instruction pushes the program counter onto the stack, and ret
pops the top value from the stack and jumps to it.
So you cannot pass parameters to a function by pushing them before the call and popping them inside the call as the saved pc will get in the way.
Options might include setting up a frame pointer so you can access parameters which are still on the stack, and then pop them as part of the return.
Or for something this simple you could pop the return address into another register and instead of returning you just jump to it.
Start by jmp far 0:main
, to normalize cs:ip
, as some BIOS will call you with cs=0
, and some with cs=0x07c0
.
print:
pop bx
mov al, [bx]
mov ah, 0eh
int 10h
ret
Maybe you have seen something like this in shell exploits, but the correct usage then looks like this:
call print ; this will put `msg` at top of stack!
msg: db 'H'
print:
pop bx ; load `msg` address
... no RET !! (there's nowhere to return to)
That's a trick how to put correct offset into stack when you don't know where your code will be located (shell exploit lands usually into some buffer overflow area, with some randomized address).
That's not needed in bootloader, as you are at fixed position 0000:7C00
and you can assemble as that, so using some custom "pass argument value by register" calling convention for custom procedures like print
is fine, without involving stack at all (except using it to store return address by the call + ret
pair).
Also initialize the stack before using it, ie at the beginning of main you may want to do for example:
main:
; set ss:sp to 0000:7C00 (so you have about ~29kB of RAM for stack)
xor ax,ax
mov ss,ax ; disables interrupts for 1 more instruction
mov sp,0x7C00 ; so sp set must follow the `ss` setup
Don't use it for any deep recursion of course, 29kB is small stack (yet plenty for reasonably written bootloader, loading just some kernel from disk, actually 100-200B of stack should be enough for that).
And set up ds
of course! As you do mov al,[bx]
like the ds
was already set. In your case the easiest thing is to keep everything at 0000:7C00
, so ahead of mov ss,ax
you can do mov ds,ax
as well.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.