2016-07-25 1 views
6

Considérez les macros suivantes:macros Bizarre (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 

Quelque part dans mon code, j'utilise la macro rendToolBar. Il est censé dessiner une grande toile blanche, puis un petit carré et, à côté, des carrés plus petits dans un certain motif, ce qui n'est pas pertinent pour ma question. Notez que rendToolBar appelle drawRect 5 fois. J'ai suivi ce code dans le débogueur turbo (parce que quelque chose s'est mal passé) et j'ai remarqué que dans la 4ème exécution de la macro drawRect, le "int 10h" de pixelFast n'est pas "int 10h", mais plutôt "int 2". Cela provoque un NMI, qui salit des choses pour mon programme. Je veux savoir ce qui fait que TASM développe différemment la macro pour cette ligne dans le 4ème appel de cette macro, malgré le fait que cette ligne "int 10h" ne repose sur aucun argument de macro. enter image description here Si vous regardez cette image, vous pouvez voir le "int 2" inattendu là, qui était censé être un "int 10". Après, vous pouvez voir:

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

Selon le code source de la macro, ces 3 instructions ont été en fait censés être

inc cx 
cmp cx, COLORBTN3_X2 
jna @@row_loop 

Il y a quelques autres instructions qui sont un peu hors tension avant l'interruption , mais vous avez compris.

+0

et quelle est la question? – Kamiccolo

+0

@Kamiccolo La question est là, juste au-dessus de l'image du débogueur turbo. – Itamar

+0

Je souhaite que toutes les questions debug-help débutant sur SO aient effectivement une capture d'écran du débogueur ou quelque chose contenant toutes les informations nécessaires pour déboguer le problème qu'elles rencontrent. Très souvent, les gens postent du code et demandent simplement pourquoi il se sépare sans même dire quelles fautes d'instructions. –

Répondre

6

Considérons le calcul pour transformer logique (segment: offset) dans les adresses linéaires:

CS:IP = 49ae:03cc = 49eac3cc est le décalage du premier octet inattendu.

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

les deux adresses Visualizing linéaires, nous avons

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

Only 32 bytes below 

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

Votre pile doit se sont affrontées dans le segment de code à un moment donné avant la capture d'écran.
Essayez d'installer la pile de façon à ce qu'elle soit suffisamment éloignée du code. La taille maximale de la pile en mode réel est de 64 Ko, car c'est la taille d'un segment.
Dans DOS est sûr de supposer que la mémoire après votre programme n'est pas utilisé et, tant qu'il existe, de sorte que vous pouvez l'utiliser pour la pile. Cela ne gaspille pas de mémoire car DOS n'est pas multitâche.
Notez que le segment de pile ne prendra pas d'espace sur le fichier binaire, sauf si vous y définissez explicitement des éléments.

Il y a deux façons de gérer la pile:

  1. Utilisation de l'assembleur
    Voir la TASM manual de référence, page 92.

    Si vous utilisez la directive STACK vient de mettre un limite supérieure sur la taille estimée de la pile.

    Si vous écrivez un fichier EXE, vous pouvez utiliser le modificateur de modèle FARSTACK.
    SS:SP doit être défini en fonction des valeurs que l'éditeur de liens a écrites dans l'en-tête MZ.
    Cela vous permet d'avoir une pile complète de 64 Ko en ne mettant pas le segment de pile dans le dgroup.

     

  2. manuellement
    Si vous savez que vous ne serez pas besoin d'un plein 64 Kio de la pile, vous pouvez le mettre à la fin du segment de données (que pour COM est également le segment de code)

    ;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 
    

    Cela donne 64 Kio - Code < + données taille >.

    Si vous avez besoin d'une pile complète 64 Kio vous pouvez simplement utiliser le segment suivant disponible

    ;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 
    

    Cela suppose que le dernier segment si elle est entièrement utilisé, mais vous permet d'économiser un segment/offset/symboles arithmétique.


C'est parce que DOS ne sont pas multi-tâches et programmes sont loaded above TSR programs.
Une partie non résidente de COMMAND.COM est chargée en haut de la mémoire conventionnelle mais elle peut être écrasée.

+0

J'ai fait le calcul et déterminé que c'est effectivement le problème. Quelle est la manière claire de réarranger les emplacements de mes segments? – Itamar

+0

J'ai réussi à le résoudre en définissant une pile plus grande, mais je veux savoir s'il existe une solution «plus propre», qui n'implique pas d'utiliser plus de mémoire que j'en ai réellement besoin. – Itamar

+1

@Itamar utilisez donc moins de pile dans votre code (moins de push/pop). De plus, vous devez prendre en compte l'espace de pile utilisé par les appels externes, comme 'int 10h'. "dont j'ai réellement besoin" - mais vous avez besoin de cette pile, comme vous l'avez utilisée (la pile inutilisée ne modifie pas la mémoire). Enfin, je suis juste curieux, pourquoi appelez-vous ce pixel macro * rapide *, lorsque vous appelez l'interruption du BIOS au lieu d'écrire le pixel directement sur la VRAM? C'est le moyen le plus lent de dessiner un pixel sous DOS (pour appeler le BIOS). : D Un nom un peu drôle ... – Ped7g