Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Форматы представления десятичных чисел
Упакованные BCD-числа хранятся по две цифры в байте в виде четырехбитовых групп, называемых тетрадами, причем каждая тетрада представляет собой двоичную комбинацию, соответствующую одной десятичной цифре, т.е. двоичное число в диапазоне 0000b – 1001b. Неупакованное BCD-число содержит одну десятичную цифру в младшей тетраде байта, старшая тетрада должна быть нулевой, однако для команд сложения и вычитания содержимое старшей тетрады несущественно. Команды десятичной коррекции упакованных чисел Для упакованных десятичных чисел допустимы только операции сложения и вычитания. Каждая операция выполняется в два этапа. На первом выполняется операция – сложение или вычитание (add, adc, sub, sbb) двух упакованных десятичных чисел, первое из которых должно находиться в регистре AL, на втором – десятичная коррекция результата в регистре AL (daa, das). Рассмотрим подробнее одну из команд коррекции – daa, коррекция после сложения BCD-чисел. После первого этапа – двоичного сложения правильных BCD-чисел возможно появление неправильного BCD-результата в двух ситуациях: · получена недопустимая тетрада, т.е. тетрада, двоичный эквивалент которой больше 9; · получена допустимая тетрада, но при сложении из нее возник двоичный перенос с весом 16, в то время как правильный вес единицы переноса должен быть равен 10. Отметим, что перенос из младшей тетрады фиксируется флагом AF, а из старшей – CF. Алгоритм выполнения команды daa состоит из двух шагов: · если AF=1 или младшая тетрада регистра AL содержит запрещенную комбинацию, к содержимому AL прибавляется 06 и флаг AF устанавливается в 1; · если CF=1 или старшая тетрада регистра AL содержит запрещенную комбинацию, к содержимому AL прибавляется 60h и флаг CF устанавливается в 1. Например. Содержимое регистров AL=65h и BL=28h, что соответствует десятичным числам 65 и 28. Выполним их сложение
и вычитание
das; AL=37h, AF=1, CF=0, ZF=0 в комментариях показаны значения регистра AL и флагов после выполнения соответствующей команды. Команды десятичной коррекции неупакованных чисел Для неупакованных чисел или, как их еще называют ASCII-чисел, существуют аналогичные команды коррекции после сложения – aaa и вычитания – aas. Сложение и вычитание ASCII-чисел также выполняется в два этапа. Кроме того, над ASCII-числами допустимо выполнение операций умножения и деления. Умножение ASCII-чисел выполняется в два этапа: · умножение одноразрядных сомножителей, представленных байтами, в которых младшие тетрады содержат десятичные цифры, а старшие тетрады – нулевые. Умножение выполняется командой mul, которая формирует в регистре AL двоичное произведение; · коррекция результата с помощью команды aam, которая преобразует полученный результат в двухбайтовое произведение, находящееся в регистрах AH (старший десятичный разряд) и AL (младший разряд). Деление также выполняется в два этапа, но в отличие от остальных команд коррекция выполняется не после, а перед выполнением операции: · коррекция делимого с помощью команды aad, которая предполагает, что в регистрах AH и AL находится двухразрядное делимое, причем AH содержит цифру десятков, а AL – цифру единиц и обе старшие тетрады нулевые. Она формирует в AX соответствующее двоичное число; · деление полученного в AX делимого на одноразрядный делитель, с получением частного в AL и остатка в AH. ПРИМЕР ВЫПОЛНЕНИЯ РАБОТЫ Написать программу сложения двух десятиразрядных неупакованных десятичных чисел. Текст программы:
model SMALL stack 100h
dataseg Ask1 db 0Ah, 0Dh, 'Введите первое слагаемое (не более 10 цифр): $' Ask2 db 0Ah, 0Dh, 'Введите второе слагаемое (не более 10 цифр): $' Buf1 db 11 Len1 db? Opnd1 db 12 dup(? ) Buf2 db 11 Len2 db? Opnd2 db 12 dup(? ) ResT db 0Ah, 0Dh, 'Сумма ' Res db 12 dup(' '), '$' AskCont db 0Ah, 0Dh db 'Завершить работу - Esc, продолжить - ЛЮБАЯ Ê Ë À Â È Ø À ' db '$'
codeseg startupcode push DS pop ES; ES < - DS
BEGIN: ; Ввод первого слагаемого B1: lea DX, Ask1 mov AH, 09h int 21h lea DX, Buf1 mov AH, 0Ah int 21h cmp Len1, 0 je B1 ; проверка 0-9 и очистка старш.тетрады lea BX, Opnd1 xor CX, CX mov CL, Len1 xor SI, SI T1: mov AL, [BX][SI] cmp AL, '0' jb B1; ошибка cmp AL, '9' ja B1; ошибка and AL, 0Fh mov [BX][SI], AL inc SI loop T1 ; прижать к правому краю mov CL, Len1 cmp CL, 10 je E1 mov DI, 9 mov SI, CX dec SI P1: mov AL, [BX][SI] mov [BX][DI], AL dec DI dec SI loop P1 ; обнулить лишнее xor DI, DI mov CL, 10 sub CL, Len1 N1: mov byte ptr [BX][DI], 0 inc DI loop N1 E1: ; Ввод второго слагаемого B2: lea DX, Ask2 mov AH, 09h int 21h lea DX, Buf2 mov AH, 0Ah int 21h cmp Len2, 0 je B2 ; проверка 0-9 и очистка старш.тетрады lea BX, Opnd2 xor CX, CX mov CL, Len2 xor SI, SI T2: mov AL, [BX][SI] cmp AL, '0' jb B2; ошибка cmp AL, '9' ja B2; ошибка and AL, 0Fh mov [BX][SI], AL inc SI loop T2 ; прижать к правому краю mov CL, Len2 cmp CL, 10 je E2 mov DI, 9 mov SI, CX dec SI P2: mov AL, [BX][SI] mov [BX][DI], AL dec DI dec SI loop P2 ; обнулить лишнее xor DI, DI mov CL, 10 sub CL, Len2 N2: mov byte ptr [BX][DI], 0 inc DI loop N2 E2: ; Сложение mov CX, 10 clc lea SI, Opnd1+9 lea DI, Opnd2+9 lea BX, Res+10 A1: mov AL, [SI] adc AL, [DI] aaa mov [BX], AL dec SI dec DI dec BX loop A1 mov AL, 0 adc AL, 0 mov [BX], AL ; Преобразование результата в ASCII mov CX, 11 A2: or byte ptr [BX], 30h inc BX loop A2 ; Вывод результата lea DX, ResT mov AH, 09h int 21h ; Запрос на продолжение работы lea DX, AskCont mov AH, 09h int 21h mov AH, 08h int 21h cmp AL, 27; ESC je QUIT jmp BEGIN
; Конец работы QUIT: exitcode 0 end ВАРИАНТЫ ЗАДАНИЙ 1. Имеются две группы заданий стандартной (варианты 1–5) и повышенной сложности (варианты 6–8), выберите самостоятельно любой вариант из какой–либо группы. 2. Введите два десятичных числа разрядностью не более 10 цифр, выполните преобразование в упакованный BCD-формат, сложите их и выведите результат. 3. Введите два десятичных числа разрядностью не более 10 цифр, выполните преобразование в упакованный BCD-формат, вычтете второе из первого и выведите результат. 4. Введите два десятичных числа, первое разрядностью не более 10 цифр, второе – из одной цифры выполните преобразование в неупакованный BCD-формат, перемножьте их и выведите результат. 5. Введите два десятичных числа разрядностью не более 10 цифр, выполните преобразование в неупакованный BCD-формат, вычтете второе из первого и выведите результат. 6. Введите два десятичных числа, первое разрядностью не более 10 цифр, второе – из одной цифры выполните преобразование в неупакованный BCD-формат, поделите первое на второе и выведите результат. 7. Напишите программу – калькулятор выполняющую действия + –, внутреннее представление чисел – упакованный BCD-формат. 8. Напишите программу – калькулятор выполняющую действия + – *, внутреннее представление чисел – неупакованный BCD-формат. 9. Напишите программу – калькулятор выполняющую действия + – /, внутреннее представление чисел – неупакованный BCD-формат. КОНТРОЛЬНЫЕ ВОПРОСЫ 1. Чем отличаются упакованный и неупакованный BCD-форматы представления десятичных чисел? 2. Что такое десятичная коррекция результата арифметической операции? 3. Почему используются различные команды десятичной коррекции для различных арифметических операций? 4. Как организовать выполнение операций сложения и вычитания над многоразрядными операндами? 5. Зачем нужны команды десятичной арифметики? 6. Почему коррекция для деления выполняется перед операцией, а для остальных операций – после?
ЦЕЛЬ РАБОТЫ Цель настоящей работы – изучение приемов программирования с использованием подпрограмм. ОСНОВНЫЕ СВЕДЕНИЯ Описание подпрограмм Описание подпрограммы в языке ассемблера имеет следующую структуру:
Здесь «тип» - одно из слов NEAR (ближняя) или FAR (дальняя). Если тип не задан, по умолчанию принимается NEAR. Процедура NEAR должна вызываться из того же сегмента кода, в котором она описана. Процедура FAR может вызываться из других сегментов, с другим значением регистра CS. Такие процедуры обычно используются как отдельные объектные модули или в составе библиотек. Команда ret выполняет возврат из процедуры в вызывающую программу. Она не обязана быть последней по тексту процедуры, но является последней по порядку выполнения. Команда ret также имеет ближний и дальний варианты в зависимости от типа подпрограммы, внутри описания которой встретилась команда. Допускается вложение описания подпрограммы внутрь описания другой подпрограммы. В заголовке подпрограммы рекомендуется комментировать ее. Как правило, следует отразить следующие моменты: действие, выполняемое подпрограммой; входные и выходные параметры; ограничения и особенности подпрограммы. Вызов подпрограмм Вызов подпрограммы выполняется командой call. Вызов также бывает ближний или дальний. При ближнем вызове в стеке запоминается текущее значение регистра IP, используемое затем командой ret (ближней) для возврата в точку вызова. При дальнем вызове в стек заносится также значение сегментного регистра CS, что позволяет команде ret (дальней) выполнить возврат в другой сегмент. Тип вызова определяется типом операнда команды. Если в качестве операнда указано имя подпрограммы, то тип FAR или NEAR выбирается в зависимости от описания подпрограммы. Если в качестве операнда используется слово или двойное слово памяти, то выполняется косвенный, соответственно ближний или дальний вызов подпрограммы по адресу, хранящемуся в памяти. При этом в двойном слове младшее слово содержит смещение, старшее слово - сегмент из адреса подпрограммы. Например. Пусть в сегменте данных описаны переменные: FADDR dd? а в сегменте кода описаны подпрограммы: FPROC proc FAR Рассмотрим следующие примеры команд вызова: call FPROC; Дальний прямой вызов п/п FPROC Передача параметров Программист имеет полную свободу в выборе способа передачи входных параметров в подпрограмму и выходных – из подпрограммы, важно лишь, чтобы обработка параметров в подпрограмме была согласована с заданием параметров в вызывающей программе. Чаще всего применяется передача параметров через регистры или через стек. При передаче через регистры программа перед вызовом заносит входные параметры в некоторые регистры процессора, а после возврата выбирает из регистров значения результатов. При передаче через стек программа перед вызовом заносит параметры в стек с помощью команды push. Обычно при этом считается, что подпрограмма имеет только входные параметры (как функция в языке Си). Чтобы подпрограмма могла изменять значения параметров, следует передавать ей не сами значения, а адреса параметров. Для доступа к параметрам, переданным в стеке, в начале подпрограммы обычно выполняются команды: push BP После этого можно адресовать величины в стеке, указывая их смещения относительно верхушки стека, адрес которой – в регистре BP. При подсчете смещения нужно учитывать, что команда call, как отмечалось выше, помещает в стек адрес возврата (одно или два слова). Удобно для адресации параметров описать соответствующую структуру данных. Можно применять смешанные способы передачи параметров. В частности, для подпрограмм-функций удобно возвращать результат в регистре, даже если входные параметры получены в стеке. Рассмотрим пример. Пусть подпрограмма типа near имеет два словных параметра, передаваемых через стек. В этом случае после вызова подпрограммы, сохранения и загрузки регистра BP (см. выше), стек будет выглядеть, как показано ниже:
Если описать следующую структуру: __arg struc __saveBP dw? __retAddr dw? __Param2 dw? __Param1 dw? __arg ends, то доступ к параметрам можно осуществить с помощью команд: mov AX, __Param1[BP]; загрузить в AX значение первого параметра Для облегчения чистки стека от переданных параметров используется разновидность команды ret с операндом – числом байтов, которые нужно убрать из стека сразу после возврата. Это позволяет вызывающей программе не заботиться об удалении параметров из стека. Для нашего примера команда возврата из подпрограммы может выглядеть следующим образом: ret 4 Сохранение регистров Каждая подпрограмма должна либо сохранять значения всех регистров процессора (кроме тех, которые используются для возврата результатов), либо, в крайнем случае, в описании подпрограммы должно быть четко указано, какие регистры она портит. Для сохранения регистров используется стек. Команды push служат для помещения регистров в стек, а pop – для их восстановления перед возвратом из подпрограммы. Сохранение регистров должно выполняться после загрузки BP (см. предыдущий параграф). Локальные переменные Переменные, размещенные в сегменте данных, являются статическими (аналогично переменным с классом static в Си). Конечно, их можно рассматривать как локальные переменные подпрограмм, обеспечив локализацию области действия с помощью директивы locals (см. ниже). Однако такое статическое распределение памяти под локальные переменные не соответствует понятию локальных переменных в блочных языках типа Pascal или C, поскольку время существования таких переменных – время существования программы. Для того чтобы решить данную проблему, т.е. обеспечить динамическое распределение памяти под локальные переменные, следует выделять для них память в стеке (как это делается в Pascal или C). Предположим, что в подпрограмме должно быть две локальные переменные длиной в слово. Чтобы обеспечить выделение памяти, для них перед командами сохранения регистров следует добавить команду: sub SP, 4 которая резервирует в стеке два слова. После выполнения этой команды стек будет выглядеть следующим образом:
И, если определить структуру: __locvars struc __var1 dw? __var2 dw? __locvars ends, то доступ к локальным переменным можно осуществить с помощью команд: mov AX, __var1[BP-4]; загрузить в AX значение 1-й локальной переменной Чистка стека от локальных переменных должна выполняться после восстановления сохраненных регистров, это можно сделать с помощью команды: add SP, 4 или mov SP, BP Популярное:
|
Последнее изменение этой страницы: 2016-03-25; Просмотров: 1415; Нарушение авторского права страницы