M_Asm2009LS
.pdf2. Розробити процедуру для обчислення виразу без використання операцій множення та ділення :
|
Вираз |
Змінні |
|
1 |
a=a*2+(c/8+(a*8+b/4+c)*2-9)/2+c |
a=2ah b=33 c=4ch d=43h |
|
2 |
a=b/8+(d*16-(d*256-(a*2+c)*4)/2)-a |
a=6ch b=72h c=32h d=19 |
|
3 |
a=b/32-(c*16+2*(а*2+d/8-1))+c*128 |
a=71 b=2ah c=4bh d=68h |
|
4 |
a=(b*4-b*2-(c*4+(d+7)/4)/8+d*256-a |
a=31h b=73 |
c=14 d=67 |
5 |
a=b/4-d*8+(а*512-d*128)/2+(b+с)*2 |
a=13h b=24 |
c=1eh d=5bh |
6 |
a=b*512+c/4-(8*a+32*b)/1024+8*d |
a=37h b=39 |
c=6bh d=2eh |
7 |
a=(a*64+(d*1024-(b+c)*2)/32-c)/2+b |
a=34h b=93 |
c=3eh d=97 |
8 |
a=a*8+c*2+(d*32-(6+(a+c)*2)/4)*2+b |
a=31h b=64 |
c=56 d=15h |
9 |
a=a*1024-(b+(c+1)*8+d/4)*8+c/16 |
a=4ch b=44 c=85 d=2ch |
|
10 |
a=b*512-(d*32+(b+1+c)/4)-d/4-c*4 |
a=30 b=3eh c=2ah d=22 |
|
11 |
a=(a*4+b*32-(d*8+1)/4-c+1)/4+b*4-c |
a=1dh b=38h c=74 d=3ah |
|
12 |
a=(b*128-d*64+(25-(b+c)/32)-a*16)/2 |
a=57h b=31h c=46 d=3fh |
|
13 |
a=b*16+(c*128+(d-1)/2+a*32)/256 |
a=1bh b=5eh c=2ah d=15h |
|
14 |
a=(a*64-b*16-(d+b*32)/8+c*16)/256 |
a=70h b=83 |
c=50 d=16h |
15 |
a=b*32-(c/8+d*4+9-b*128)/256-a*16 |
a=73 b=95 c=89 d=24h |
|
16 |
a=a*1024-(c*32+(d+1)/8-a/4)+b*8)/2 |
a=36h b=39 |
c=43 d=33 |
17 |
a=b*512-c*8+(7+(b-c)/8-d/4)/2-a*16 |
a=25h b=3eh c=29 d=82 |
|
18 |
a=b*16+c*8-(c+a)/2-(b*64+d+7)/128 |
a=30h b=62h c=24 d=17 |
|
19 |
a=a*256+((b+d*2-1)/4-(b+c)/2)*2-c |
a=10 b=21h c=18h d=86 |
|
20 |
a=(b+а)*64+c*32-(a/2+d*128)/256 |
a=33h b=54 |
c=83 d=38h |
3. Задано значення без знакової змінної a розміром у байт. Розробити процедуру, яка обчислює:
1)суму чисел, записаних у старшій та молодшій тетрадах;
2)кількість бітів зі значенням 1, які розташовані з 1 до 6 бітів
убайті;
3)кількість бітів зі значенням 1, які розташовані з 0 до 5 бітів
убайті;
4)різницю між числами в старшій та молодшій тетрадах;
5)різницю між числом в бітах з 0 до 5 та числом у бітах з 3 до 7;
6)кількість бітів зі значенням 0;
7)кількість бітів зі значенням 1;
8)різницю між кількістю бітів зі значенням 1 та 0;
9)різницю між числами в молодшій та старшій тетрадах;
71
10)побітову логічну суму значень у старшій та молодшій тетрадах;
11)побітову логічну суму за модулем 2 чисел у старшій та молодшій тетрадах;
12)побітовий логічний добуток чисел старшої та молодшої тетрад;
13)побітовий логічний добуток бітів з 1 до 3 та з 5 до 7;
14)побітову логічну суму бітів з 0 до 2 та з 5 до 7;
15)побітову логічну суму за модулем 2 бітів з 2 до 5 та з 4 до
7;
16)кількість бітів зі значенням 1 в розрядах від 1 до 6;
17)кількість бітів зі значенням 0 в розрядах від 2 до 5;
18)суму числа молодшої тетради та інвертованого числа старшої тетради;
19)різницю числа молодшої тетради та інвертованого числа старшої тетради;
20)суму інвертованого числа молодшої тетради та інвертованого числа старшої тетради.
4.Написати процедуру кодування символьного рядка, помінявши місцями для кожного байта рядка, вміст старшої та молодшої тетради.
72
Лабораторна робота № 6 Програмування мовою асемблера цілочислових виразів.
Мета: вивчення команд множення та ділення цілочислових даних. Програмування арифметичних виразів. Ознайомлення з деякими алгоритмами введення-виведення цілочислових даних.
План заняття.
1.Множення цілих чисел. Команди MUL, IMUL
2.Ділення цілих чисел. Команди DIV, IDIV. Команди перетворення
3.Алгоритми введення-виведення цілих чисел.
Теоретичні відомості
1. Множення цілих чисел. Команди MUL, IMUL
Команда множення цілих чисел без знака – MUL
Загальний вигляд команди
MUL src; ext:acc=(acc)*(src).
Команда забезпечує множення вмісту акумулятора (acc) на вміст джерела (src) і запис результату (добутку подвійної довжини двох співмножників) в розширення акумулятора – ext та акумулятор – acc.
Алгоритм роботи
Для правильного виконання команди множення MUL потрібно до виконання цієї команди завантажити один із співмножників в акумулятор, другий співмножник – вміст (src). Обидва співмножники повинні бути однакового типу, тобто мати однакову довжину. Якщо тип співмножника byte, то акумулятор – регістр AL, якщо тип співмножника word, то акумулятор – регістр AX, якщо ж тип dword, то акумулятор – регістр EAX. У залежності від типу співмножників відповідно вибирається розширення ext акумулятора: якщо тип byte, то розширення акумулятора ext – регістр AH, якщо тип word, то розширення акумулятора ext – регістр DX, а якщо ж тип dword, то розширення акумулятора – регістр EDX.
Стан прапорців після виконання команди MUL: якщо вміст (ext) = 0,
тобто старша половина результату нульова, то of=0 та cf=0, у противному випадку – of=1 та cf=1, значення прапорців sf, zf, af, pf – не визначені.
Команда множення цілих чисел зі знаком – IMUL
Загальний вигляд команди має три форми:
IMUL src; ext:acc=(acc)*(src);
73
IMUL reg,src; reg=(reg)*(src);
IMUL dst,src,imm; dst=(src)*imm.
Операнди в усіх формах цих команд множення трактуються як цілі числа зі знаком, причому від’ємні числа зображуються в доповняльному коді.
Алгоритм роботи
Алгоритм команди з одним операндом: вміст акумулятора (acc) множиться на вміст джерела (src), результат записується в розширення акумулятора та акумулятор. Якщо розширення акумулятора відрізняється від розширення знакового біта, тобто
(ext) ≠ 0...0h (ext) та (ext) ≠ 0 f ... fh , то of = 1 та cf = 1 (старша половина добутку відмінна від нуля), в противному випадку of = 0 та cf = 0 (старша половина добутку дорівнює нулеві і її можна не враховувати в подальших обчисленнях). Значення прапорців sf, zf, af, pf не визначені.
Алгоритм команди з двома операндами: вміст загального регістра (reg) множиться на вміст (src) і молодша половина добутку записується в загальний регістр reg. Значення прапорців of = 1 та cf = 1 означають, що втрачається значуща частина добутку. Значення прапорців sf, zf, af, pf не визначені.
Алгоритм команди з трьома операндами: вміст джерела (src) множиться на безпосередній операнд – сталу imm і молодша половина добутку записується в приймач dst. Приймачем може бути тільки загальний регістр, довжина якого повинна збігатися з довжиною джерела src. Значення прапорців of = 1 та cf = 1 означають, що втрачається значуща частина добутку. Значення прапорців sf, zf, af, pf не визначені.
2. Ділення цілих чисел. Команди DIV, IDIV. Команди перетворення
Загальний вигляд команди
DIV src; acc=частка((ext:acc)/(src)) src=остача((ext:acc)/(src)).
Операндами команди ділення без знакових чисел DIV є: ділене – вміст двох регістрів – розширення акумулятора та акумулятора (ext:acc); дільник – вміст джерела (src).
Алгоритм роботи:
Ділене ділиться на дільник. Одержується частка і остача. Частка заокруглюється (дробова частина відкидається) і завантажується в акумулятор. Остача завантажується в розширення акумулятора. Перед виконанням команди ділення потрібно ділене розмістити в розширення акумулятора та акумулятор, які визначаються типом джерела (як описано вище в командах множення). Значення всіх
74
прапорців of, cf, sf, zf, af, pf не визначені. Якщо частка за довжиною перевищує довжину акумулятора або дільник дорівнює нулеві, то виникає особлива ситуація – ділення на нуль, частка і остача при цьому не визначені. Як правило, ця ситуація призводить до аварійного призупинення виконання даної програми.
Загальний вигляд команди
IDIV src; acc=частка((ext:acc)/(src)) src=остача((ext:acc)/(src)).
Операндами команди ділення IDIV є: ділене – вміст двох регістрів
– розширення акумулятора та акумулятора (ext:acc); дільник – вміст джерела (src), які вважаються цілими числами зі знаком, зображеними в доповняльному коді. Далі команда IDIV виконує такі ж дії як і команда DIV, одержуються результати в доповняльному коді, причому остача формується так, щоб її знак збігався зі знаком діленого. Особлива ситуація ділення на нуль може виникнути аналогічно, як і в команді DIV.
До підгрупи команд ділення належать команди перетворення, які можна використати для підготовки діленого. Всі команди без операндів. Команда перетворення байта в слово CBW – розширює (копіює) знаковий біт регістра AL в усі біти регістра AH. Команда перетворення слова в подвійне слово CWD – копіює знаковий біт регістра AX в усі біти регістра DX. Команда перетворення слова в подвійне слово з розширенням CWDE – передає вміст знакового біта регістра AX в усі біти старшої половини регістра EAX. Команда перетворення подвійного слова в почетверенне слово CDQ копіює знаковий біт регістра EAX в усі біти регістра EDX. Усі команди перетворення не впливають на стан прапорців of, cf, sf, zf, af, pf.
3. Алгоритми введення-виведення цілих чисел
Для введення-виведення цілих чисел при програмуванні мовою асемблера користувачеві необхідно створити власні процедури. Деякі алгоритми та їх реалізації запропонуємо в цьому параграфі.
Спочатку наведемо один із способів введення цілих чисел, який можна описати так:
•введення набору десяткових цифр як символьного рядка;
•перетворення символів цього рядка в зображення заданого числа в двійковій системі, тобто зображення числа в машинній формі.
Для реалізації першого етапу використаємо функції переривань
DOS 21h або BIOS 10h для введення символьного рядка в буфер, який завчасно опишемо.
75
На другому етапі перетворимо кожний ASCII-символ цифри в деякі значення, які за рекурентним алгоритмом перетворимо у внутрішнє зображення.
Для прикладу розглянемо алгоритм перетворення символьного зображення десяткового цілого знакового числа з діапазону –32767 до 32767 у внутрішнє зображення розміром у слово. Результат перетворення запишемо в регістр АХ.
Алгоритм можна описати так:
1.Очищуємо регістр АХ.
2.Робимо активною першу позицію буфера введення.
3.В регістр СХ записуємо кількість введених символів у буфер.
4.Пропускаємо всі символи пробіли. Для цього переміщуємо активну позицію буфера та зменшуємо значення регістра СХ на одиницю. Перевіряємо чи буфер не порожній. Якщо буфер порожній, то перехід до пункту 12.
5.Першим символом, який ми ввели в буфер, може бути знак числа плюс ‘+’ або мінус ‘–‘. Здійснюємо перевірку наявності знака перед числом. Якщо знак є, то фіксуємо цю інформацію у комірці знака та переходимо до наступної позиції буфера.
6.Перевіряємо, чи символ активної позиції буфера визначає символьне значення цифри (від ‘0’ до ‘9’). Якщо ні, друкуємо повідомлення ‘Не цифра.’ та здійснюємо перехід до пункту
12.
7.Перетворюємо символьне значення цифри у відповідне значення, тобто віднімаємо від значення символу цифри значення символу нуль (‘0’).
8.Множимо вміст регістра АХ на 10.
9.Додаємо одержане значення до вмісту регістра АХ.
10.Перевіряємо наявність наступного символу в буфері (перевірку здійснюємо за вмістом регістра СХ). Якщо символ є, переходимо до пункту 6. У противному разі переходимо до пункту 11.
11.Перевіряємо, чи одержане значення в регістрі АХ із допустимого діапазону. Якщо ні, то друкуємо інформацію про переповнення, інакше перевіряємо знак числа. Якщо число від’ємне, то вміст регістра АХ перетворюємо у доповняльний код.
12.Кінець алгоритму.
Реалізація алгоритму подана у фрагменті програми як процедура ininteger.
76
Data |
segment |
|
|
MAXELEM = 6 |
|
||
inputs |
label byte |
|
|
maxs |
db MAXELEM |
|
|
nums |
db ? |
|
|
strs |
db MAXELEM dup(' ') |
||
sign |
db 0 ; 0– додатне, 1–від’ємне |
||
decimal |
dw ? |
|
|
msgprpov |
db 0ah,0dh,’пеpeповнення’,0ah,0dh,'$' |
||
msgnonn |
db 0dh,0ah,'немає цифpи',0ah,0dh,'$' |
||
msgnotd |
db 0ah,0dh,’Не цифра’,0ah,0dh,’$’ |
||
msgvv |
db 0ah,0dh,’Введіть десяткове число’,0ah,0dh,’$’ |
||
Data |
ends |
|
|
Stk segment stack |
|
||
dw 1024 dup (‘ ‘) |
|
||
Stk ends |
|
|
|
Code segment |
|
|
|
|
ASSUME CS:Code,DS:Data,SS:Stk |
||
; ******************* |
|
||
ininteger proc |
|
||
|
push BX |
|
|
|
push CX |
|
|
|
push DX |
|
|
|
push SI |
|
|
|
push BP |
|
|
|
lea |
DX,msgvv |
|
|
mov |
AH,9 |
|
|
int |
21h |
|
|
mov |
BP,0ah |
|
|
xor |
BX,BX |
|
|
lea |
DX,inputs |
|
|
mov |
AH,0ah |
|
|
int |
21h |
|
|
xor |
DX,DX |
;1) Очистити pегiстp DX. |
|
xor |
AX,AX |
;1) Очистити pегiстp AX. |
|
lea |
SI,strs |
;2) |
|
dec SI |
|
|
|
xor |
CX,CX |
|
|
mov |
CL,nums |
;3) Завантажити лічильник. |
m1: |
inc |
SI |
|
77
|
mov |
BL,[SI] |
; 4) Перший символ в BL |
|
cmp |
BL,' ' |
; 5) Це пробiл? |
|
jne |
m2 |
; 4) преехiд, якщо не пpобiл |
|
loop |
m1 |
|
|
jmp |
nonnum |
; 4)-12) не було числа |
m2: |
mov |
sign,0 |
; sign=0 – плюс |
|
cmp |
BL,'+' |
;5) |
|
je |
plus |
|
|
cmp |
BL,'–' |
;5) |
|
jne |
nminus |
|
|
mov |
sign,1 |
;5) sign=1 - минус |
plus: |
dec |
CX |
|
peretv: |
inc |
SI |
|
|
mov |
BL,[SI] |
; 5) |
nminus: |
|
|
; 6) перевiрка на цифру |
|
cmp |
BL,’9’ |
|
|
ja |
notdig |
; перехід, якщо вміст BL >'9' |
|
cmp |
BL,’0’ |
|
|
jb |
notdig |
; перехід, якщо вміст BL <’0’ |
|
sub |
BL,'0' |
; 7) |
|
mov |
AX,DX |
; AX - попереднє число |
|
mul |
BP |
; 8) ( AX)*10 |
|
jc |
perepov |
;якщо DX<>0 тоді – переповнення |
|
add |
AX,BX |
;9) |
|
jc |
perepov |
|
|
mov |
DX,AX |
; число записуємо в DX |
|
loop |
peretv |
; 10) пеpехiд до наступного символу |
|
mov |
AX,DX |
|
|
cmp |
sign,0 |
; 11) число додатнe |
|
je |
exit |
|
|
cmp |
AX,7fffh |
; 11) модуль вiд'ємного числа > 7fffh ? |
|
ja |
perepov |
|
|
neg |
AX |
; 11)перетворити в доповняльний код |
exit: |
mov decimal,AX |
|
|
|
pop BP |
; вихiд |
|
|
pop SI |
|
|
|
pop DX |
|
|
|
pop CX |
|
|
|
pop BX |
|
|
|
ret |
; вихiд |
|
78 |
|
|
|
perepov: lea |
DX,msgprpov |
; переповнення |
|
|
jmp error |
|
|
nonnum: lea |
DX,msgnonn |
; не було цифpи |
|
error: |
mov AH,9 |
|
|
|
int |
21h |
|
|
mov AX,0ffffh |
|
|
|
jmp exit |
|
|
notdig: |
lea |
DX,msgnotd |
|
|
jmp error |
|
|
ininteger |
endp |
|
|
Code |
Ends |
|
|
End Main
Тепер наведемо один з алгоритмів виведення цілих чисел зі знаком.
Виведення цілих чисел складається з двох етапів:
•перетворення внутрішнього зображення числа в рядок символів, тобто кожну цифру числа подамо як ASCII символ;
•виведення одержаного рядка на дисплей або стандартний пристрій виведення.
Для прикладу розглянемо алгоритм перетворення цілого числа зі
знаком із діапазону –32767 до 32767. Зрозуміло, що це 16-бітове число. Для перетворення в сегменті даних потрібно зарезервувати буфер із семи байтів.
Алгоритм можна описати так:
1.Початок.
2.Розмістити початкове ціле число зі знаком у регістр АХ.
3.Заповнити буфер символами ‘ ’ – пробіл. У сьому позицію буфера записати символ долара (‘$’). Установити активною шосту позицію буфера.
4.Перевірити знак числа. Якщо число від’ємне, то відзначити цей факт і перетворити число в додатне.
5.Виконати команду без знакового цілочислового ділення на 10.
6.Остачу від ділення перетворити в ASCII символ (до остачі додаємо код символу ‘0’).
7.Записати символьне зображення остачі в активну позицію буфера. Індекс активної позиції зменшити на одиницю.
8.Якщо частка не дорівнює нулеві, повернутися до пункту 4.
9.Якщо число від’ємне, то в буфер записати символ мінус ('–').
10.Кінець.
79
На другому етапі можна вивести вміст буфера за допомогою функцій виведення переривання DOS 21h або BIOS 10h.
Реалізація алгоритму подана у фрагменті програми як процедура
outinteger. |
|
|
|
|
|
Data |
segment |
|
|
|
|
; |
|
|
|
|
|
sign |
|
db |
0 ; 0 – число від’ємне, 1 – додатне |
||
decimal |
dw |
–4567 |
|
|
|
outdecimal |
db |
6 dup(' '),'$' |
|
||
; |
|
|
|
|
|
Data |
ends |
|
|
|
|
Stk segment stack |
|
|
|
||
dw 1024 dup (‘ ‘) |
|
|
|
||
Stk ends |
|
|
|
|
|
Code segment |
|
|
|
|
|
|
ASSUME CS : Code, DS : Data,SS:Stk |
|
|||
; |
|
|
|
|
|
outinteger proc |
|
|
|
|
|
|
push |
SI |
|
|
|
|
push |
DX |
|
|
|
|
push |
CX |
|
|
|
|
push |
BX |
|
|
|
|
mov |
AX,decimal |
; 1) Початкове число в регістрі AX |
||
|
mov |
DL,’ ‘ |
|
; 2) |
|
|
mov |
SI,0 |
|
; SI=0 |
|
|
mov |
CX,6 |
|
;CX=0 |
|
cls_buf: |
mov |
outdecimal[SI],DL |
;Запис пробілів у буфер |
||
|
inc |
SI |
|
|
|
|
loop |
cls_buf |
|
; 2) |
|
|
mov |
SI,5 |
|
; 2) |
|
|
mov |
BX,0ah |
|
; |
|
|
mov |
DL,0 |
|
; |
|
|
test |
AX,8000h |
;(AX) – від’ємне число? |
||
|
jne |
ns |
|
; |
|
|
mov |
DL,1 |
|
; |
|
|
neg |
AX |
|
; |
|
ns: |
mov |
sign, DL |
;В sign запам’ятати інформацію |
||
|
|
|
|
;про знак |
|
n1: |
cwd |
|
;Розширення (AX) у два регістри (DX,AX) |
||
|
div |
BX |
|
;4) AX=частка, DX=остача |
|
80 |
|
|
|
|
|