1

I am learning MS-DOS source code during that i am in difficulty with some line of code:

BIOSSEG: EQU 40H
BIOSLEN: EQU 2048
DOSLEN: EQU 8192

In the next page there are some lines of code,

INIT:
XOR BP,BP ; Set up stack just 
          ; below I/O system
MOV SS,BP
MOV SP,BIOSSEG*16

If we put the value 0000 to a segment, then how can we put that segment just after another segment?

Clearly BP and SS will contain hex values 0000H.
I want to understand how MOV SS,BP will set up stack just below I/O system.

8
  • The BIOS Data Area (BDA) is what they call the "I/O System". It is generally between addtess 0x400 and 0x500 (on some ancient systems to 0x520). The thing about the code in question where this occurs - it doesn't put the stack below 0x400 (which is the Interrupt vector table from 0x000 to 0x400). It uses BP in memory references into the BDA using BP as a base pointer. In a MOV instruction using BP as a base pointer the default segment is SS (not DS). In essence SS:BP happens to point to the base of the BDA (what they call the IO system) and they use BP to index into the BDA (I/O system) Commented Jun 1, 2022 at 4:13
  • You should notice below the code in question there are a bunch of memory accesses with [BP+ ...] . They are initializing data in the BDA (IO System) via SS segment. Commented Jun 1, 2022 at 4:16
  • @MichaelPetch Then why would SP be set this way? Perhaps they do both: Keeping the stack at a 'valid' address at the higher interrupts, but also writing the BDA. Commented Jun 1, 2022 at 4:18
  • @Brendan : The code in MS-DOS where this occurs is running with interrupts off. This is old v1.x MSDOS initialization code. As to Sebasiian's comment I just noticed they do PUSH/POP so they do use the stack but only clobber the very top of the IVT and use it as a small scratch area. Commented Jun 1, 2022 at 4:28
  • 1
    Here is the code in question github.com/microsoft/MS-DOS/blob/master/v1.25/source/IO.ASM and a book/lecture notes, which in chapter 3 goes over this source code (but wrongly assumes the stack grows upwards): lacl.univ-paris12.fr/cegielski/msdos.html Commented Jun 1, 2022 at 4:51

1 Answer 1

3

Under the x86-16 architecture the segments are massively overlapping. They have a distance of 16 bytes (but a size of 64KiB). Each memory location can be addressed by 4096 combinations of segment address and offset.

The actual memory location can be calculated as

location = 16 * segment + offset

Putting 0 into SS generally makes the first 64KiB of memory accessible. Multiplying BIOSSEG with 16 and using it as the offset is the actual trick. It sets the offset address of the stack, which is stored in the stack pointer, to the beginning of the BIOS data segment. The stack extends downwards from there.

The start of the BIOS data is at memory location 400h = 16 * 40h + 0h.

So 40h:0h is equivalent to 0h:400h. But you can only use the second notification for the stack (of the two shown, you could e.g. also use 1h:3f0h or 10h:300h to the same effect) to get a positive offset address (for SP) and being able to access addresses downwards.

Normally the IVT (Interrupt Vector Table) is stored at the beginning of the memory. It contains 256 adresses (for the 256 interrupts) with 4 bytes each, taking 256 * 4 bytes = 400h.

So theoretically there would be no room for a stack.

Either the IVT is moved for this system in some way (if this is possible) or the higher interrupts are overwritten by the stack. The higher interrupts are only used by software, whereas the CPU and hardware typically use interrupts 0h to fh and 70h to 77h, making the upper 88h interrupts potentially overwritable, if not called by any software with the int instruction.

So the stack could have 4 bytes * 88h = 520 bytes.

Typically the 256 bytes beginning at 30h:0 are used at POST and early bootup for a stack, overwriting the interrupt vectors for interrupts C0h to FFh.

Alternatively hardware interrupts can be temporarily deactivated by a flag except the non-maskable interrupt (NMI), which is int 2h.

Sign up to request clarification or add additional context in comments.

3 Comments

The start of the BIOS data is at 0x00000, it's the end of the BIOS data that was at 0x0400. Note that the BIOS Data Area contains both the IVT and an assortment of BIOS data; and parts of the the IVT (corresponding to interrupts that BIOS doesn't use/support) are used or reserved for unrelated BIOS data. Back then (before slave PIC was added) it might've only been the first 128 bytes of the IVT that was actually used as an IVT (with the remaining 896 bytes of IVT used or reserved for completely unrelated BIOS data).
@Brendan According to some definitions the BDA contains the IVT. But I would definitely not be in agreement that the BDA ends at 0x400 / 40h:0h. There the interesting part begins.
Ah, you're right. I dug into how it was back then (1981, when some models only had 16 KiB of RAM, before XT existed) and BDA ended at 0x0500, but there was an "extended data segment" after that which contained a single "STATUS_BYTE"; so 0x0501 was the address of the first usable byte.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.