奇怪的宏 (TASM)
Weird Macros (TASM)
考虑以下宏:
pixelFast MACRO
; This macro draws a pixel, assuming the coordinates are already loaded in cx&dx and the color is in al.
xor bh, bh
mov ah, 0ch
int 10h
ENDM
drawRect MACRO x1, y1, x2, y2, color
LOCAL @@loop, @@row_loop
xor cx, cx
mov dx, y1
mov al, BYTE PTR [color]
@@loop:
mov cx, x1
@@row_loop:
pixelFast
inc cx
cmp cx, x2
jna @@row_loop
inc dx
cmp dx, y2
jna @@loop
ENDM
rendToolBar MACRO
drawRect COLORDISP_X1, COLORDISP_Y1, COLORDISP_X2, COLORDISP_Y2, foreground_color
mov temp_color, 36h
drawRect COLORBTN1_X1, COLORBTN1_Y1, COLORBTN1_X2, COLORBTN1_Y2, temp_color
mov temp_color, 2Eh
drawRect COLORBTN2_X1, COLORBTN2_Y1, COLORBTN2_X2, COLORBTN2_Y2, temp_color
mov temp_color, 4h
drawRect COLORBTN3_X1, COLORBTN3_Y1, COLORBTN3_X2, COLORBTN3_Y2, temp_color
mov temp_color, 2Bh
drawRect COLORBTN4_X1, COLORBTN4_Y1, COLORBTN4_X2, COLORBTN4_Y2, temp_color
ENDM
在我的代码中的某处,我使用了 rendToolBar 宏。它应该画一个大的白色canvas,然后是一个小方块,然后在它旁边以某种模式绘制一些较小的方块,这与我的问题无关。
请注意,rendToolBar 调用了 drawRect 5 次。我在 turbo 调试器中遵循了这段代码(因为出现了严重错误)并注意到在 drawRect 宏的第 4 次执行中,来自 pixelFast 的 "int 10h" 实际上不是 "int 10h",而是 "int 2" .这会导致 NMI,这会弄乱我的程序。我想知道是什么让 TASM 在第 4 次调用该宏时为该行以不同方式扩展宏,尽管这一行 "int 10h" 不依赖于任何宏参数。
如果你看这张图片,你会看到意想不到的 "int 2",它应该是 "int 10"。之后可以看到:
cmp [bx+si], ax
add ch, bh
cmp [bx+03], dx
根据宏的源代码,这 3 条指令实际上应该是
inc cx
cmp cx, COLORBTN3_X2
jna @@row_loop
在中断之前还有一些其他指令有点偏离,但你明白了。
考虑将逻辑(段:偏移)地址转换为线性地址的数学:
CS:IP = 49ae:03cc = 49eac
其中 3cc 是第一个意外字节的偏移量。
SS:SP = 39ed:fffc = 49ecc
.
可视化两个线性地址,我们有
| |
| 49ecc | <-- Stack pointer, going down
| |
Only 32 bytes below
| |
| 49eac | <-- Execution flow, going up
| |
您的堆栈一定在屏幕截图之前的某个时刻与代码段发生冲突。
尝试设置堆栈,使其距离代码足够远。
实模式下的最大堆栈大小为 64KiB,因为这是一个段的大小。
在 DOS 中可以安全地假设你的程序之后的内存没有被使用1并且,只要它存在,那么你就可以将它用于堆栈。
这不是浪费内存,因为 DOS 不是多任务处理。
请注意,除非您在其中明确定义内容,否则堆栈段不会在二进制文件上使用 space。
有两种管理堆栈的方法:
使用汇编器
请参阅 TASM manual 以供参考,第 92 页。
如果您正在使用 STACK
指令,只需为堆栈的估计大小设置一个上限。
如果您正在编写 EXE,则可以使用模型修饰符 FARSTACK
。
SS:SP
应根据链接器在 MZ header.
上写入的值进行设置
通过不将堆栈段放入 dgroup.
,您可以拥有完整的 64KiB 堆栈
手动
如果你知道你不需要完整的 64KiB 堆栈,你可以把它放在数据段的末尾(对于 COM 来说也是代码段)
;COM ;EXE
mov ax, cs mov ax, ds ;Assume SMALL memory model
mov ss, ax mov ss, ax ;see below
xor sp, sp xor sp, sp
这给出了 64KiB -
.
如果您需要完整的 64KiB 堆栈,您可以使用下一个可用段
;COM ;EXE
mov ax, cs mov ax, ds ;Assume SMALL memory model, if not
add ax, 1000h add ax, 1000h ;use the symbol for the last data
mov ss, ax mov ss, ax ;segment
xor sp, sp xor sp, sp
这假定最后一段如果被完全使用但可以使您免于某些 segment/offset/symbols 算法。
1 这是因为DOS不是多任务的,程序是loaded above TSR programs.
COMMAND.COM的非常驻部分被加载到常规内存的顶部,但它可以被覆盖。
考虑以下宏:
pixelFast MACRO
; This macro draws a pixel, assuming the coordinates are already loaded in cx&dx and the color is in al.
xor bh, bh
mov ah, 0ch
int 10h
ENDM
drawRect MACRO x1, y1, x2, y2, color
LOCAL @@loop, @@row_loop
xor cx, cx
mov dx, y1
mov al, BYTE PTR [color]
@@loop:
mov cx, x1
@@row_loop:
pixelFast
inc cx
cmp cx, x2
jna @@row_loop
inc dx
cmp dx, y2
jna @@loop
ENDM
rendToolBar MACRO
drawRect COLORDISP_X1, COLORDISP_Y1, COLORDISP_X2, COLORDISP_Y2, foreground_color
mov temp_color, 36h
drawRect COLORBTN1_X1, COLORBTN1_Y1, COLORBTN1_X2, COLORBTN1_Y2, temp_color
mov temp_color, 2Eh
drawRect COLORBTN2_X1, COLORBTN2_Y1, COLORBTN2_X2, COLORBTN2_Y2, temp_color
mov temp_color, 4h
drawRect COLORBTN3_X1, COLORBTN3_Y1, COLORBTN3_X2, COLORBTN3_Y2, temp_color
mov temp_color, 2Bh
drawRect COLORBTN4_X1, COLORBTN4_Y1, COLORBTN4_X2, COLORBTN4_Y2, temp_color
ENDM
在我的代码中的某处,我使用了 rendToolBar 宏。它应该画一个大的白色canvas,然后是一个小方块,然后在它旁边以某种模式绘制一些较小的方块,这与我的问题无关。
请注意,rendToolBar 调用了 drawRect 5 次。我在 turbo 调试器中遵循了这段代码(因为出现了严重错误)并注意到在 drawRect 宏的第 4 次执行中,来自 pixelFast 的 "int 10h" 实际上不是 "int 10h",而是 "int 2" .这会导致 NMI,这会弄乱我的程序。我想知道是什么让 TASM 在第 4 次调用该宏时为该行以不同方式扩展宏,尽管这一行 "int 10h" 不依赖于任何宏参数。
cmp [bx+si], ax
add ch, bh
cmp [bx+03], dx
根据宏的源代码,这 3 条指令实际上应该是
inc cx
cmp cx, COLORBTN3_X2
jna @@row_loop
在中断之前还有一些其他指令有点偏离,但你明白了。
考虑将逻辑(段:偏移)地址转换为线性地址的数学:
CS:IP = 49ae:03cc = 49eac
其中 3cc 是第一个意外字节的偏移量。
SS:SP = 39ed:fffc = 49ecc
.
可视化两个线性地址,我们有
| |
| 49ecc | <-- Stack pointer, going down
| |
Only 32 bytes below
| |
| 49eac | <-- Execution flow, going up
| |
您的堆栈一定在屏幕截图之前的某个时刻与代码段发生冲突。
尝试设置堆栈,使其距离代码足够远。
实模式下的最大堆栈大小为 64KiB,因为这是一个段的大小。
在 DOS 中可以安全地假设你的程序之后的内存没有被使用1并且,只要它存在,那么你就可以将它用于堆栈。
这不是浪费内存,因为 DOS 不是多任务处理。
请注意,除非您在其中明确定义内容,否则堆栈段不会在二进制文件上使用 space。
有两种管理堆栈的方法:
使用汇编器
请参阅 TASM manual 以供参考,第 92 页。如果您正在使用
STACK
指令,只需为堆栈的估计大小设置一个上限。如果您正在编写 EXE,则可以使用模型修饰符
,您可以拥有完整的 64KiB 堆栈FARSTACK
。
SS:SP
应根据链接器在 MZ header.
上写入的值进行设置 通过不将堆栈段放入 dgroup.手动
如果你知道你不需要完整的 64KiB 堆栈,你可以把它放在数据段的末尾(对于 COM 来说也是代码段);COM ;EXE mov ax, cs mov ax, ds ;Assume SMALL memory model mov ss, ax mov ss, ax ;see below xor sp, sp xor sp, sp
这给出了 64KiB -
.
如果您需要完整的 64KiB 堆栈,您可以使用下一个可用段
;COM ;EXE mov ax, cs mov ax, ds ;Assume SMALL memory model, if not add ax, 1000h add ax, 1000h ;use the symbol for the last data mov ss, ax mov ss, ax ;segment xor sp, sp xor sp, sp
这假定最后一段如果被完全使用但可以使您免于某些 segment/offset/symbols 算法。
1 这是因为DOS不是多任务的,程序是loaded above TSR programs.
COMMAND.COM的非常驻部分被加载到常规内存的顶部,但它可以被覆盖。