简体   繁体   中英

Assembly 8086- Pushing and popping registers from stack not working

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM