Приведение DIV в случайное местоположение?

Итак, у меня есть эта точная проблема.

Решение дано в нуле DX, но в моем случае оно уже есть!
Моя программа состоит в том, чтобы просто делить 16-битное число на 8-битное число.

Мой код:

data segment
num1 dw 0204h
num2 db 02h
quotient db ?
remainder db ?
data ends
code segment
assume cs:code,ds:data
start: mov ax,data
mov ds,ax
mov ax,num1
div num2
mov quotient,al
mov remainder,ah
mov ah,4ch
int 21h
code ends
end start

Любое решение?

Ответ 1

Вам очень нужно начать использовать пробелы, чтобы отделить ваш код сборки. Этот код делает что-то чрезвычайно простое (разделение одного числа на другое), но его чрезвычайно трудно читать. Это, очевидно, не так: простой код должен быть прост для чтения! Почему трудно читать? Потому что у вас есть весь ваш шаблонный код, застрявший против кода, который фактически выполняет операцию деления, и мои глаза просто глазуруют. Я не могу выделить важные биты из шаблона. Пробелы свободны; не бойтесь этого. Ваш ассемблер не против.

Напиши так:

data segment

num1      dw 0204h
num2      db 02h
quotient  db ?
remainder db ?

data ends


code segment
assume cs:code, ds:data

start:
    ; Initialize Data Segment (DS)
    mov ax, data
    mov ds, ax

    ; Do the division and save the results
    mov ax, num1
    div num2
    mov quotient, al
    mov remainder, ah

    ; Terminate process
    ; (Note that you should also be setting AL to a result code here!)
    mov ah, 4ch
    int 21h
end start

code ends

Теперь, не намного ли более понятно, что? Кроме того, в то время как MASM/TASM позволят вам избежать неприятностей, не попадайте в плохие привычки. Это еще один способ сделать ваш код нечитаемым и получить неправильные результаты. Существует два разных способа использования символов в коде: один из способов - использовать адрес/смещение этого символа, а другой способ - использовать содержимое/значение этого символа. В MASM/TASM, когда вам нужен адрес/смещение, вам нужно использовать ключевое слово OFFSET. Когда вы используете содержимое/значение, вам не требуется технически, но вы действительно должны обернуть символ в скобках, указав, что он разыменован. Другими словами, вместо:

mov ax, num1

напишите:

mov  ax, [num1]

С этим отрывом от груди, давайте посмотрим, что не так с вашим кодом. Майкл Пётч уже отметил, что это еще один случай, когда MASM/TASM "делают то, что я имею в виду, а не то, что я пишу", это не делает вам никаких выгод. Он выполняет 8-разрядное деление, потому что вы использовали 8-битный операнд (num2) с инструкцией DIV. Это означает, что он действительно делает:

AX / [num2]

с коэффициентом в AL, а остаток в AH. Если коэффициент больше 8 бит, он не будет вписываться в AL, и деление будет переполняться.

Обходной путь состоит в том, чтобы выполнить 16-битное деление, и в этом случае фактор будет помещен в AX, а остаток будет помещен в DX.

Чтобы получить это, напишите код:

mov  ax, [num1]      ; AX = [num1]
xor  dx, dx          ; DX = 0

xor  bx, bx          ; BX = 0
mov  bl, [num2]      ; BL = [num2], BH = 0

div  bx              ; DX:AX / BX

mov  [quotient],  ax
mov  [remainder], dx

(Так как это также clobbering BX, вы можете сохранить его исходное значение, выполнив push bx вверху и pop bx в конце.)


Документация для инструкции DIV содержит удобную таблицу, в которой показано, как 8-разрядные, 16-разрядные и 32-разрядные подразделения работа:

Operand Size        | Dividend  | Divisor   | Quotient  | Remainder | Maximum Quotient
--------------------------------------------------------------------------------------
Word/byte           | AX        | r/m8      | AL        | AH        | 2^8 - 1
Doubleword/word     | DX:AX     | r/m16     | AX        | DX        | 2^16 - 1
Quadword/doubleword | EDX:EAX   | r/m32     | EAX       | EDX       | 2^32 - 1

"divisor" является единственным операндом для команды DIV. "Дивиденд" неявный. Обратите внимание, что "r/m" означает либо регистр, либо операнд памяти.