Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

M_Asm2009LS

.pdf
Скачиваний:
17
Добавлен:
23.02.2016
Размер:
2.34 Mб
Скачать

2. Розробити процедуру для обчислення виразу без використання операцій множення та ділення :

 

Вираз

Змінні

 

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

 

 

 

 

 

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]