0

I'm playing with real mode bare metal programming using NASM and qemu. I'm trying to understand how stack works and in particular how it's stored in memory.

My code is in a 512 byte boot sector with "magic byte" 0xaa55 at the end and it is loaded at memory address 0x7c00.

I've set the stack base pointer at 0x7cfe. I'm aware that I have to pay attention to not override my code with the stack, but this is not the case. My code only occupies few bytes. This is my code:

[BITS 16]
org 0x7c00

mov bp, 0x7cfe
mov sp, bp

push 'A'

jmp $

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

The program sets the stack pointer, pushes the char 'A' into the stack and then it start and infinite loop. I assemble and tun it with the following command:

nasm -f bin boot.asm -o boot.bin
&& qemu-system-i386 -drive file=boot.bin,index=0,media=disk,format=raw -monitor stdio

While the emulator is running I dump the memory in the following way:

qemu> pmemsave 0x7c00 512 medump.bin

When I open the file memdump.bin I see the character 'A' (41 00 since is little endian) at the base address and before (of on top if we think in term of stack) I see a lot of "junk", i.e.:

200: 00 00 00 00 C5 EF 00 00 00 00 00 00 02 02 00 DF 00 00 FE F7
220: 00 00 FE 7C 00 00 BC 93 00 00 00 DF 00 00 60 F8 00 00 00 00
240: 00 00 55 AA 00 00 08 7C 00 00 02 02 41 00 00 00 00 00 00 00

What are those bytes?

If I do not set the stack pointer base all the bytes after the code and before the byte 0xaa55 are zeros.

Could you give any information or at least some pointers?

Thanks

EmarJ
  • 186
  • 1
  • 12
  • Can you show how you produce the binary file? – Margaret Bloom Sep 23 '16 at 12:30
  • @MargaretBloom I added more info – EmarJ Sep 23 '16 at 12:37
  • What about clearing the interrupt flag with `cli` at the very beginning and not setting it back? What I mean is: Everything below SP and down to the end of the stack is considered overwritable by anyone that need to. And ISRs need to. – Margaret Bloom Sep 23 '16 at 12:43
  • Not related to your question. You can move the value `0x7cfe` directly to _SP_ with. `mov sp, 0x7cfe`.It is when setting _SS_ that you need to use a register. And it is a very good idea to explicitly set the _SS_ segment when setting _SP_. They act as a pair *SS*:*SP* . It is possible for some environments (real hardware) to not have _SS_ set to 0. You seem to be relying on that. If you want stack to grow down from 0x0000:0x7cfe(physical address 0x07cfe) then set it that way. This would do `xor ax, ax` `mov ss, ax` `mov sp, 0x7cfe`. As to not restrict your code you could place it below 0x07c00 – Michael Petch Sep 23 '16 at 13:57
  • @MichaelPetch I've just started to learn these things, I need some time to fully understand your comment. It's not required to set the base pointer `bp`? Thanks – EmarJ Sep 23 '16 at 14:06
  • No, _BP_ is not required at all. _BP_ is often used by higher level languages as a stack frame but in 16-bit real mode code it is often used as a general purpose register that also happens to be useful in memory addressing. Setting its value buys you nothing unless you intend to use it. There are some BIOS routines that actually use it as a parameter. – Michael Petch Sep 23 '16 at 14:14
  • @MichaelPetch Thanks! – EmarJ Sep 23 '16 at 14:16

1 Answers1

3

In order to process interrupts the CPU need a properly set stack.
Just for serving the interrupt by passing control to the ISR (Interrupt Service Routing) the CPU pushes FLAGS, CS and IP.

The ISR then may, and usually do, need more stack.

The basic assumption behind the stack is that everything below SS:SP is considered free, available, memory.
When you set SP to 7cfeh you are just saying that every memory location below 7cfeh is free for other to use as stack memory by others.
That's the garbage you see.
Note that the addresses listed as 200, 220 and 240 are below the stack pointer, in my opinion using "on top" for such addresses, while figuratively correct, is a bit confusing.

If you want to ensure to be the only one messing with the stack, issue a cli as early as possible (and absolutely before setting the stack).
That suffices in an UP system.


Edit

Don't assume SS is zero, set it explicitly, initialize the stack with

mov ax, <ss_value>

;cli                    Decomment for very old 8088 compatibility

;Use these instructions in this order in pair
;Interrupts are disabled for 1 inst after mov ss, ...
mov ss, ax
mov sp, <sp_value>

;sti                    Decomment for very old 8088 compatibility
Margaret Bloom
  • 41,768
  • 5
  • 78
  • 124
  • It should probably be pointed out to the OP that by just setting _SP_ (like the OP did) is a pretty bad idea because you don't even know where _SS_ was. Can't assume it was 0. On real hardware it wasn't always the case. Always think it is a good idea to set SS:SP to something you know. – Michael Petch Sep 23 '16 at 13:43
  • @MichaelPetch Note that loading `ss` with `mov ...,ss` actually turns off interrupts for the next instruction. Thus you can do `mov ...,ss; mov ...,sp` just fine, though `lss ...,sp` might be useful, too. – fuz Sep 23 '16 at 14:03
  • @FUZxxl I know that and under the actual answer I use that technique in my comment without _CLI_. The only caveat is that there were a sizable quantity of physical 8088s with a bug where interrupts were not turned off until the end of the next instruction. So many real bootloaders use _CLI_ before setting the stack. As for _LSS_, it didn't exist as an instruction on the 8086/8088 (is 80386+) if you are targeting the lowest common denominator for a bootloader I'd avoid it. I have no idea what the intended target of this guys bootloader is. So usually easier not to make assumptions. – Michael Petch Sep 23 '16 at 14:08
  • @MargeretBloom I haven't studied interrupt yet, so I thought was only me touching the stack. I tried to insert a `cli` instruction and, even I don't know exactly what it does, seems working. Any reference to suggest to learn these things? – EmarJ Sep 23 '16 at 14:10
  • @MargaretBloom Another thing. You said that *everything below SP is considered free*, but in this case I have no access to memory below me, so my running code itself is in a "dangerous" memory place. If I was the only one touching the stack I could account for that, but in this case... – EmarJ Sep 23 '16 at 14:13
  • @EmarJ Your code starts at 0x7c00. Your stack grow down from 0x7cfe towards 0x7c00 (254 bytes separate them). It is potentially dangerous because your code and stack are so close. It won't take much as your bootloader code expands and you use the stack (especially for accessing BIOS interrupts) that your stack could end up overwriting your bootloader code. In your test bootloader you only push a 2-byte value on the stack, and you have almost no code so you are safe. As your bootloader expands just be aware you might run into issues. – Michael Petch Sep 23 '16 at 14:20
  • Thank you @MichaelPetch, I always forget that. – Margaret Bloom Sep 23 '16 at 14:21
  • @EmarJ You can try googling "8086 interrupts" to get a general idea, then switch to Intel manuals (see the x86 tag info on this site). – Margaret Bloom Sep 23 '16 at 14:21
  • 1
    Just as a reference to that 8088 defect (I lived through that era so know about it first hand), PCMAG in 1987 ran an [article on the issue](https://books.google.com/books?id=1L7PVOhfUIoC&pg=PA492&lpg=PA492&dq#v=onepage&q&f=false) with some code snippets. – Michael Petch Sep 23 '16 at 14:24
  • @MichaelPetch Thanks Michael for the reference. I'm aware of the fact that I can override my code with the stack, I was in fact experimenting how close I could stay. And now I understand that since I am not the only one using the stack I have to be really careful – EmarJ Sep 23 '16 at 14:54
  • @MargaretBloom I'm sorry for the spam, this will be last comment. I want to just say that if I use `cli` command I don't have the "junk" on the stack but if I use only the instruction to set `ss:sp` the junk is still there – EmarJ Sep 23 '16 at 14:56
  • @EmarJ : if you read my original comment under your question, I stated that what I was saying about SS:SP wasn't related to your question. I was suggesting a way to improve your bootloader. The reason you have junk on the stack is because you didn't turn off interrupts before changing SP. An interrupt occurred after that (possibly even an IRQ6 drive interrupt) pushing what it needed. It seems what happened was you pushed `A` it was placed on the stack,and while in your `jmp $` loop you got an interrupt, it used space below `A` and when finished the stack pointer was restored. – Michael Petch Sep 23 '16 at 15:22
  • @EmarJ When the stack is used (push, call etc) values that were pushed don't get set to zero when they are popped. So they appear as just garbage on the stack. On another side note not related to your question `push 'A'` isn't available on the 8088/8086. Pushing immediate values was introduced on the 80188/80186 (and later). If you want to run the code on a real 8088, 8086 you'd have to move the value 'A' to a register and push the register on the stack. If you are always going to use QEMU, by default it emulates a 386 so you are okay. If you were to find an ancient IBM-PC it wouldn't work. – Michael Petch Sep 23 '16 at 15:30