考虑以下宏:
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 调试器中遵循了这段代码(因为出现了严重错误)并注意到在第 4 次执行 drawRect 宏时,pixelFast 中的“int 10h”实际上不是“int 10h”,而是“int 2”。这会导致一个 NMI,它把我的程序搞砸了。我想知道是什么让 TASM 在第四次调用该宏时为该行以不同的方式扩展宏,尽管这一行“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 不是多任务处理。
请注意,堆栈段不会占用二进制文件的空间,除非您在其中明确定义内容。
有两种管理堆栈的方法:
见 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
这假设最后一段如果完全使用,但可以使您免于某些段/偏移/符号算法。
1 这是因为 DOS 不是多任务处理,程序是 loaded above TSR programs .
COMMAND.COM 的非驻留部分加载到常规内存的顶部,但它可以被覆盖。
关于assembly - 奇怪的宏 (TASM),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38575023/