2

I'm writing a tiny kernel for educational purposes and I was thinking of a good way to set up my stack in real-mode. I read from here: http://wiki.osdev.org/Memory_Map_%28x86%29 that addresses 0x07E00 to 0x7FFFF were guaranteed to be free for use.

So if I set the stack segment to 0x0900, that would give the stack a range from 0x0900 << 4 to 0x0900 << 4 + 0xFFFF which is 0x09000 to 0x18FFF which means I would get a safe 0x10000 bytes of stack space, right?

And I could just set my stack pointer to 0, and it would "underflow" to 0xFFFE at the next push too? Segmentation is a little confusing to me.

This is how the relevant part of the code looks:

BITS 16
ORG 0x0000

mov ax, 0x07C0
mov ds, ax          ; ds = origin of code

mov ax, 0xB800
mov es, ax          ; es = origin of video memory

mov ax, 0x0900
mov ss, ax

mov ax, 0
mov sp, ax
Matthew
  • 268
  • 1
  • 11
  • 2
    0x900 (but segments are added to the offset after they're shifted left four times which would make the physical address of the origin 0x9000 + 0) – Matthew Dec 25 '15 at 09:54
  • 1
    The calculation from segment:offset (The stack is SS:SP) to physical address is `(segment<<4)+offset=physical address` which is same as `(segment*16)+offset=physical address` SS:SP of 0x0900:0x0000 = physical address 0x09000 .The stack would range from 0x0900:0x0000 to 0x0900:0xFFFF which is physical addresses (0x0900<<4)+0=0x09000 to (0x0900<<4)+0xFFFF= 0x18FFF. You are completely safe as long as you don't intend to place anything between physicall address 0x09000 and 0x18FFF – Michael Petch Dec 25 '15 at 16:08
  • 3
    My general experience with real BIOSes over the past few decades is that free memory is safe between 0x00500 to 0x07BFF and from 0x07E00 to 0x9EFFF On many BIOSes and chipsets the Extended BIOS Data Area (EBDA) runs just under 0xA0000. Its size often gets misreported by some BIOSes but is usually 0k to 4k in size. So a safe starting point at the top of conventional memory would be 4k below 0xA0000 which is how I arrived at 0x07E00 to 0x9EFFF . So SS:SP of 0x9000:0xF000 would be fine. If you need a full 64k below most worst case EBDA sizes you could use SS:SP=0x8F00:0x0000 – Michael Petch Dec 25 '15 at 16:42
  • 1
    Something you should be aware of when setting up any SS:SP is that with interrupts on you should do the move to _SS_ and the move to _SP_ in two consecutive instructions. When updating _SS_ the CPU is suppose to disable interrupts until the end of the _NEXT_ instruction. So usually the next instruction is the update of _SP_ Your code could have looked like `mov ax, 0x0900` `mov ss, ax` `xor sp, sp` (xor a value to itself will yield 0). If you want to do it the way you are doing it then you should explicitly turn off interrupts around your update of _SS_/_SP_ with _CLI_ and _STI_ – Michael Petch Dec 25 '15 at 16:51
  • Okay thank you, I'm probably not going to need that much stack space and set the SS to 0x0050. I'll probably clear the interrupt flag or do as you said (so much to learn) to be safe but that leads me to another question, some people push and pop words to place them in registers (I did that) before the stack segment is even set up (I remember you saying we shouldn't rely on any register except DL and the physical address of CS:IP to be setup correctly) so why do people do that? – Matthew Dec 25 '15 at 21:14
  • The people who push and pop without setting up a stack (or do such instructions before setting one up) run the risk of a stack being in a bad state, may not be large enough, and more importantly you don't really know where the BIOS placed it's stack before jumping to our code. Imagine if we chose to read data into a memory location where the BIOS had set up a stack, and we had used that stack for pushing data. The Stack data would be overwritten. Even if you don't push and pop, calling many BIOS interrupts require a properly set up stack (some BIOSes use their own stack, but some do not) – Michael Petch Dec 25 '15 at 23:18
  • So it's best to set segment registers by loading the value in *ax* then moving it into the appropriate segment. Alright thank you once again. – Matthew Dec 25 '15 at 23:25
  • You can use any available non segment register (preferably not _DX_ unless you save the _DL_ value first). But yes, if you want to create a well behaved bootloader with the best chance of running on a wide variety of hardware, it is good to set everything up manually. Setting up the stack first before using stack related instructions prevents unnecessary problems. – Michael Petch Dec 25 '15 at 23:28
  • You should also avoid interrupts without a valid stack set up, @MichaelPetch. Though I guess you meant that too when mentioned "stack related instructions". – edmz Dec 26 '15 at 11:26
  • @black, I was vague, but enter,leave,push, pop,int all,iret(pusha,popa on later processors),or any instruction that takes a memory operand that uses the SS segment override or SP. Anything that would potentially places data on the stack or remove data to be used would qualify. – Michael Petch Dec 26 '15 at 16:53
  • My use of an "bad state" is also ambiguous. "Bad" is relative to the needs of our bootloader. We don't know if it is in a memory region that is large enough to service our potential requests and usage (Like placed just above the interrupt vector table and the BDA) near the bottom of memory. The _SS:SP_ will generally be large enough to handle hardware interrupts that the BIOS may service directly but we really don't know if it will suit OUR needs. For these reasons it is best to set up our own. – Michael Petch Dec 26 '15 at 17:07

0 Answers0