Ensamblaje 8086 en DOSBox: ¿Error con la instrucción idiv?

Resuelto eequinox asked hace 7 años • 1 respuestas

Estaba ayudando a un amigo mío a depurar su programa y lo redujimos a un problema que ocurre incluso aquí:

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

end start

Después de ensamblarlo con tasm 4.1 y ejecutarlo en DOSBox 0.74, entra en un bucle infinito. Al inspeccionarlo con turbo debugger se puede ver que sucede después de la idivinstrucción, que por alguna razón modifica los registros csy ip, y luego de dos instrucciones aparentemente aleatorias los restaura para que apunten a la idivlínea, ejecutándolo nuevamente hasta el infinito.

¿Alguien tiene alguna explicación para esto?

eequinox avatar Apr 24 '17 01:04 eequinox
Aceptado

Esta pregunta es una variación de otras fallas relacionadas con la división. La x86etiqueta wiki tiene algunos enlaces adicionales:

  • idiv/ divproblemas : cero edxprimero o firmar y extender eax. . 32 bits divfalla si el cociente 64b/32b => 32b en realidad no cabe en 32b.

El código aparentemente aleatorio al que parece saltar su depurador es el controlador de excepciones aritméticas (también el mismo que Divide por cero). Lo que sucede es que su código está experimentando un error Division Overflow. Estás haciendo un IDIV de 16 bits/8 bits . De la documentación:

Con signo divide AX por r/m8, con el resultado almacenado en: AL ← Cociente, AH ← Resto.

ingrese la descripción de la imagen aquí

Notarás que para la división con un divisor de 8 bits (en tu caso BL ), el rango del cociente es de -128 a +127. 044c0h IDIV 85 es 207 (decimal). 207 no cabe en un registro de 8 bits firmado, por lo que se produce un desbordamiento de división y la causa del problema inesperado.

Para resolver esto, puede subir a un divisor de 16 bits. Entonces puedes colocar tu divisor en BX (registro de 16 bits). Eso sería mov bx, 85. Desgraciadamente no es tan sencillo. Cuando se utiliza un divisor de 16 bits, el procesador asume que el dividendo es de 32 bits, con 16 bits altos en DX y 16 bits bajos en AX .

Divida con signo DX:AX por r/m16, con el resultado almacenado en AX ← Cociente, DX ← Resto.

Para resolver esto, debe firmar y ampliar el valor de 16 bits en AX . Esto es simple ya que solo necesita usar la instrucción CWD después de colocar el valor en AX . De la referencia del conjunto de instrucciones

DX:AX ← signo-extensión de AX.

Efectivamente, si el bit más significativo (MSB) de AX es 0, DX se convertirá en 0. Si el MSB es 1, entonces DX se convertirá en 0ffffh (todos los bits establecidos en uno). El bit de signo de un número es el MSB.

Con todo esto en mente, su código de división podría ajustarse para tomar un divisor de 16 bits:

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
Michael Petch avatar Apr 23 '2017 19:04 Michael Petch