Загрузка данных
Ход работы и заключение
Ход работы
1. Постановка задачи.
По заданию требовалось вычислить два целочисленных выражения средствами языка Ассемблера, встроенного в Borland Pascal (в нашей реализации — ассемблерная вставка в C++):
· D = (C / A) * C + B
· E = (B − C) / A * C
где A — int (16 бит), B — longint (32 бита), C — integer (16 бит).
2. Выбор типов данных в C++.
Поскольку задание написано на типах языка Pascal, была учтена разница в разрядности:
По заданию (Pascal) Размер Тип в C++
int (A) 2 байта int (берём младшие 16 бит через word ptr)
longint (B) 4 байта long
integer (C) 2 байта short
Для результатов D и E использован тип long (4 байта), так как результаты могут превышать 16-битный диапазон.
3. Реализация ассемблерной вставки.
Вычисления выполнены двумя блоками __asm { ... }.
Первый блок — D = (C / A) * C + B:
· mov ax, c — загрузка C в регистр AX;
· cwd — знаковое расширение AX до пары DX:AX (подготовка к делению);
· idiv word ptr a — знаковое деление DX:AX на A, частное в AX;
· imul c — знаковое умножение AX на C, результат в DX:AX;
· add ax, word ptr b — сложение младших 16 бит с младшей половиной B;
· adc dx, word ptr b + 2 — сложение старших 16 бит со старшей половиной B с учётом переноса;
· mov word ptr d, ax и mov word ptr d + 2, dx — сохранение результата в переменную D.
Второй блок — E = (B − C) / A * C:
· mov ax, word ptr b и mov dx, word ptr b + 2 — загрузка 32-битного B в DX:AX;
· sub ax, c — вычитание C из младшей половины;
· sbb dx, 0 — вычитание заёма из старшей половины;
· idiv word ptr a — знаковое деление (B − C) на A;
· imul c — умножение результата на C;
· сохранение результата в переменную E.
4. Вывод результатов.
Для вывода использован поток cout с переключателями dec и hex: значения A, B, C, D, E выводятся как в десятичном, так и в шестнадцатеричном виде.
5. Проверка на контрольном примере.
При A = 4, B = 1000, C = 20:
· D = (20 / 4) * 20 + 1000 = 5 * 20 + 1000 = 1100 = 0x44C
· E = (1000 − 20) / 4 * 20 = 980 / 4 * 20 = 245 * 20 = 4900 = 0x1324
Результаты совпали с ручным расчётом.
---
Заключение
В ходе лабораторной работы были изучены и применены на практике основные арифметические команды языка Ассемблера: add, adc, sub, sbb, imul, idiv, а также команда преобразования данных cwd.
В результате работы были сделаны следующие выводы:
1. Команды add и adc (а также sub и sbb) применяются совместно для выполнения 32-битной арифметики на 16-битных регистрах. Младшие половины чисел обрабатываются командами add/sub, а старшие — командами adc/sbb с учётом переноса (заёма), сохранённого в флаге CF.
2. Команда cwd обязательна перед знаковым делением idiv, когда делимое — 16-битное число, лежащее в AX. Она расширяет его до пары DX:AX, копируя знак в DX, что необходимо для корректной работы деления.
3. Команды idiv и imul используются для знаковых чисел, а div и mul — для беззнаковых. Поскольку исходные данные задачи знаковые, применены именно idiv и imul.
4. Команда imul при 16-битных операндах даёт 32-битный результат, который помещается в пару DX:AX, что позволяет корректно получать результаты, выходящие за пределы 16-битного диапазона.
5. Была учтена разница в разрядности типов данных между языком Pascal (где int = 2 байта) и C++ (где int = 4 байта). Для точного соответствия заданию переменные A и C объявлены как short, B — как long, а в ассемблерных вставках для доступа к младшим 16 битам 32-битных переменных использовался оператор word ptr.
6. Результаты вычислений, полученные с помощью ассемблерных вставок, совпали с результатами ручного расчёта, что подтверждает корректность написанной программы.
Таким образом, цель работы — ознакомление с языком Ассемблера и получение навыков работы с арифметическими командами и командами преобразования данных — была достигнута в полном объёме.