如何检查8086上的堆栈是否为空
How to check if the stack is empty on 8086
我正在使用 8086 汇编器进行一项研究,检查数学表达式中括号的平衡。算法如下:
- 空栈
- 读取字符直到字符串结尾
- 如果字符是开符号,将其压入堆栈
- 如果是一个结束符号,则如果栈为空则报错,否则出栈
- 如果弹出的符号不是对应的开仓符号,则报错
- 文件结束时,如果栈不为空则报错
除了需要检查栈是否为空的部分,我已经想通了。如何检查堆栈是否为空或如何在代码中创建一个空堆栈?当堆栈中没有任何东西可以弹出时,我尝试将其弹出并输出一些值。这个值是什么意思?
所以你想用call-stack来实现你的栈数据结构。这很正常,在 asm 中通常是个好主意。
您不想 "empty" 调用堆栈,您只是想设置一种方法来告诉您何时弹出了您在此函数中推送的所有数据。即您的堆栈数据结构为空。
要么推送一个您的代码不会以其他方式生成的标记值,要么使用寄存器(或内存位置)来保存 SP 的当前值。尤其是在 16 位代码中,使用 mov bp,sp
来制作堆栈帧是很常见的,因此您已经将一个寄存器专门用于记住 SP 的旧值。你可以在后面的代码中使用 cmp sp,bp
来查看你是否清空了你的堆栈数据结构。
如果您需要任何 space 用于堆栈上的局部变量,您可以为 BP 以上的局部变量设置 space 的堆栈帧,以及低于 BP 的可变大小堆栈数据。
my_func:
; push si ; save whatever other register you want
sub sp, 28 ; reserve 28 bytes for locals
push bp
mov bp, sp
.my_loop:
... ; use locals from [bp+2] to [bp+26]
cmp bp, sp
jne .my_loop
; else fall through: stack empty
leave
add sp, 28
ret
如果你根本不需要将任何局部变量溢出到内存中,那么你就不需要制作堆栈框架并且可以使用 bp
作为一个额外的暂存器。但如果你这样做,那么你可以免费使用 bp
作为你的堆栈数据结构的顶部。
请注意,它不是一个传统的堆栈帧:通常 [bp+2]
是 return 地址,因此调试器堆栈回溯可能会显示虚假结果。但是 [bp+0]
仍然指向调用者保存的 bp
值,所以只有那个条目是错误的,它不会中断通过 这个函数的回溯 。
调用栈为空是什么意思?
除非 sp = 0xFFFE
(2 字节在段的顶部对齐),否则 ss:sp
上方总是有内存。如果 sp
从 0xFFFE
循环到 0x0000
,pop
甚至不会出错。 pop [mem]
可以 #GP
如果弹出内存超出 CS、DS、ES、FS 或 GS 的段限制,根据手册,但我认为这只有在虚幻模式下才有可能。
push
in real-mode does fault with #SS
如果 sp
或 esp
的新值超出堆栈段限制(在纯 x86-16 中不可能不使用在保护模式下加载的虚幻模式缓存段描述符).实际上,我认为您可能会在纯 real-mode 和 SP=1
/ push ax
中获得 #SS。存储地址将为 ss:0xFFFF
,因此 2 字节存储将包括从 ss<<4
开始的 64k 段之外的内存 1 字节
我正在使用 8086 汇编器进行一项研究,检查数学表达式中括号的平衡。算法如下:
- 空栈
- 读取字符直到字符串结尾
- 如果字符是开符号,将其压入堆栈
- 如果是一个结束符号,则如果栈为空则报错,否则出栈
- 如果弹出的符号不是对应的开仓符号,则报错
- 文件结束时,如果栈不为空则报错
除了需要检查栈是否为空的部分,我已经想通了。如何检查堆栈是否为空或如何在代码中创建一个空堆栈?当堆栈中没有任何东西可以弹出时,我尝试将其弹出并输出一些值。这个值是什么意思?
所以你想用call-stack来实现你的栈数据结构。这很正常,在 asm 中通常是个好主意。
您不想 "empty" 调用堆栈,您只是想设置一种方法来告诉您何时弹出了您在此函数中推送的所有数据。即您的堆栈数据结构为空。
要么推送一个您的代码不会以其他方式生成的标记值,要么使用寄存器(或内存位置)来保存 SP 的当前值。尤其是在 16 位代码中,使用 mov bp,sp
来制作堆栈帧是很常见的,因此您已经将一个寄存器专门用于记住 SP 的旧值。你可以在后面的代码中使用 cmp sp,bp
来查看你是否清空了你的堆栈数据结构。
如果您需要任何 space 用于堆栈上的局部变量,您可以为 BP 以上的局部变量设置 space 的堆栈帧,以及低于 BP 的可变大小堆栈数据。
my_func:
; push si ; save whatever other register you want
sub sp, 28 ; reserve 28 bytes for locals
push bp
mov bp, sp
.my_loop:
... ; use locals from [bp+2] to [bp+26]
cmp bp, sp
jne .my_loop
; else fall through: stack empty
leave
add sp, 28
ret
如果你根本不需要将任何局部变量溢出到内存中,那么你就不需要制作堆栈框架并且可以使用 bp
作为一个额外的暂存器。但如果你这样做,那么你可以免费使用 bp
作为你的堆栈数据结构的顶部。
请注意,它不是一个传统的堆栈帧:通常 [bp+2]
是 return 地址,因此调试器堆栈回溯可能会显示虚假结果。但是 [bp+0]
仍然指向调用者保存的 bp
值,所以只有那个条目是错误的,它不会中断通过 这个函数的回溯 。
调用栈为空是什么意思?
除非 sp = 0xFFFE
(2 字节在段的顶部对齐),否则 ss:sp
上方总是有内存。如果 sp
从 0xFFFE
循环到 0x0000
,pop
甚至不会出错。 pop [mem]
可以 #GP
如果弹出内存超出 CS、DS、ES、FS 或 GS 的段限制,根据手册,但我认为这只有在虚幻模式下才有可能。
push
in real-mode does fault with #SS
如果 sp
或 esp
的新值超出堆栈段限制(在纯 x86-16 中不可能不使用在保护模式下加载的虚幻模式缓存段描述符).实际上,我认为您可能会在纯 real-mode 和 SP=1
/ push ax
中获得 #SS。存储地址将为 ss:0xFFFF
,因此 2 字节存储将包括从 ss<<4