assembly - 奇怪的宏 (TASM)

标签 assembly macros interrupt tasm dosbox

考虑以下宏:

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”不依赖于任何宏参数。
enter image description here
如果您查看此图像,您会在那里看到意外的“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/

    相关文章:

    c++ - 在 win 7 64 位的 visual studio 2012 中编写 x86 汇编代码的问题

    c - 了解 C 中的 for 循环如何与 IA32 机器代码相关

    javascript - Sweet.js 调用方法的宏

    java - 音乐播放时中断线程

    c - 耳机插3.5接口(interface)怎么知道?

    assembly - 什么是大双字?

    c - #include header with C declarations in a assembly file without errors?

    c - 如何使用另一个宏创建 C 宏名称?

    c - #和##展开顺序

    c++ - 如何在超时时停止异步评估功能?