Сборка 8086 на DOSBox: ошибка с инструкцией idiv?

Я помогал своему другу отладить его программу, и мы сузили ее до проблемы, которая возникает даже здесь:

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

end start

После сборки его с помощью tasm 4.1 и запуска его на DOSBox 0.74, он входит в бесконечный цикл. При проверке с помощью отладчика турбо можно увидеть, что это происходит после idiv инструкция, которая по какой-то причине изменяет cs а также ip регистры, и после двух, казалось бы, случайных инструкций восстанавливает их, чтобы указать на idiv линия, выполняя это снова до бесконечности.

У кого-нибудь есть объяснение этому?

1 ответ

Решение

Этот вопрос является разновидностью других ошибок, связанных с разделением. x86 В теге wiki есть несколько дополнительных ссылок:


Кажется, что случайный код, к которому переходит ваш отладчик, это обработчик арифметических исключений (также такой же, как Divide by Zero). Происходит то, что ваш код испытывает Division Overflow, Вы делаете 16-битный /8-битный IDIV. Из документации:

Разделите знак AX на r/m8, и результат сохранится в: AL ← Фактор, AH ← Остаток.

Вы заметите, что для деления с 8-битным делителем (в вашем случае BL) диапазон для коэффициента составляет от -128 до +127. 044c0h IDIV 85 - 207 (десятичное число). 207 не помещается в 8-битный регистр со знаком, поэтому вы получаете переполнение деления и причину непредвиденной проблемы.

Для решения этой проблемы вы можете перейти на 16-битный делитель. Таким образом, вы можете поместить свой делитель в BX (16-битный регистр). Это было бы mov bx, 85, К сожалению, это не так просто. При использовании 16-битного делителя процессор предполагает, что дивиденд составляет 32 бита с высокими 16-битными в DX и более низкими 16-битными в AX.

Разделите со знаком DX:AX на r/m16, результат сохранится в AX ← Частное, DX ← Остаток.

Чтобы решить эту проблему, вы должны подписать расширение 16-битного значения в AX. Это просто, поскольку вам нужно использовать инструкцию CWD только после помещения значения в AX. Из справочника набора команд

DX:AX ← знак-удлинитель AX.

Фактически, если старший значащий бит (MSB) AX равен 0, DX станет 0. Если MSB равен 1, то DX станет 0ffffh (все биты установлены в единицу). Знаковый бит числа - это MSB.

Имея все это в виду, ваш код деления может быть скорректирован, чтобы принять 16-битный делитель:

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
Другие вопросы по тегам