一、8086内存地址表

起始地址 结束地址 大小 作用
0x00000 0x003FF 1KB 中断向量表
0x00400 0x004FF 256B BIOS数据区
0x00500 0x07BFF 30464B约30KB 可用区域
0x07C00 0x07DFF 512B MBR被BIOS加载到此处,共512个字节
0x07E00 0x9FBFF 622080B约608KB 可用区域
0x9FC00 0x9FFFF 1KB 拓展BIOS数据区
0xA0000 0xAFFFF 64KB 用于彩色显示适配器
0xB0000 0xB7FFF 32KB 用于黑白显示适配器
0xB8000 0xBFFFF 32KB 用于文本模式显示适配器
0xC0000 0xC7FFF 32KB 显示适配器BIOS
0xC8000 0xEFFFF 160KB 映射硬件适配器的ROM或内存映射式I/O
0xF0000 0xFFFEF 64KB-16B 系统BIOS范围是0xF0000~0xFFFFF共64KB,为了说明入口地址,将最上面的16个字节从此处去掉了,所以此处终止地址为0xFFFEF
0xFFFF0 0xFFFFF 16B BIOS入口地址。 此处16个字节的内容是跳转指令:jmp0xF000:E05B

二、写代码 - 根据可用区域使用桟

从上表可以看出,0x00500 - 0x07BFF 这个可用区域比较大,我们就选这一段,作为桟的位置

我们设置

  • 桟段地址:0x0000
  • 偏移地址:0xFFFF

因为“栈”这块内存需要经常使用,8086计算机工程师为了让我们更方便的管理“栈”这段内存。
为我们设计提供了“三个”专门负责管理“”的寄存器。

  1. SS=Stack Segment 栈段寄存器
  2. SP=Stack Pointer 栈顶指针寄存器
  3. BP=Base Pointer 栈底指针寄存器
;桟定义到0x0FFFF
mov ax, 0x0000
mov ss, ax

;桟底 ss:bp 合成20位内存地址:0x0FFFF
mov bp, 0xFFFF


;栈顶 ss:sp 合成20位内存地址:0x0FFFF
mov sp, 0xFFFF

;寄存器里存一些数据
mov ax, 0x1234
mov bx, 0x2345
mov cx, 0x3456
mov dx, 0x4567


;桟是数据临时的“中转站”
push ax
push bx
push cx
push dx

;出栈,先进后出
pop dx ;出栈后存到dx
pop cx
pop bx
pop ax

End:
jmp near End

times 510 - ($ -$$) db 0x00
dw 0xAA55 ; 相当于 db 0x55,0xAA

push ax 中的 push 指令相当于做了如下工作:

sub sp, 2
mov word[ss:sp], ax

pop ax 中的 pop 指令相当于做了如下工作:

mov dx, word[ss:sp]
add sp, 2

三、运行结果查看

桟:先进后出,后进先出

观察运行结果,桟底在内存地址高位,栈顶在内存地址低位
每次运行 push 指令,栈顶 sp 都会减 2

虽然出栈的时候我们取出数据,但是桟内存地址里面的数据其实还在,只是桟指针地址已经指向下一个桟地址了

不要把栈想象的有多神秘,就是用 ss,sp,bp,以及 pushpop 指令,管理控制读写的一块内存区域而已。
一般我们要把一些数据临时放到某个内存的时候,就会考虑放到“栈”里面


四、可以自己模拟桟

如果没有SS段寄存器,也没有SP和BP寄存器,也没有push和pop这些汇编指令,就用我们之前学过的知识,我们自己也可以模拟出一个栈来实现。
我们使用:

  • ds 作为栈段。
  • si 寄存器作为栈底。
  • di 寄存器作为栈顶。

如果要用寄存器来提供偏移地址,只能使用BX、SI、DI、BP