Сдвиг и округление
Методические рекомендации по выполнению лабораторной работы по МДК.01.01 Системное программирование для студентов 2 курса
Составитель: Токаева Я.Ю.,
преподаватель колледжа
Сдвиг и округление. Методические рекомендации по выполнению лабораторной работы по МДК.01.01 Системное программирование для студентов 2 курса /Составитель: Токаева Я.Ю./ ‒ Череповец: Череповецкий металлургический колледж имени академика И.П. Бардина, 2019. – 23 с.
Рецензенты:
Данная методическая разработка рассмотрена на заседании цикловой комиссии «Информационные технологии и вычислительная техника» и рекомендована к применению.
Председатель: /Молоткова Л.Н./
__________________2019 г.
Протокол №___
Содержание
Цель работы | ||
Средства обучение | ||
Теоретическое обоснование | ||
Задание | ||
Ход работы | ||
Рекомендации по оформлению отчета | ||
Контрольные вопросы | ||
Литература | ||
Приложение А – Варианты заданий |
Лабораторная работа № 15 «Сдвиг и округление»
1 Цель работы: закрепление на практике принципов обработки двоичных данных в языке Ассемблер на примере операций сдвига и округления.
2 Средства обучения:
· ПЭВМ;
· методические рекомендации.
Теоретическое обоснование
Операции сдвига вправо и сдвига влево сдвигают биты в переменной на заданное количество позиций. Каждая команда сдвига имеет две разновидности:
<мнемокод> <операнд>, <непосредственный операнд>
<мнемокод> <операнд>, CL
Первый операнд должен быть регистром или ячейкой памяти. Именно в нем осуществляется сдвиг. Второй операнд определяет количество позиций для сдвига, которое задается непосредственным операндом или хранится в регистре CL (и только CL).
Команды сдвига меняют флаги CF, OF, PF, SF и ZF.
Существует несколько разновидностей сдвигов, которые отличаются тем, как заполняются «освобождающиеся» биты.
При логическом сдвиге «освобождающиеся» биты заполняются нулями. Последний ушедший бит сохраняется во флаге CF.
SHL операнд, количество_сдвигов
SHR операнд, количество_сдвигов
SHL и SHR сдвигают биты операнда (регистр/память) влево или вправо соответственно на один разряд.
Указанное выше действие повторяется количество раз, равное значению второго операнда.
Пример:
; al = 01011011 (двоичное)
shr al, 3
Это означает: сдвиг всех битов регистра al на 3 разряда вправо. Так что al станет 00001011. Биты слева заполняются нулями, а биты справа выдвигаются. Последний выдвинутый бит, становится значением флага переноса cf.
Бит переноса это бит флагового регистра процессора. Этот регистр не такой как eax или ecx, к которому вы можете непосредственно обращаться (хотя есть опкоды, которые это делают), его содержание, зависит от результатов многих команд. Флаг переноса это бит во флаговом регистре и что он может быть установлен (т.е. равен 1) или сброшен (равен 0).
Команда shl такая же, как и shr, но сдвигает влево.
; bl = 11100101 (двоичное)
shl bl, 2
После выполнения команды регистр bl будет равен 10010100 (двоичное). Два последних бита заполнились нулями, флаг переноса установлен, потому, что последний выдвинутый слева бит был равен 1.
Арифметический сдвиг влево эквивалентен логическому сдвигу влево (это одна и та же команда) – «освобождающие» биты заполняются нулями. При арифметическом сдвиге вправо «освобождающиеся» биты заполняются знаковым битом. Последний ушедший бит сохраняется во флаге CF.
SAL операнд, количество_сдвигов
SAR операнд, количество_сдвигов
Команда SAL такая же, как SHL, а вот SAR не совсем такая, как SHR. Команда SAR также, как и SHR сдвигает все биты операнда вправо на один разряд, при этом выдвигаемый справа бит становится значением флага переноса CF. Обратите внимание: одновременно слева в операнд вдвигается не нулевой бит, как в SHR, а значение старшего бита операнда.
Пример:
al = 10100110
sar al, 3
al = 11110100
sar al, 2
al = 11111101
bl = 00100110
sar bl, 3
bl = 00000010
При циклическом сдвиге «освобождающиеся» биты заполняются ушедшими битами. Последний ушедший бит сохраняется во флаге CF.
rol операнд, количество_сдвигов; циклический сдвиг операнда влево
ror операнд, количество_сдвигов; циклический сдвиг операнда вправо
rcl операнд, количество_сдвигов; циклический сдвиг операнда влево через флаг переноса
rcr операнд, количество_сдвигов; циклический сдвиг операнда вправо через флаг переноса
Циклический сдвиг напоминает смещение, выдвигаемые биты, снова вдвигаются с другой стороны
Пример команды ror (циклический сдвиг вправо) представлен на рисунке 1.
Рисунок 1 – Команда ROR
Как видно из рисунка 1, биты вращаются, то есть каждый бит, который выталкивается снова вставляется с другой стороны. Флаг переноса CF содержит значение последнего выдвинутого бита.
RCL и RCR сдвигают все биты операнда влево (для RCL) или вправо (для RCR) на один разряд, при этом старший (для RCL) или младший (для RCR) бит становится значением флага переноса CF; одновременно старое значение флага переноса CF вдвигается в операнд справа (для RCL) или слева (для RCR) и становится значением младшего (для RCL) или старшего (для RCR) бита операнда. Указанные действия повторяются количество раз, равное значению второго операнда.
ROL и ROR сдвигают все биты операнда влево (для ROL) или вправо (для ROR) на один разряд, при этом старший (для ROL) или младший(для ROR) бит операнда вдвигается в операнд справа (для ROL) или слева (для ROR) и становится значением младшего (для ROL) или старшего (для ROR) бита операнда; одновременно выдвигаемый бит становится значением флага переноса CF. Указанные действия повторяются количество раз, равное значению второго операнда.
Расширенные сдвиги немного отличаются от остальных сдвигов. В расширенных сдвигах участвуют два регистра или ячейка памяти и регистр, которые как бы объединяются в единое целое и «освобождающиеся» биты одного операнда заполняются битами из другого операнда.
SHLD <операнд1>, <операнд2>, <количество>; Расширенный сдвиг влево
SHRD <операнд1>, <операнд2>, <количество>; Расширенный сдвиг вправо
Команда SHLD сдвигает влево биты операнда1 на указанное количество позиций. Младшие («освободившиеся») биты операнда1 заполняются старшими битами операнда2. Сам операнд2 не меняется.
Команда SHRD сдвигает вправо биты операнда1 на указанное количество позиций. Старшие («освободившиеся») биты операнда1 заполняются младшими битами операнда2. Сам операнд2 не меняется.
Количество, как и в других операциях сдвига, задается непосредственным операндом или хранится в регистре CL. Но используются только последние 5 бит операнда, определяющего количество, т.е. максимальное количество позиций сдвига равно 32.
Команды расширенного сдвига обычно используют для создания упакованных данных.
Рассмотрим процесс округления числа до двух десятичных знаков после запятой. Если число равно 12,345, то необходимо прибавить 5 к отбрасываемому разряду и сдвинуть число вправо на один десятичный разряд:
Число: 12,345
Плюс 5: +5
------
Округленное число: 12,350 = 12,35
Если округляемое число равно 12,3455, то необходимо прибавить 50 и сдвинуть на два десятичных разряда. Для 12,34555 необходимо прибавить 500 и сдвинуть на три десятичных разряда:
12,3455+50 - - - 12,3505 = 12,35
12,34555+500 - - -12,35055 = 12,35
К числу, имеющему шесть знаков после запятой, необходимо прибавить 5000 и сдвинуть на четыре десятичных разряда и т.д. Поскольку данные представляются в компьютере в двоичном виде, то 12345 выглядит как шест.3039. Прибавляя 5 к 3039, получим 303E, что соответствует числу 12350 в десятичном представлении. Пока все хорошо. Но вот сдвиг на одну двоичную цифру дает в результате шест.181F, или 1675 – т.е. сдвиг на одну двоичную цифру просто делит число пополам. Но нам необходим такой сдвиг, который эквивалентен сдвигу вправо на одну десятичную цифру. Такой сдвиг можно осуществить делением на 10 (шест.A):
Шест.303E: Шест.A = 4D3 или дес.1235
Преобразование шест.4D3 в ASCII-формат дает число 1235. Теперь остается лишь вставить запятую в правильную позицию числа 12,35, и можно выдать на экран округленное и сдвинутое значение.
Таким образом, можно округлять и сдвигать любые двоичные числа. Для трех знаков после запятой необходимо прибавить 5 и разделить на 10, для четырех знаков после запятой: прибавить 50 и разделить на 100. Возможно, вы заметили модель: фактор округления (5, 50, 500 и т.д.) всегда составляет половину фактора сдвига (10, 100, 1000 и т.д.).
Конечно, десятичная запятая в двоичном числе только подразумевается.
Пример: преобразование времени и расценки работ для расчета зарплаты.
Программа позволяет вводить с клавиатуры значения продолжительности и расценки работ и отображать на экран рассчитанную величину заработанной платы. Для краткости в программе опущены некоторые проверки на ошибку.
TITLE SCREMP (EXE) Ввод времени и расценки,
;вывод величины оплаты
; ----------------------------------------------------
STACKSG SEGMENT PARA STACK 'Stack'
DW 32 DUP(?)
STACKSG ENDS
; ----------------------------------------------------
DATASG SEGMENT PARA 'Data'
HRSPAR LABLE BYTE;Список параметров для
; ввода времени:
MAXHLEN DB 6;---------------------
ACTHLEN DB?
HRSFLD DB 6 DUP(?)
RATEPAR LABLE BYTE;Список параметров для
; ввода расценки:
MAXRLEN DB 6;---------------------
ACTRLEN DB?
RATEFLN DB 6 DUP(?)
MESSG1 DB 'Hours worked? ','$'
MESSG2 DB 'Rate of pay? ','$'
MESSG3 DB 'Wage = '
ASCWAGE DB 10 DUP(30H), 13, 10, '$'
ADJUST DW?
ASCHRS DB 0
ASCRATE DB 0
BINVAL DW 00
BINHRS DW 00
BINRATE DW 00
COL DB 00
DECIND DB 00
MULT10 DW 01
NODEC DW 00
ROW DB 00
SHIFT DW?
TENWD DW 10
DATASG ENDS
; ----------------------------------------------------
CODESG SEGMENT PARA 'Code'
BEGIN PROC FAR
ASSUME CS:CODESG,DS:DATASG,SS:STACKSG,ES:DATASG
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATASG
MOV DS,AX
MOV ES,AX
MOV AX,0600H
CALL Q10SCR;Очистить экран
CALL Q20CURS;Установить курсор
A20LOOP:
CALL B10INPT;Ввести время и расценку
CMP ACTHLEN,00;Завершить работу?
JE A30
CALL D10HOUR;Получить двоичное время
CALL E10RATE;Получить двоичную расценку
CALL F10MULT;Расчитать оплату
CALL G10WAGE;Преобразовать в ASCII
CALL K10DISP;Выдать результат на экран
JMP A20LOOP
A30:
MOV AX,0600H
CALL Q10SCR;Очистить экран
RET;Выйти из программы
BEGIN ENDP
; Ввод времени и расценки
; ----------------------------------------------------
B10INPT PROC
LEA DX,MESSG1;Запрос для ввода времени
MOV AH,09
INT 21H
LEA DX,HRSPAR;Ввести время
MOV AH,0AH
INT 21H
CMP ACTHLEN,00;Пустой ввод?
JNE B20
RET; да - вернуться A20LOOP
B20:
MOV COL,25;Установить столбец
CALL Q20CURS
LEA DX,MESSG2;Запрос для ввода расценки
MOV AH,09
INT 21H
LEA DX,RATEPAR;Ввести расценку
MOV AH,0AH
INT 21H
RET
B10INPT ENDP
; Обработка времени:
; -----------------
D10HOUR PROC
MOV NODEC,00
MOV CL,ACTHLEN
SUB CH,CH
LEA SI,HRSFLD-1;Установить правую позицию
ADD SI,CX; времени
CALL M10ASBI;Преобразовать в двоичное
MOV AX,BINVAL
MOV BINHRS,AX
RET
D10HOUR ENDP
; Обработка расценки:
; ------------------
E10RATE PROC
MOV CL,ACTRLEN
SUB CH,CH
LEA SI,RATEFLD-1;Установить правую позицию
ADD SI.CX; расценки
CALL M10ASBI;Преобразовать в двоичное
MOV AX,BINVAL
MOV BINRATE,AX
RET
E10RATE ENDP
; Умножение, округление и сдвиг:
; -----------------------------
F10MULT PROC
MOV CX,05
LEA DI,ASCWAGE;Установить формат оплаты
MOV AX,3030H; в код ASCII (30)
CLD
REP STOSW
MOV SHIFT,10
MOV ADJUST,00
MOV CX,NODEC
CMP CL,06;Если более 6 десятичных
JA F40; знаков, то ошибка
DEC CX
DEC CX
JLE F30;Обойти, если менее 3 знаков
MOV NODEC,02
MOV AX,01
F20:
MUL TENWD;Вычислить фактор сдвига
LOOP F20
MOV SHIFT,AX
SHR AX,1;Округлить результат
MOV ADJUST,AX
F30:
MOV AX,BINHRS
MUL BINRATE;Вычислить оплату
ADD AX,ADJUST;Округлить оплату
ADC DX,00
CMP DX,SHIFT;Результат слишком велик
JB F50; для команды DIV?
F40:
SUB AX,AX
JMP F70
F50:
CMP ADJUST,00;Сдвиг нее требуется?
JZ F80
DIV SHIFT;Сдвинуть оплату
F70: SUB DX,DX;Стереть остаток
F80: RET
F10MULT ENDP
; Преобразование в ASCII формат:
; -----------------------------
G10WAGE PROC
LEA SI,ASCWAGE+7;Установить дес. точку
MOV BYTE PTR[SI],'.'
ADD SI,NODEC;Установить правую позицию
G30:
CMP BYTE PTR[SI],'.'
JNE G35;Обойти, если дес.поз.
DEC SI
G35:
CMP DX,00;Если dx:ax < 10,
JNZ G40
CMP AX,0010; то операция завершена
JB G50
G40:
DIV TENWD;Остаток - ASCII-цифра
OR DL,30H
MOV [SI],DL;Записать ASCII символ
DEC SI
SUB DX,DX;Стереть остаток
JMP G30
G50:
OR AL,30H;Записать последний ASCII
MOV [SI],AL; символ
RET
G10WAGE ENDP
; Вывод величины оплаты:
; ---------------------
K10DISP PROC
MOV COL,50;Установить столбец
CALL Q20CURS
MOV CX,09
LEA SI,ASCWAGE
K20:;Стереть лидирующие нули
CMP BYTE PTR[SI],30H
JNE K30; пробелами
MOV BYTE PTR[SI],20H
INC SI
LOOP K20
K30:
LEA DX,MESSG3;Вывод на экран
MOV AH,09
INT 21H
CMP ROW,20;Последняя строка экрана?
JAE K80
INC ROW; нет - увеличить строку
JMP K90
K80:
MOV AX,0601H; да --
CALL Q10SCR; прокрутить и
MOV COL,00; установить курсор
CALL Q20CURS
K90: RET
K10DISP ENDP
; Преобразование ASCII-чисел
; в двоичное представление:
; --------------------------
M10ASBI PROC
MOV MULT10,0001
MOV BINVAL,00
MOV DECIND,00
SUB BX,BX
M20:
MOV AL,[SI];ASCII-символ
CMP AL,'.';Обойти, если дес.точка
JNE M40
MOV DECIND,01
JMP M90
M40:
AND AX,000FH
MUL MULT10;Умножить на фактор
ADD BINVAL,AX;Сложить с дв.значением
MOV AX,MULT10;Вычислить следующий
MUL TENVD; фактор x 10
MOV MULT10,AX
CMP DECIND,00;Десятичная точка?
JNZ M90
INC BX; да - обойти точку
M90:
DEC SI
LOOP M20
;Конец цикла
CMP DECIND,00;Была дес.точка?
JZ M100; да --
ADD NODEC,BX; сложить с итогом
M100: RET
M10ASBI ENDP
; Прокрутка экрана:
; ----------------
Q10SCR PROC NEAR;AX установлен при вызове
MOV BH,30;Цвет (07 для ч/б)
SUB CX,CX
MOV DX,184FH
INT 10H
RET
Q10SCR ENDP
; Установка курсора:
; -----------------
Q20CURS PROC NEAR
MOV AH,02
SUB BH,BH
MOV DH,ROW
MOV DL,COL
INT 10H
RET
Q20CURS ENDP
CODESG ENDS
END BEGIN
Программа содержит следующие процедуры:
· B10INPT. Вводит значения времени работы на ее расценку с клавиатуры. Эти значения могут содержать десятичную запятую.
· D10HOUR. Выполняет преобразование значения времени из ASCII в двоичный формат.
· E10RATE. Выполняет преобразование значения расценки из ASCII в двоичный формат.
· F10MULT. Выполняет умножение, округление и сдвиг. Величина зарплаты без дробной части или с одним или двумя знаками после запятой не требует округления и сдвига. Данная процедура ограничена тем, что позволяет обрабатывать величину зарплаты с точностью до шести десятичных знаков, что, конечно, больше, чем требуется.
· G10WAGE. Вставляет десятичную запятую, определяет правую позицию для начала записи ASCII символов и преобразует двоичное значение зарплаты в ASCII-формат.
· K10DISP. Заменяет лидирующие нули на пробелы и выводит результат на экран.
· M10ASBI. Преобразует ASCII в двоичный формат (общая процедура для времени и расценки) и определяет число цифр после запятой в введенном значении.
Первое ограничение в данной программе, состоит в том, что допускает не более шести десятичных знаков после запятой. Другое ограничение – размер самой зарплаты и тот факт, что сдвиг включает деление на число, кратное 10, а преобразование в ASCII-формат включает деление на 10. Если значение времени или расценки содержит больше шести десятичных знаков или зарплата превышает величину около 655350, то программа выдает нулевой результат. На практике программа может предусмотреть в данном случае вывод предупреждающего сообщения или иметь подпрограммы для исключения таких ограничений.
Программа, разработанная для пользователей, не являющихся программистами, должна не только выдавать предупреждающие сообщения, но также проверять корректность вводимых значений. Правильными символами при вводе числовых значений являются цифры от 0 до 9 и символ десятичной запятой. Для любых других символов программа должна выдать предупреждающее сообщение и вновь повторить запрос на ввод.
Полезной командой для проверки корректности вводимых символов является XLAT.
Тщательно проверяйте программы для любых возможных состояний: нулевое значение, максимально большие и малые значения, отрицательные значения.
Некоторые применения программ допускают наличие отрицательных величин. Знак минус может устанавливаться после числа, например, 12,34-, или перед числом -12,34. Программа может проверять наличие минуса при преобразовании в двоичный формат. Можно оставить двоичное число положительным, но установить соответствующий индикатор исходной отрицательной величины. После завершения арифметических операций знак минус при необходимости может быть вставлен в ASCII поле.
Если необходимо, чтобы двоичное число было также отрицательным, то можно преобразовать, как обычно, ASCII-формат в двоичный, а для изменения знака двоичного числа воспользоваться командами преобразования знака.
Будьте внимательны при использовании команд IMUL и IDIV для обработки знаковых данных. Для округления отрицательных чисел следует не прибавлять, а вычитать фактор 5.
Задание
Привести решения задач, представленных в приложении А, согласно своему варианту.
5 Ход работы:
· изучить теоретическое обоснование работы;
· выполнить задание согласно варианту (приложение А);
· ответить на контрольные вопросы;
· сделать выводы по проделанной работе;
· оформить отчет.