奇怪的宏(TASM)

Ita*_*mar 6 macros assembly 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
Run Code Online (Sandbox Code Playgroud)

在我的代码中的某处,我使用了rendToolBar宏.它应该绘制一个大的白色画布,然后是一个小方块,旁边是一些特定图案的小方块,这与我的问题无关.请注意,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
Run Code Online (Sandbox Code Playgroud)

根据宏的源代码,这3条指令实际上应该是

inc cx
cmp cx, COLORBTN3_X2
jna @@row_loop
Run Code Online (Sandbox Code Playgroud)

在中断之前还有一些其他指令有点断,但你明白了.

Mar*_*oom 6

考虑将逻辑(段:偏移)地址转换为线性地址的数学运算:

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
   |       |
Run Code Online (Sandbox Code Playgroud)

您的堆栈必须在截屏之前的某个时刻与代码段发生冲突.
尝试设置堆栈,使其远离代码.


实模式下的最大堆栈大小为64KiB,因为这是段的大小.
在DOS中可以安全地假设程序之后的内存未被使用1,并且只要它存在,所以你可以将它用于堆栈.这不是浪费内存,因为DOS不是多任务处理.
请注意,除非您在其中明确定义内容,否则堆栈段不会占用二进制文件的空间.

管理堆栈有两种方法:

  1. 使用汇编程序
    参见TASM手册以供参考,第92页.

    如果您正在使用该STACK指令,只需在堆栈的估计大小上加上一个上限.

    如果您正在编写EXE,则可以使用模型修改器FARSTACK.
    SS:SP应根据链接器在MZ头上写入的值来设置.
    这样就可以通过不将堆栈段放入dgroup来获得完整的64KiB堆栈.

     

  2. 手动
    如果你知道你不需要一个完整的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
    
    Run Code Online (Sandbox Code Playgroud)

    这给出了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
    
    Run Code Online (Sandbox Code Playgroud)

    这假定最后一个段如果完全使用但是从一些段/偏移/符号算术中省去了.


1这是因为DOS不是多任务处理,程序是在TSR程序之上加载的.COMMAND.COM的
非常驻部分加载到常规内存的顶部,但可以覆盖它.