2016-07-25 77 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