2016-07-25 42 views
6

Betrachten Sie die folgenden Makros:Weird-Makros (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 

Irgendwo in meinem Code, ich benutze das rendToolBar Makro. Es soll eine große weiße Leinwand und dann ein kleines Quadrat und daneben einige kleinere Quadrate in einem bestimmten Muster zeichnen, was für meine Frage irrelevant ist. Beachten Sie, dass rendToolBar drawRect 5 mal aufruft. Ich folgte diesem Code im Turbo-Debugger (weil etwas sehr schief gelaufen ist) und bemerkte, dass in der 4. Ausführung des drawRect-Makros das "int 10h" von pixelFast nicht wirklich "int 10h", sondern "int 2" ist. Dies führt zu einem NMI, der Dinge für mein Programm durcheinander bringt. Ich möchte wissen, was bewirkt, dass TASM das Makro für diese Zeile im vierten Aufruf für dieses Makro unterschiedlich erweitert, trotz der Tatsache, dass diese Zeile "int 10h" nicht auf irgendwelche Makroargumente angewiesen ist. enter image description here Wenn Sie dieses Bild betrachten, können Sie das unerwartete "int 2" dort sehen, das ein "int 10" sein sollte. Nach ihm können Sie sehen:

cmp [bx+si], ax 
add ch, bh 
cmp [bx+03], dx 

Nach den Quellcode des Makros wurden diese drei Befehle in der Tat soll

inc cx 
cmp cx, COLORBTN3_X2 
jna @@row_loop 

Es gibt einige andere Anweisungen sein, die ein wenig abseits der vor der Unterbrechung sind , aber Sie verstehen es.

+0

und was ist die Frage? – Kamiccolo

+0

@Kamiccolo Die Frage ist da, direkt über dem Bild des Turbo-Debuggers. – Itamar

+0

Ich wünschte, alle Anfänger Debug-Hilfe Fragen zu SO hatte tatsächlich einen Debugger Screenshot oder etwas, das alle Informationen enthält, die benötigt werden, um das Problem zu debuggen, das sie haben. So oft Leute Post Code und nur fragen, warum es segfold ohne zu sagen, welche Anweisung Fehler. –

Antwort

6

Betrachten wir die mathematische logische (Segment: Offset) zu transformieren Adressen in Linear eine:

CS:IP = 49ae:03cc = 49eac wo 3cc die des ersten unerwarteten Byte versetzt ist.

SS:SP = 39ed:fffc = 49ecc.

die beiden linearen Adressen Visualizing, haben wir

|  | 
    | 49ecc | <-- Stack pointer, going down 
    |  | 

Only 32 bytes below 

    |  | 
    | 49eac | <-- Execution flow, going up 
    |  | 

Ihr Stack in das Code-Segment zu einem bestimmten Zeitpunkt vor dem Screenshot kollidierte haben müssen.
Versuchen Sie, den Stapel so einzurichten, dass er weit genug vom Code entfernt ist.

Die maximale Stapelgröße im Realmodus beträgt 64 KB, da dies die Größe eines Segments ist.
In DOS ist sicher anzunehmen, dass der Speicher nach Ihrem Programm nicht verwendet wird und, solange es existiert, so dass Sie es für den Stapel verwenden können. Das verschwendet keinen Speicher, da DOS kein Multitasking ist.
Beachten Sie, dass das Stapelsegment keinen Platz für die Binärdatei beansprucht, es sei denn, Sie definieren explizit darin.

Es gibt zwei Möglichkeiten, um den Stapel zu verwalten:

  1. Mit dem Assembler
    Siehe TASM manual als Referenz, Seite 92.

    Wenn Sie die STACK Direktive verwenden nur ein setzen Obergrenze für die geschätzte Größe des Stapels.

    Wenn Sie eine EXE schreiben, können Sie den Modellmodifikator FARSTACK verwenden.
    SS:SP sollte der Linker schrieb auf der MZ-Header auf der Grundlage der Werte eingestellt werden.
    Dies können Sie einen vollständigen 64KiB Stack durch nicht das Stapelsegment in den DGROUP setzen.

     

  2. manuell
    Wenn Sie wissen, dass Sie nicht ein volles 64KiB Stack benötigen, können Sie es am Ende des Datensegments setzen (das für COM ist auch der Code-Segment)

    ;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 
    

    Das gibt 64KiB - < Code + Datengröße >.

    Wenn Sie einen vollständigen 64KiB benötigen stapeln Sie nur die nächste verfügbare Segment können

    ;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 
    

    Dies setzt voraus, dass das letzte Segment, wenn voll ausgenutzt, sondern erspart Ihnen einige Segment/Offset/Symbole Arithmetik.


Dies liegt daran, DOS ist nicht Multitasking und Programme sind loaded above TSR programs.
Ein nicht residenten Teil COMMAND.COM überschrieben wird ein den oberen Teil des konventionellen Speichers, aber es kann geladen.

+0

Ich habe die Mathematik und festgestellt, dass dies in der Tat das Problem ist. Was ist der eindeutige Weg, die Standorte meiner Segmente neu zu ordnen? – Itamar

+0

ich es geschafft, es zu lösen, indem ein größerer Stapel definieren, aber ich möchte wissen, ob es eine „sauberere“ Lösung ist, die nicht mehr Speicher mit sich bringt, als ich tatsächlich benötigen. – Itamar

+1

@Itamar verwenden Sie also weniger Stapel in Ihrem Code (weniger Push/Pop). Außerdem müssen Sie den Stack-Platz berücksichtigen, der von externen Anrufen benutzt wird, wie zB 'int 10h'. "als ich wirklich brauche" - aber Sie brauchen diesen Stapel, wie Sie ihn benutzt haben (unbenutzter Stapel ändert den Speicher nicht). Schließlich bin ich nur neugierig, warum nennst du das Makro Pixel * schnell *, wenn du den BIOS-Interrupt aufruft, anstatt das Pixel direkt in VRAM zu schreiben? Es ist die langsamste Möglichkeit, ein Pixel in DOS zu zeichnen (um das BIOS aufzurufen). : D Ein bisschen komischer Name ... – Ped7g