2016-07-25 93 views
6

考虑下面的宏:奇怪的宏(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宏。它应该画一个大的白色画布,然后是一个小广场,并在它旁边放置一些小方格,这与我的问题无关。 请注意,rendToolBar调用drawRect 5次。我在turbo调试器中关注了这段代码(因为出错的地方),并注意到在drawRect宏的第四次执行中,pixelFast的“int 10h”实际上不是“int 10h”,而是“int 2”。这导致了一个NMI,它为我的程序混淆了一些东西。我想知道是什么让TASM在第4次调用宏时以不同的方式扩展宏,尽管这行“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 

有哪些是有点过中断之前的一些其他指令,但你明白了。

+0

有什么问题吗? – Kamiccolo

+0

@Kamiccolo问题在那里,就在turbo调试器的图片上方。 – Itamar

+0

我希望所有关于SO的初学者调试帮助问题实际上都有一个调试器屏幕截图或包含调试他们遇到问题所需的所有信息的东西。所以人们经常会发布代码,只是询问为什么会发生段错误,甚至没有说出哪些指令错误。 –

回答

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 
    |  | 

你的筹码必须在截图之前的一些点发生了冲突到代码段。
尝试设置堆栈,使其远离代码。


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

有管理堆栈两种方式:

  1. 使用汇编
    TASM manual参考,第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 
    

    这给出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 
    

    这是假设的最后一段,如果完全使用,但一些段/偏移/符号运算为您节省。


这是因为DOS不是多任务和方案loaded above TSR programs
COMMAND.COM的非常驻部分被加载到常规内存的顶部,但可以被覆盖。

+0

我做了数学,并确定这确实是问题。重新安排细分市场位置的明确方式是什么? – Itamar

+0

我设法通过定义一个更大的堆栈来解决它,但是我想知道是否有一个“更干净”的解决方案,它不涉及使用比我实际需要更多的内存。 – Itamar

+1

@Itamar在代码中使用更少的堆栈(少推/弹出)。你也必须考虑外部调用使用的堆栈空间,比如'int 10h'。 “比我真正需要的” - 但你需要这个堆栈,就像你使用它一样(未使用的堆栈不会修改内存)。最后,我只是好奇,当你调用BIOS中断而不是将像素直接写入VRAM时,为什么你称这个宏像素为* fast *?这是在DOS中绘制像素(调用BIOS)最慢的方法。 :D有点有趣的名字... – Ped7g