assembly x86 x86-16 tasm dosbox

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:

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