assembly - 8086 ensamblado en DOSBox: ¿Error con la instrucción idiv?
x86 x86-16 (1)
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 depurador, se puede ver que sucede después de la instrucción
idiv
, que por alguna razón modifica los registros
cs
e
ip
, y después de dos instrucciones aparentemente aleatorias los restaura para que apunten a la línea
idiv
, ejecutándola nuevamente hasta el infinito.
¿Alguien tiene alguna explicación para esto?
Esta pregunta es una variación de otras fallas relacionadas con la división.
El
wiki de la etiqueta
x86
tiene algunos enlaces adicionales:
- problemas de
idiv
/div
: ceroedx
primero o sign-extendeax
en él. . Fallosdiv
32 bits si el cociente 64b / 32b => 32b no cabe realmente en 32b.
El código aparentemente aleatorio al que su depurador parece saltar es el controlador de Excepción Aritmética (también el mismo que Divide by Zero).
Lo que sucede es que su código está experimentando un
Division Overflow
.
Estás haciendo un
IDIV
16 bits / 8 bits.
De la documentación:
Firmado divide AX por r / m8, con el resultado almacenado en: AL ← Cociente, AH ← Restante.
Notará que para la división con un divisor de 8 bits (en su caso BL ) el rango para el cociente es -128 a +127. 044c0h IDIV 85 es 207 (decimal). 207 no cabe en un registro firmado de 8 bits, por lo que obtiene un desbordamiento de división y la causa de su problema inesperado.
Para resolver esto, puede pasar a un divisor de 16 bits.
Para que pueda colocar su divisor en
BX
(registro de 16 bits).
Eso sería
mov bx, 85
.
Lamentablemente no es tan simple.
Cuando se usa un divisor de 16 bits, el procesador supone que el dividendo es de 32 bits con 16 bits altos en
DX
y 16 bits más bajos en
AX
.
DX dividido firmado: AX por r / m16, con el resultado almacenado en AX ← Cociente, DX ← Restante.
Para resolver esto, debe firmar extender 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ía en 0ffffh (todos los bits configurados 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