Сборка 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 есть несколько дополнительных ссылок:
idiv
/div
проблемы: нольedx
сначала или подписать-продлитьeax
внутрь., 32bitdiv
ошибки, если частное 64b/32b => 32b не вписывается в 32b.
Кажется, что случайный код, к которому переходит ваш отладчик, это обработчик арифметических исключений (также такой же, как 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