2017-04-23 3 views
5

J'aidais un de mes amis déboguer son programme, et nous réduit à un problème qui se produit ici même:8086 assembly sur DOSBox: Bug avec instruction idiv?

.MODEL small 
.STACK 16 
.CODE 
start: 
    mov ax, 044c0h 
    mov bl, 85 
    idiv bl 
exit: 
    mov ax, 4c00h 
    int 21h 

end start 

Après assemblage avec tasm 4.1, et en cours d'exécution sur DOSBox 0,74, il passe en une boucle infinie. En l'examinant avec le débogueur turbo, on peut voir après l'instruction idiv, qui pour une raison quelconque modifie les registres et ip, et après que deux instructions apparemment aléatoires les restaurent pour pointer vers la ligne idiv, en l'exécutant à nouveau à l'infini.

Est-ce que quelqu'un a des explications à ce sujet?

+3

Parce que le quotient de votre division signé ne peut pas tenir dans un registre 8 bits (_AL_) afin vous obtenez un débordement arithmétique. La plage du quotient sur un r16/r8 [IDIV] (http://www.felixcloutier.com/x86/IDIV.html) est comprise entre -128 et +127. Votre division donne un quotient de 207. –

+1

@MichaelPetch: Yep. J'étais sur le point de soumettre cela comme une réponse, mais vous me battez! – wallyk

+1

Ce serait le gestionnaire d'interruption INT 00h. Un peu surprenant pour moi que ça saute tout de suite au code cassé, provoquant une boucle infinie. Je m'attendais à ce qu'il se termine l'application, imprime un message d'erreur, ou quelque chose d'un peu plus évident. Vous êtes la deuxième personne en autant de jours à être confus par ceci. –

Répondre

7

Cette question est une variante des autres défaillances liées à la division. Le x86 tag wiki a quelques liens supplémentaires:


Le code apparemment aléatoire votre débogueur semble sauter est le gestionnaire Arithmétique d'exception (également le même que division par zéro). Qu'est-ce qui se passe, c'est que votre code connaît un Division Overflow. Vous effectuez un IDIV 16 bits/8 bits. De la documentation:

Signé diviser AX par r/m8, avec le résultat stocké dans: AL ← Quotient, AH ← Reste.

enter image description here

Vous remarquerez que pour la division avec un diviseur 8 bits (dans votre cas BL) la plage du quotient est de -128 à 127. 044c0h IDIV 85 est 207 (décimal). 207 ne rentre pas dans un registre 8 bits signé, ce qui entraîne un débordement de division et la cause de votre problème inattendu.

Pour résoudre ce problème, vous pouvez passer à un diviseur de 16 bits. Vous pouvez donc placer votre diviseur dans BX (registre 16 bits). Ce serait mov bx, 85. Malheureusement, ce n'est pas si simple. Lors de l'utilisation d'un diviseur de 16 bits, le processeur suppose que le dividende est de 32 bits avec 16 bits élevés dans DX et 16 bits inférieurs dans AX.

Divise signée DX: AX par r/m16, avec le résultat stocké dans AX ← Quotient, DX ← Reste.

Pour résoudre cela, vous devez signer étendre la valeur de 16 bits dans AX. Ceci est simple car il vous suffit d'utiliser l'instruction après avoir placé la valeur dans AX.De la référence du jeu d'instructions

DX: AX ← signe-extension de AX.

efficacement si le bit le plus significatif (MSB) de AX est DX deviendra 0. Si le MSB est 1 alors DX deviendrait 0FFFFh (tous les bits à un). Le bit de signe d'un nombre est le MSB.

Avec tout cela à l'esprit votre code de division pourrait être ajustée pour prendre un 16 bits diviseur:

mov ax, 044c0h 
cwd    ; Sign extend AX into DX (DX:AX = 32-bit dividend) 
mov bx, 85   ; Divisor is 85 
idiv bx   ; Signed divide of DX:AX by BX