Да се тества и трасира изпълнима програма за процесор...

59
59 Организация на компютрите 1

Transcript of Да се тества и трасира изпълнима програма за процесор...

Page 1: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

42

Организация на компютрите

1

Page 2: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Задание:МА SM 04

Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма - с използване на CV/MASM 6.1 (или съвместим) .

Съдържание на обяснителната записка (+дискета 3,5” с комплект файлове):

1.Титулен лист , задание2.Изчислителен метод, организация на данните и сорс на програмата3.Избор на тестови стойности за данните4.План за трасиране на програмата и наблюдавани регистри5.Резултати от изпълнението и коментар за трасираните стойности

Съдържание:

Фамилия микропроцесори ix86..........................................................................3

Формат на данните в Assembler..................................................................3

Регистров модел................................................................................................4

Организация на програмата на асемблерен език..............................................9

Дефиниране на данни......................................................................................17

Достъп до данните с адреси и указатели......................................................23

Десетична аритметика..................................................................................24

Директиви за разклонения и цикли................................................................26

Макроси............................................................................................................27

Управление на клавиатурата и дисплея.........................................................29

Изчислителен метод....................................................................................35

Сорс на програмата.......................................................................................36

Избор на тестови стойности....................................................................38

План за трасиране........................................................................................39

Литература...................................................................................................422

2

Page 3: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Фамилия микропроцесори ix 86

През 1971 г. Фирмата Intel (Integrated Electronics) представи първия в света 4 битов микропроцесор 4004, 2 години по-късно – станалия в последствие извънредно популярен 8 битов 8080, а през 1978 – 16 битовия 8086. Изискването да се разшири адресното пространство на паметта до 1MB при микропроцесор с 16 битови регистри наложи използването на сегментна организация на паметта, която е запазена понастоящем за съвместимост. Малко по-късно се появи микропроцесорът 8088, архитектурно напълно съвместим с 8086, но с 8 битова шина за данни. За широката му популярност допринесе използването му в масовите персонални компютри на IBM – PC и PC/XT. Поради извънредно бързо натрупване на програмно осигуряване за 8086 фирмата въведе специален режим на емулиране на 8086 при следващите микропроцесори – т.нар. режим на реален адрес (Real Address Mode – реален режим, R режим).Следващата значима стъпка в областта на 16 битовите микропроцесори е 80286 (1982), който може да се използва в два основни режима – реален (емулиращ 8086) и защитен (Protected Address Mode – защитен адрес на виртуален адрес, P режим). Защитеният режим на 80286 предлага нови възможности и средства на програмиста – разширено адресно пространство на паметта – до 16 MB, въвеждане на дескриптори на сегменти и таблици за обръщане към паметта, защита на паметта на 4 нива, организация на виртуална памет и мултипрограмиране. Масовото приложение на 80286 е за PC/AT и първите модели на PS/2.В областта на 32 битовите микропроцесори, фирмата стартира с 80386 (1985г.), работещ в два основни режима – R (реален, емулиращ 8086), P (защитен, емулиращ 80286 – основен и най-сложен режим) и един допълнителен – V ( Virtual 8086 mode – виртуален режим а 8086 – подрежим на P).Основната разлика на 80486 с предшественика 80386 е значителното ускоряване на повечето инструкции, изпълними на 1 цикъл, което при 50 MHz версия води до достигане на производителност 50 MIPS. През 1993 е представен последният модел на фамилията 80586 (или още P5), в последствие получил наименованието Pentium. Двете първоначални версии на Pentium са с честота 60-66 MHz, което осигурява производителност около 110 MIPS.Понататъшното увеличение на производителността при Pentium (150 MIPS при 150 MHz) е постигнато чрез така нареченото супер скаларна архитектура, при която за 1 цикъл с изпълняват 2 инструкции върху двата вътрешни конвейерни целочислени процесора. Pentium се характеризира и като един RISK процесор с еднократно време за изпълнение на повечето инструкции.

Формат на данните в Assembler

При обръщане към данните в паметта процесорът ги интерпретира по няколко начина в зависимост от инструкцията, логиката на програмата и др., като някои от форматите се поддържат от CPU, а други - от FPU.

Цели без знакИзползват се три формата на цели без знак (Unsigned Integer) - с дължина 1 байт (Byte), 2 байта (Word) и 4 байта (Double Word), при което най-младшият значещ бит (least significant bit - Isb) е с индекс 0, а най-старшият значещ бит (most significant bit - msb) -

3

Page 4: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

съответно 7,15 или 31. Обхватът на представимите стойности очевидно е 0-255 (Byte), 0-65535 (Word) и 0-4294967295 (Double Word).

Цели със знакЗа представяне на цели със знак (Signed Integer) се използва обикновен допълнителен машинен код (msb е знаков разред: 0 - положителен, а 1 -отрицателен). Обхватът на представимите стойности съответно е от -128 до +127 (Byte), -32678 до +32767 (Word) и -2147483648 до +2147483647 (Double Word). FPU поддържа и 64-битов формат на цяло със знак, получаван като резултат от умножение или деление на 32-битови операнди.

Десетични (BCD) данниДесетичните данни BCD (Binary Coded Decimal1) се представят без знак в непакетиран вид (една BCD цифра в 1 байт) и пакетиран вид (две BCD цифри в 1 байт), като кодирането на цифрите е в ASCII2 (0 - 30h, a 9 - 39h). FPU поддържа 80-битово (10 байтово) цяло десетично със знак (в пряк машинен код) с общо 18 BCD цифри (старшият байт се използва като знаков).

Данни с плаваща запетаяДанните с плаваща запетая (Floating Point) са с пряк машинен код по стандарта IEEE-754 с три дължини - единична (4 байта), двойна (8 байта) и разширена (10 байта), като знакът на мантисата е msb (поддържани само от FPU). Разрядността на мантисата и характеристиката съответно са 24 и 8, 53 и 11, 64 и 15, като мантисата е двоично нормализирана. При форматите с единична и двойна дължина msb на мантисата се обработва като т. нар. скрит бит, който не се обменя с паметта (понеже винаги е 1) - по такъв начин се постига фактическо удължаване на мантисата с 1 разряд. Характеристиката представлява порядъка на числото, увеличен съответно с 127, 1023 и 16383 за избягване на представянето и обработката на отрицателни стойности. При данните с плаваща запетая са дефинирани и специални стойности - денормализирани (още т.нар. антипрепълване), машинна нула (характеристиката съдържа само нули) и нечислови стойности NAN (още безкрайност, характеристиката съдържа само единици), които се обработват като изключения.

НизовеПоддържат се четири вида низове (Strings) с произволна обща дължина и формати Byte, Word, DWord и bit.

ASCII данниЗа кодиране на един символ се използва ASCII код - единственият вътрешен код на процесорите ix86.

УказателиПоддържат се указатели (Pointer, данни за адресна аритметика) с две дължини -NEAR (32-битов offset) и FAR (48 бита=32-битов offsets 16-битов selector).

Регистров моделРегистровият модел на i486 включва всички регистри на J386 и J387, които могат да се групират по следния начин:4 Основни регистри (регистри с общо предназначение, указател на инструкцията, регистър на флаговете и сегментни регистри);* Системни регистри (управляващи регистри, адресни регистри);* Регистри за плаваща запетая (регистри за данни, дума на признака - tag word, дума за състояние, указатели за инструкция и данни и регистър на управляваща дума);* Регистри за настройка и тестване.

4

Page 5: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Основните регистри и регистрите за плаваща запетая са достъпни за приложните програми. Системните регистри и регистрите за настройка и тестване са достъпни само на привилегировано ниво 0 и се използват от системните програми.

Основни регистриНа фиг.2-2 са показани основните регистри на i486. Съдържанието на тези регистри е специфично за всяка задача и автоматично се обновява при преход към следваща.

Регистри с общо предназначениеДеветте 32-битови регистри с общо предназначение ЕАХ, ЕВХ, ЕСХ, EDX, ESI, EDI, ЕВР и ESP се използват за съхранение на стойности за данни (операнди с дължина 1, 8, 16 или 32 бита, както и полета с дължина 1-32 бита) или адреси (атрибути с дължина 16 или 32 бита). Младшите 16-битови подрегистри (0-15 бита) могат да се специфицират самостоятелно чрез използване на 16-битовите им имена - АХ, ВХ, СХ, DX, SI, Dl, BP и SP, при което не се променя старшата им част (битове 16-31). Освен това при 8-битовите операции могат да се специфицират младшият (старшият) байт в регистрите АХ, ВХ, СХ и DX, съответно именовани с AL, BL, CL и DL (АН, ВН, СН и DH).[Е]АХ (Accumulator) - акумулатор, използван при умножение, деление, входно-изходни операции и операции на ускорената аритметика;[Е]ВХ (Base) - указател на базов адрес (база), използван при адресиране в сегмента на данните;[Е]СХ (Count) - брояч на цикли, повторения и измествания;[E]DX (Data) - данни при умножение, деление, входно-изходни операции;[E]SI (Source Index) - индекс-източник или източник при низови операции;[E]DI (Destination Index) - индекс-приемник или приемник при низови операции;[Е]ВР (Base Pointer) - указател на базата на стека;[E]SP (Stack Pointer) - указател на върха на стека.12

Регистър на флаговете

Регистърът на флаговете Е FLAGS (32-битов) съдържа постоянно дефинирани битове и битови полета за управление на операциите и представяне на текущото състояние на микропроцесора i486 (8 флага - за състояние и 6 - за управление). Младшите 16 разреда на регистъра представляват флаговият регистър FLAGS на J86-286. Разредите 1, 3, 5, 15 и 19-31 се игнорират, като при обработка на прекъсвания или при използване на инструкция PUSHF се препоръчва зареждането на 1 в първия и на нули - в останалите разреди.Регистър на флаговете EFLA GSbit# 186/88 i286 i386/

I486DI486SX Pentium Предназначение

0 С С С С С Carry (Пренос)2 Р Р Р Р Р Parity {Четност)4 A A A A A Auxiliary (Десетичен пренос)6 Z Z Z Z Z Zero (Нула)7 S S S S S Sign (Знак)8 Т Т Т Т Т Trap (Стъпков режим)9 I I I I I Interrupt (Прекъсване)10 D D D D D Direction (Посока)11 О О О О О Overflow (Препълваме)12 IOPO IOPO IOPO IOPO I/O Privilege Level 0 (В/И привилегии О)13 IOP1 IOP1 IOP1 IOP1 I/O Privilege Level 1 (В/И привилегии 1)14 NT NT NT NT Nested Task (Вложена задача)16 RF RF RF Resume (Възстановяване)17 VM VM VM Virtual Mode (Виртуален режим)18 AC AC Alignment Check (Адресно изравняване)19 VIF Virtual Interrupt (Копие на IF)20 VIP VI Pending (Допълнителен IF)21 ID Identification (Индикатор CPUID)

5

Page 6: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

а) флагове за състояние:CF (Carry Flag) - флаг за пренос (заем), установява се в стойност 1 при формиране на пренос от аритметична инструкция, както и при операции за изместване или ротация;PF (Parity Flag) - флаг за четност, установява се в стойност 1 при четен брой единици в младшия байт на резултата от операция;AF (Auxilary carry Flag) - флаг за десетичен пренос (заем), установява се в стойност 1 при формиране на десетичен пренос (пренос от младша към старша тетрада в байт);ZF (Zero Flag) - флаг за нулев резултат, установява се в стойност 1 при получаване на нулев резултат от операция;SF (Sign Flag) - флаг за отрицателен знак, установява се в стойност 1 при получаване на отрицателен резултат от операция;OF (Overflow Flag) - флаг за препълване, установява се в стойност 1 при аритметично препълване (резултат извън представимите стойности);IOPL (I/O Privilege Level) - флаг (2-битов) за привилегировано ниво на входа/изхода, използва се за защита на паметта в защитен режим;NF (Nested task Flag) - флаг за вложена задача, установява се в 1 при защитен режим и превключване към вложена задача чрез инструкция CALL (J286+). Състоянието на флага се обработва от инструкцията за връщане от прекъсване IRET, като при стойност 1 се извършва обратно превключване на задачата, а при стойност 0 - нормално връщане от прекъсване;б) Управляващи флагове:TF (Trap/Trace Flag) - флаг за стъпков режим, при стойност 1 процесорът изпълнява трасиране, като след всяка поредна инструкция се генерира прекъсване с преход към настройващата програма;IF (Interrupt Flag) - флаг за маскиране на прекъсванията, при стойност 1 се приемат и обработват външни схемни прекъсвания към клема INTR на микропроцесора;VIF (Virtual Interrupt Flag) - копирана стойност във виртуален режим VM на флаг IF при Pentium;VIP (Virtual Interrupt Pending flag) - допълнителен флаг IF при Pentium;DF (Direction Flag) - флаг за посока при обработка на низове, при стойност 0 се изпълнява автоинкрементиране, а при стойност 1 - автодекрементиране на индексните регистри ESI/SI и EDI/DI, използвани за адресиране на низовете, т.е. низовете се обработват в посока отляво-надясно (по нарастващи адреси) или в посока отдясно-наляво (по намаляващи адреси);RF (Resume Flag) - флаг за възстановяване, при стойност 1 блокира някои изключения при изпълнението на инструкции, което се използва от средствата за настройка;VM (Virtual Mode i86) - флаг за виртуален режим,' при стойност 1 процесор i486 работи като високопроизводителен i86, а при стойност 0 i486 работи в реален или защитен режим;AC (Alignment Check) - флаг за адресно изравняване, при стойност 1 разрешава обработката на грешка при адресно изравняване за отделните формати на записите в паметта, а при стойност 0 - забранява (нов признак в i486, липсващ при !386). Проверката за адресно изравняване (адресно изключение) се обработва на привилегировано ниво 3 (игнорира се на нива 0-2) чрез прекъсване #17. В таблицата са представени адресните граници за някои формати на записите в паметта.

6

Page 7: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

формат на записа Адресна граница (В)Дума 2Двойна дума 4Плаваща запетая, единична точност 4Плаваща запетая, двойна точност 8Селектор 2Сегментен указател (48 Ь) 4Псевдо-дескриптор (48 Ь) 4Битов низ 4

ID (Identification flag) - индикатор за поддържане на инструкция CPUID при Pentium (данни за версията и производителя на Pentium);

Сегментни регистриМикропроцесорът i486 използва 6 сегментни 16-битови регистъра CS, SS, DS, ES, FS и GS, съдържащи т.нар. селектори (начални адреси) за обръщане към паметта. В защитен режим размерът на сегмента е от 1 В до пълния капацитет на паметта -4GB (2 В). В реален режим размерът на сегмента е ограничен до 64 KB (2 В):* CS (Code Segment) - сегментен регистър на кода, дефинира текущия сегмент, съдържащ машинните инструкции (кода) на изпълняваната програма;* DS (Data Segment) - сегментен регистър на данните, дефинира сегмент в паметта за данни на изпълняваната програма;* SS (Stack Segment) - сегментен регистър на стека, дефинира сегмент в паметта, в който се изпълняват стековите операции;* ES (Extra Segment), FS (i386+), GS (I386+) - допълнителни сегментни регистри за дефиниране на още три сегмента с данни, достъпни за текущата програма.формирането на физически адрес в i486 се извършва на основата на полетата offset (отместване), селектор и дескриптор. С помощта на дескриптора за всеки сегмент се дефинират базов адрес (32 бита), размер (limit, 20 бита) и права за достъп (access rights, 8 бита) - признаци, определящи кои програми и при изпълнението на какви операции могат да получат достъп до конкретния сегмент.

Системни регистриСистемните регистри се състоят от четири (32-битови) регистъра за обслужване на страничното управление (пейджинга) CRO, CR1 (резервиран), CR2 и CR3 (достъпни на привилегировано ниво 0) и четири сегментни адресни регистъра GTDR, IDTR, LDTR, TR за обслужване на сегментацията на паметта.Регистърът CRO (достъпен за запис и четене) съдържа системни флагове за управление, чрез които се организират режимите на работа на i486 и се представя глобалното състояние на процесора. Младшите 16 бита образуват т.нар. дума за състояние на машината (MSW - Machine State Word - въведена първоначално при 1286), зареждана с инструкцията LMSW. Отделните битове на CRO имат следния смисъл:* PG (PaGing) - странично преобразуване, чрез който се разрешава (стойност 1) или забранява (стойност 0) страничното преобразуване на адреса;* CD (Cache Disable) - забрана на кеш-паметта, при стойност 1 се забранява записа във вътрешния кеш и не се предизвиква обработката на изключенията;* NW (Not Write-through) - забрана на проникващ запис в кеша, при стойност 1 забранява непрекъснатия запис в кеша и не се обработват съответните изключения;* AM (Alignment Mask) - маска за адресно изравняване, при стойност 1 се разрешава обработката на изключенията поради адресно изравняване;* WP (Write Protect) - защита при запис, при стойност 1 се блокира записът в страници на потребителско ниво от супервайзорни обръщания. При стойност 0 супервайзорният процес може да записва само в прочитаните страници на потребителско ниво;

7

Page 8: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

* NE (Numeric Error) - числова грешка, при стойност 1 се разрешава стандартният механизъм за съобщения при числови грешки. При стойност О и активен вход IGNNE# числовите грешки се пренебрегват, а при неактивен вход IGNNE# числовата грешка спира процесора и изчаква прекъсване;* ЕТ (Extention Type) - тип за разширение, използва се от инструкциите на копроцесора на I386* TS (Task Switched) - превключена задача, процесорът установява стойност 1 при всяко превключване на задачата и го проверява при изпълнението на инструкции с плаваща запетая;* EM (EMulation) - емулиране, при стойност 1 се обработва изключение 7 (недостъпен копроцесор) при инструкция WAIT или инструкция за числова обработка;* МР (Math Present) - наличен копроцесор, използва се при 1286 и 1386 за синхронизация на копроцесора;* РЕ (Protect Enable) - разрешение на защитата, при стойност 1 се разрешава защитата на паметта на ниво на сегменти.Регистърът CR2 е достъпен само За четене от програмата, като процесорът зарежда в него последния 32-битов линеен адрес, предизвикал странично изключение #14.Регистърът CR3 в старшата си част съдържа физическия адрес на каталога на страниците (таблици на страниците от първо ниво) и още се нарича базов регистър на каталога на страниците (PDBR). Младшите 12 бита от регистъра се пренебрегват при формирането на адреса (поради изискването за адресно изравняване към странична граница), но в това поле допълнително са дефинирани две флага:* PCD (Page Cache Disable) - забрана за кеширане на страници, показващ дали текущата страница е подходяща за кеширане;* PWT (Page Write-Through) - проникващ запис в страница, състоянието на този бит формира изхода PWT, който при активно ниво задава за текущата страница непрекъснато кеширане за външен кеш. (флагът се използва само за външен кеш, докато при вътрешния кеш той се пренебрегва, поради постоянната му работа в режим с непрекъснат запис.)Системните сегментни регистри служат за поддържане на сегментацията на паметта (т.нар. регистри за защитен режим):GDTR (Global Descriptor Table Register) и IDTR (Interrupt Descriptor Table Register) -регистър на глобалната дескрипторна таблица и регистър на дескрипторната таблица за прекъсвания (с еднакъв формат и дължина 48 бита), съдържат 32-битов базов адрес и 16-битов размер на сегмента. Първият регистър локализира в паметта глобалната дескрипторна таблица GDT (т.е. таблица с общосистемните сегменти), а вторият - дескрипторната таблица на прекъсванията IDT, чрез която се обработват прекъсванията и изключенията при изпълнението на програмите;LDTR (Local Descriptor Table Register) и TR (Task Register) - регистър на локалната дескрипторна таблица и регистър на задачата, с дължина по 16 бита. Регистърът LDTR, посочващ локалната дескрипторна таблица LDT, съдържа дескрипторите на собствените сегменти за текущата задача. Регистърът TR посочва текущия сегмент за състояние на задачата TSS (Task Status Segment), който се използва при превключване на задачите.Регистри за плаваща запетаяПрограмният модел на FPU съдържа осем 80-битови регистри за данни RO-R7 (образуващи стек), регистър на признаците TW (16 битов), управляващ регистър CW (16 битов), регистър на състоянието SW (16 битов), регистри-указатели за инструкции IP (48 битов) и данни DP (48 битов).

8

Page 9: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Всеки от наличните осем 80-битови регистри за данни RO-R7 съхранява данни в съответствие с използвания разширен формат за представяне с плаваща запетая.Три битовото поле ТОР (указател на стека) от регистъра SW служи за посочване на текущия връх на стека, като останалите записи в стека се подредени циклично. Операцията за запис на стека PUSH декрементира полето ТОР, след което зарежда стойността в новия връх на стека. Операцията за четене от стека POP прочита стойността от върха, след което инкрементира полето ТОР.Регистърът на признаците TW съхранява дву-битови признаци за данни за всеки от регистрите RO-R7 (00 - допустима ненулева стойност; 01 - нула; 10 - специална стойност; 11 - празен регистър), като признакът за регистър RO заема битове 0-1, а за R7 - битове 14-15 от TW.Регистърът на състоянието SW освен за съхранение на стековия указател ТОР за FPU се използва за запазване на кода на условието (4 бита) и на флагове (8 бита).Съдържанието на регистрите-указатели за инструкции и данни се обработва предимно при изключения. Обработката на изключение предполага запис в паметта на състоянието (частичното или пълното) на FPU и евентуалното му възстановяване след анализа. В зависимост от режима на процесора (реален или защитен режим) и формата на'операнда (32- или 16-бита) съхраненото състояние в паметта от инструкцията FSTENV (частично състояние) има различен вид. При запазване в паметта на пълното състояние (заедно със стека RO-R7) чрез инструкцията FSAVE записите ще бъдат удължени съответно до 108 байта (32-битов формат) или 94 байта (16-битов формат).

Организация на програмата на асемблерен езикСтруктура на програматаCopс - код и асемблиране

Copс-кодът на програма на асемблерен език се състои от инструкции и данни. В частта на инструкциите се записват една или повече процедури. Процедурата, от която започва изпълнението на програмата, се нарича главна програма (главна процедура). Процесът на транслиране на програмата на машинен език се нарича асемблиране, а транслаторът, който е прието да се нарича Асемблер1, чете файла със сорса на програмата и генерира файл с обектен модул. При срещане на инструкция Асемблерът генерира еквивалентна машинна инструкция и я записва в обектния модул, а при срещане на изискване за дефиниране на данни отделя памет за тях и я добавя към обектния модул.

Инструкции и директивиПрограмата на асемблерен език се състои от последователност от редове, наречени изречения. Съществуват два основни вида изречения: инструкции и директиви. Инструкциите са изречения, които се транслират до машинни инструкции (една инструкция поражда една машинна инструкция в обектния модул). Директивите са изречения, които дават команди на Асемблера за процеса на асемблиране на програмата. Наричат се още псевдо-инструкции и показват как един операнд или част от програмата трябва да бъдат обработени от Асемблера. Някои директиви генерират и записват информация в обектния файл, а други определят само действия на Асемблера при транслиране на програмата. С тях се дефинират данни, сегменти, процедури, макроси и т.н.

Формат на изречениятаВсяко изречение е с дължина до 512 символа, без да се прави разлика между главни и малки букви. Изреченията са съставени от едно до четири полета, които се разделят с

9

Page 10: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

поне един интервал или табулация (с изключение на коментара, който може да се запише веднага след операндите).

Синтаксис:[ Име ] [операция ] [ операнди ] [ ; Коментар ]Името на изречението дефинира етикет, към който може да се прави обръщане към изречението в произволно друго място на програмата. Ако изречението е директива, това поле определя име на променлива, тип, процедура, сегмент или макрос, а ако е инструкция, тогава е етикет на инструкцията.Полето за операцията определя действието и представлява мнемонично име на инструкция или име на директива.Операндите определят обектите на действие на изречението. Ако са повече от един, те се разделят със запетаи.Коментарът представлява пояснителен текст за програмиста и се пренебрегва от Асемблера при асемблиране на програмата.Всички полета са опционни, но някои инструкции и директиви изискват име или определен брой операнди. Ако изречението не може да се събере на един физически ред от програмата, то може да се пренесе на следващ ред със символа обратно наклонена черта (\) преди коментара или края на реда.Коментарът може да се запише като;mekcmсамостоятелно на един ред от програмата или след всички останали полета на изречението. В макроси може да се използва макрокоментар, който се записва като; ; mekcmи в този случай в листинга на програмата се появява само в макродефиницията, а не в макроразширението.Коментар може да се запише и като текст с произволен брой редове, ако се използва директивата COMMENT:COMMENT ограничител [ mekcm][ mekcm ][ mekcm] ограничител mekcm]Текстът между двата ограничителя и до края на реда с втория ограничител се пренебрегва при асемблиране. Като ограничител може да се използва произволен символ (първият непразен символ след директивата COMMENT).Елементите, от които се изграждат изреченията и които са разгледани подробно по-долу, са: имена, регистри, запазени думи и имена, константи и оператори над операндите за построяване на изрази.

ИменаИмената се използват като етикети на инструкции или символични имена на променливи, константи, процедури, сегменти и на дефинирани от потребителя типове - структури, обединения, записи и типове, определени с TYPEDEF.

СимволиВсяко име представлява комбинация от букви, цифри и символа за подчертаване (_). То трябва да започва с буква или с един от следните символи: за подчертаване (_), за долар ($), символа at (@) или точка (.). Асемблерът не прави разлика между малки и главни букви. Дължината на всяко име е произволна, но Асемблерът отчита само първите 247 символа. Като име не може да се използва някоя от запазените думи или символични имена.Използването на символа at (@) като първи символ на потребителско име трябва да се избягва, защото MASM 6.1 предефинира някои имена, започващи с него.

10

Page 11: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

РегистриОбръщането към регистрите на микропроцесора в изреченията става чрез предварително възприети имена, подробно описани в т.2.3.

Запазени думи и именаТе имат специално предназначение в Асемблера и могат да се използват само за него. Към запазените имена спадат мнемоничните имена на инструкциите, имената на регистрите, наименованията на директивите, имената на операторите и предварително дефинираните символични имена. Не се прави разлика при изписването им с малки или главни букви.Асемблерът поддържа множество от предварително дефинирани чрез директиви EQU символични имена, които могат да се използват на произволно място в програмата за представяне на стойността, към която са приравнени. Например, предварително дефинираното символично име @FileName представлява основното име на текущия cope-файл, така че ако текущият cope-файл е report.asm, стойността на @FileName е report. В Приложение 1.3 са включени предварително дефинираните имена.

КонстантиВ инструкциите и директивите като операнди могат да се посочват числа и низове, които се разглеждат от Асемблера като константи

Цели константиСинтаксис:

цифри [ суфикс ]Целите константи се записват като последователност от двоични, осмични, десетични или шестнадесетични цифри и опционен суфикс, определящ основата на бройната система. В таблицата са показани допустимите цифри и суфикси за различните бройни системи.

Бройни системи, използвани за представяне на числа в асемблерния език

Бройна система Основа Суфикс ЦифриДвоична 2 Y или В 01Осмична 8 Q или О 01234567Десетична 10 Т или D 0123456789Шестнадесетична 16 Н 0123456789ABCDEF

Шестнадесетичните цифри и суфиксите могат да бъдат записани с малки или главни букви. Шестнадесетичните числа трябва да започват винаги с десетична цифра (0 -9). Ако числото започва с една от цифрите А - F, пред нея трябва да се запише 0, за да може Асемблерът да направи разлика със символично име. Например 0FF00h се интерпретира като шестнадесетина константа, докато FF00h - като символично име.Ако не е използвана директивата .RADIX, основата на бройната система по подразбиране е 10. С директивата .RADIX може да се смени бройната система по подразбиране (например, с .radix 8 основата на бройната система по подразбиране става 8).От целите константи могат да се съставят константни изрази с помощта на аритметични оператори, логически оператори или оператори за отместване. Те се оценяват от Асемблера по време на асемблиране на програмата.Символични цели константиМогат да се дефинират и символични цели константи, с които се присвояват имена на цели константи. Това може да стане с една от двете директиви EQU (EQUate -приравнявам) или знака за равенство (=). С тях се присвояват стойности на имена по време на асемблиране на програмата, които ще бъдат използвани като непосредствени

11

Page 12: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

операнди. Асемблерът не отделя памет при срещане на директивите EQU или =, а само заменя името със стойността на константата или израза.Двете директиви EQU и = се различават по това, че имената, дефинирани с =, могат да се преопределят в програмата, докато тези, дефинирани с EQU, не могат. Името трябва да бъде уникално в програмата, а целият израз може да бъде цяла константа, израз от цял тип, константа-низ с един или два символа (четири за 1386+) или израз, даващ адрес. Използването на символични цели константи дава възможност да се смени стойността на цяла константа в цялата програма само чрез смяна на стойността на израза в директивата EQU или =.

Константи – низовеСинтаксис:

"mekcm"'mekcm'При записването им могат да се използват кавички или апострофи, но символите, ограждащи низа, трябва да са еднакви. Низът може да има дължина до 255 символа и може да се разположи на няколко физически реда от програмата, като редовете с продължение завършват със запетая. Кавичките или апострофът могат да се използват и вътре в низ, но се записват два пъти.

ОператориОператорите се прилагат в изрази, чиито стойности се изчисляват по време на асемблиране на програмата. Използват се за представяне на операнди в инструкциите и директивите. Операторите не трябва да се бъркат с процесорните инструкции. Например, запазената дума add е мнемонично име на инструкцията за събиране, докато символът + е оператор и използван в израз като Ammount+2 за изчисляване на операнд дава указание на Асемблера да добави 2 към константната стойност Ammount, която може да е стойност или адрес.Когато в израз се използват няколко оператора, стойността на израза се изчислява по следните правила:* действията в скобите се изпълняват преди съседните им действия;* двуаргументните операции с no-висок приоритет се изпълняват преди тези с по-нисък;* действията с еднакъв приоритет се изпълняват отляво надясно;* едноаргументните операции с еднакъв приоритет се изпълняват отдясно наляво.В табл.5.2 са подредени според приоритета им всички възможни оператори. Операторите на един и същ ред имат еднакъв приоритет.Действието и необходимостта от прилагане на операторите са пояснени в текста по-долу и в следващите глави.

Организиране на сегментифизически и логически сегменти

При фамилията процесори 1x86 терминът сегмент има две значения:* блок от паметта с фиксиран размер, наречен „физически сегмент. Броят на байтовете във физически сегмент е 64 KB за 16-разредните процесори и 4 GB за 32-разредните;* блок от паметта с променлива дължина, наречен „логически сегмент, отделен за инструкциите на програмата или за данните й.

12

Page 13: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

физическият сегмент може да започва само в област от паметта с начален адрес, делящ се на 16 (такива адреси се наричат „параграфи"). Максималната дължина на един физически сегмент за 8086/80286 е 64 KB, защото 65535 е максималното число, което може да се представи с 16 разряда. Този размер на физически сегмент се поддържа и от процесорите I386+ в реален режим, но в защитен режим те използват 32-разредни регистри за съхраняване на адреси до 4 GB.Когато програмите са малки, инструкциите и данните поотделно заемат по-малко от 64 KB и се разполагат в отделни сегменти, поради което всяка променлива или инструкция се адресира с 16-разредно отместване спрямо началото на сегмента. При по-големи програми, обаче, данните (или инструкциите) трябва да се поставят в два или повече сегмента и тогава програмата трябва да използва сегментен адрес и отместване за адресиране на данните. Нещата се усложняват още повече, когато данните представляват непрекъснат поток, преминаващ границата между два сегмента (например при текстовите редактори). Проблемът с границите на сегментите отпада при 32-разредния защитен режим, при който сегментите пак съществуват, но поради големите си размери един сегмент може лесно да побере всички данни и инструкции и на най-големите програми.Логическите сегменти съдържат трите компоненти на програмата: инструкции, данни и стек. Асемблерът организира тези три части така, че да заемат физически сегменти от паметта. Сегментните регистри CS, DS, ES и SS съдържат адресите на физическите сегменти, в които са разположени логическите сегменти.Логическите сегменти могат да се дефинират с опростени сегментни директиви, с пълни сегментни директиви или смесено в една и съща програма.Опростените сегментни директиви скриват повечето от подробностите при дефинирането на сегментите, прилагайки конвенциите на езиците от високо ниво на Microsoft. С тях се генерира необходимият код, задават се сегментните атрибути и подреждането на сегментите. Пълните сегментни директиви имат по-сложен синтаксис, но предлагат по-пълен контрол върху начина на генериране на сегментите. При тях трябва да се добави код за изпълнение на действията, които се изпълняват автоматично при опростените сегментни директиви.Опростени сегментни директиви за изграждане на програмиСкелетна структура на програма с опростени сегментни директивиСтруктурирането на програма с опростените сегментни директиви изисква използването на няколко директиви за присвояване на стандартни имена, изравняване и атрибути на сегментите. Опростените сегментни директиви ca:.MODEL, .CODE, .CONST, .DATA, .DATA?, .FARDATA, .FARDATA?, .STACK, .STARTUP и .EXIT. Този начин на изграждане на сегменти осигурява лесна връзка на програмата с езици от високо ниво.Програмите на асемблерен език се състоят от модули, които са съставени от сегменти. Всяка програма, написана само на MASM, има един главен модул, от който започва изпълнението на програмата. Този главен модул може да съдържа сегменти за инструкциите, за данните и за стека, дефинирани с опростените сегментни директиви. Всички допълнителни модули могат да имат само сегменти за инструкциите и за данните. Всеки модул, изграден с опростените сегментни директиви, трябва задължително да използва директивата .MODEL.Дефиниране на основните атрибути на модула с .MODELДирективата .MODEL дефинира атрибутите, които характеризират целия модул: модел на паметта, конвенциите по подразбиране за повикване и именуване, операционна система, тип на стека, името на сегмента с инструкциите и отдалечеността по

13

Page 14: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

подразбиране на процедурите. Тя трябва да се постави в сорс-файла преди всички останали опростени сегментни директиви.Синтаксис:.MODEL модел_на_паметта [ eзukoвa конвенция ] отдалеченост на cmeka.Първият параметър - модел на паметта - се посочва задължително, докато опциите на модела за избор на езиковите конвенции и на отдалечеността на стека могат и да се пропуснат. За всяко поле може да се посочи само една стойност.В таблицата са показани възможните стойности за модела на паметта, които са и стандартни модели на паметта за езиците от високо ниво на Microsoft. Те задават операционната система и стойностите по подразбиране за сегментите.Традиционните модели, използвани в много езици, са SMALL, MEDIUM, COMPACT, LARGE HUGEМоделът SMALL (малък) поддържа един сегмент за данните и един сегмент за инструкциите, които имат атрибут за отдалеченост по подразбиране NEAR (адресирането в паметта става само с 16-разредно отместване, без сегментен адрес). Атрибути на моделите на паметта| Модел на паметта Сегмент за

инструкциитеСегмент за данните

I Операционна система Инструкции и данни в един сегмент

TINY NEAR NEAR \ MS-DOS ДаSMALL NEAR NEAR 1 MS-DOS, Windows НеMEDIUM FAR NEAR ! MS-DOS, Windows НеCOMPACT NEAR FAR ! MS-DOS, Windows НеLARGE FAR FAR ! MS-DOS, Windows НеHUGE FAR FAR ! MS-DOS, Windows НеFLAT NEAR NEAR ! Windows NT Да

Моделът LARGE (голям) поддържа няколко сегмента за данни и няколко за инструкции, всички с атрибут за отдалеченост FAR (адресират се със сегментен адрес и отместване). Моделът MEDIUM (среден) поддържа един сегмент за данните и няколко за инструкциите, a COMPACT (компактен) - един сегмент за инструкциите и няколко за данните. Моделът HUGE (огромен) предполага наличие на логически сегмент за данните, разположен в няколко физически сегмента, но Асемблерът не поддържа средства за такива приложения, поради което реализацията им трябва да се осъществи от програмиста и на практика този модел е еквивалентен на модела LARGE.

Моделът TINY (мъничък) работи само под MS-DOS и разполага всички данни и инструкции само в един сегмент, следователно цялата програма може да заема до 64 KB памет. С този модел се създават MS-DOS .COM файлове.

Моделът FLAT (плосък) предоставя несегментирана конфигурация за 32-разредните операционни системи. Подобно на модела TINY всички данни и инструкции се намират в един сегмент, но 32-разреден. За да се използва този модел, пред директивата .model flat трябва да се запише директивата .386, .486 или .586. Операционната система автоматично инициализира сегментните регистри при зареждане на програмата и те трябва да се променят само при смесване на 16-разредни и 32-разредни сегменти в една програма. Всички адреси и указатели са 32-разредни с атрибут за отдалеченост NEAR.

Вторият параметър (опционен) в директивата .MODEL - избор на езиковата конвенция - опростява съвместимостта с езиците от високо ниво при смесено програмиране. Възможните стойности са: С, BASIC, FORTRAN, PASCAL, SYSCALL и STDCALL (конвенциите BASIC, FORTRAN и PASCAL са идентични) При дефиниране на процедури с директивата PROC и обръщане към тях с INVOKE автоматично се генерира код, съвместим с начина на обръщане към подпрограми в посочения език.

14

Page 15: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Третият параметър (опционен) в директивата .MODEL - задаване на отдалечеността на стека - може да приема стойност NEARSTACK или FARSTACK. NEARSTACK води до обединяване на сегмента за стека със сегмента за данните в един физически сегмент (наречен DGROUP) и кара директивата .STARTUP да генерира инструкции, които присвояват на регистър SS стойността на DS. Така регистър DS може да се използва за достъп до елементите на стека (включително параметрите и локалните променливи), a SS - за достъп до данните в същия сегмент. FARSTACK задава отделен сегмент за стека. Стойността по подразбиране NEARSTACK е удобна за повечето програми; FARSTACK трябва да се използва за специални приложения като например резидентни програми.

Определяне на процесор и копроцесорИзползваният процесор може да се зададе в програмата с една от директивите .186, .286, .386, .486 или .586. След посочване на процесора могат да се използват само инструкциите от набора за избрания процесор. Процесорът по подразбиране е 8086.За избор на копроцесор се използват директивите .8087 (по подразбиране), .287, .387 и .NO87 (последната изключва употребата на всички копроцесорни инструкции). Директивата .486 разрешава използването на всички копроцесорни инструкции, които са вградени заедно с останалите в чипа на i486.Задаването на процесор предполага и използването на инструкциите на съответния копроцесор, така че директивите за определяне на копроцесор служат само при смяна на стойността по подразбиране.

Създаване на стекСтекът е област от паметта за запис и четене на стойности на регистри и съхраняване на адреса на връщане при обръщане към подпрограма, а също така за съхраняване на временни или локални променливи. Ако главният модул на програмата е написан на език от високо ниво, тогава подробностите по създаване на стека се поемат от съответния транслатор, но в противен случай в главния (или единствения) модул на асемблерен език трябва да се използва директивата .STACK за дефиниране на стек.Синтаксис:.STACK [[размер!Използвана съвместно с директивата .MODEL, създава сегмент за стека с име STACK и с посочения размер в брой байтове (стойността по подразбиране е 1024) и едновременно затваря сегмента за стека (не се използва специална директива за завършване на сегмента за стека).

Създаване на сегменти за даннитеПрограмите могат да съдържат както близки, така и отдалечени данни (адресирани само с отместване или със сегментен адрес и отместване). Често използваните и по-важни данни е добре да бъдат разположени в близък сегмент за данни, до който достъпът е много по-бърз. При 16-разредна операционна система, обаче, обемът на всички близки данни от всички модули не може да превишава 64 KB и ако тази памет не е достатъчна за данните, трябва да се създаде отдалечен сегмент за данни.Опростените сегментни директиви за създаване на сегменти за данни са .DATA, .DATA?, .CONST, .FARDATA Създава близък сегмент за данни с име _DATA и с обем до 64 KB за MS-DOS или 512 MB за модел на паметта FLAT.Kaктo .DATA (с име на сегмента _BBS), но за съхраняване само на променливи без начални стойности.Kakmo .DATA (с име на сегмента CONST), но за съхраняване само на Константи.

15

Page 16: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Създава отдалечен сегмент за данни с посоченото име или с име FAR_DATA при липса на име..FARDATA? [[име]]Kakmo .FARDATA (с посоченото име или с име FAR_BBS при липса на име), но за съхраняване на променливи без начални стойности.При посочване на директива .MODEL Асемблерът автоматично създава специална група от сегменти с име DGROUP с обем до 64К, в която разполага близките сегменти за данни, дефинирани с .DATA, .DATA? и .CONST независимо от модела на паметта. Директивите .DATA? и .CONST подобряват съвместимостта с езици за програмиране от високо ниво на Microsoft.Предварително дефинираното символично име @DATA може да се използва за получаване на името на групата на сегмента за данни, @DATASize - за модела на паметта, зададен с .MODEL, a @WordSize и @CurSeg - за размера на думата и името на текущия сегмент.Моделите COMPACT, LARGE и HUGE използват отдалечени адреси на данните по подразбиране. Въпреки това при тях също могат да се създават сегменти с директивите .DATA, .DATA? и .CONST. При използване на .FARDATA и .FARDATA? за малки и средни модели на паметта се създават отдалечени сегменти за данни с имена FAR_DATA и FAR_BBS.Всеки сегмент за данни завършва при срещане на нова директива за дефиниране на друг сегмент.

Създаване на сегменти за инструкциитеМалкият модел на паметта (SMALL) често е най-добрият избор за програми, които не са свързани с модули, написани на други езици, особено когато не са необходими повече от 64 KB за инструкциите. В този случай директивата .CODE дава указание за начало на близък сегмент за инструкциите, който завършва при срещане на следваща директива за дефиниране на сегмент.Синтаксис:.CODE [ име ] Когато програмата има нужда от сегмент за инструкциите с обем над 64К, трябва да се използва някой от моделите на паметта MEDIUM, LARGE или HUGE, при които директивата .CODE създава отделен отдалечен сегмент за инструкциите за всеки модул.При отдалечени сегменти за инструкциите Асемблерът именува всеки сегмент с MODNAME_TEXT, където MODNAME е името на модула. Допуска се в един модул да има няколко сегмента за инструкции, които получават имена CODENAME_TEXT, където CODENAME е името на сегмента.Създаване на начален и краен код с директивите .STARTUP и .EXITНай-лесният начин за започване и завършване на програма за MS-DOS е с използване на директивите .STARTUP и .EXIT в главния модул, който съдържа началото и обикновено и края на програмата. Тези две директиви не се поставят в модул, повикван от друг модул..STARTUP .EXIT [израз ]Първата директива автоматично генерира инструкциите за зареждане на сегментните регистри с н!необходимите стойности в зависимост от атрибута за отдалеченост на стека, зададен с .MODEL. Тези директиви, обаче, не се отнасят за модел на паметта FLAT и затова не трябва да се използват в програми под управлението на Windows NT.Директивата .STARTUP се поставя там, където започват изпълнимите инструкции от програмата, обикновено след директивата .CODE, a .EXIT - преди END (.EXIT води до

16

Page 17: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

генериране на изпълними инструкции, докато END - не). Директивата END информира Асемблера за края на модула и всички модули трябва да завършват с нея.Ако не се използва .STARTUP, трябва да се посочи начален адрес като аргумент на директивата END - етикет на инструкция, от която да започне изпълнението на програмата.При посочен атрибут NEARSTACK при задаване на модела на паметта директивата .STARTUP генерира следните инструкции:При MS-DOS и атрибут FARSTACK не е необходимо да се инициализира SS:SP, така че .STARTUP зарежда само регистър DS:При завършване на програмата тя може да върне така наречения „код при излизане" (exit code) към операционната система, обикновено 0 при нормално завършване и 1 при възникване на грешка. Тази стойност се поставя като аргумент на директивата .EXIT. Директивата .EXIT генерира следните инструкции, които връщат управлението към операционната система:В следващия пример е показана програма, използваща опростените сегментни директиви за дефиниране на сегменти. Програмата дефинира две области от паметта с дължина по 100 байта и копира стойностите от първата област (с име lista) във втората (с име listb). Предварително дефинираното символично име @DATA се замества с името на групата от сегменти за данни по подразбиране, в случая DGROUP. Ако в програмата се включи директивата .STARTUP, двете инструкции mov ax, ©data и mov ds, ах стават излишни и могат да се премахнат. Директивата .STARTUP, обаче, не генерира инструкции за зареждане на регистър ES. Понеже програмата използва регистър ES, тя трябва да го зареди със сегментния адрес на сегмента за данните и затова използва двете инструкции mov ax, @datanmov es, ax.

Дефиниране на данниДефиниране на прости типове данни

Асемблерът транслира програмата чрез генериране на обектен модул. Обектният модул съдържа не само инструкции, но и пространство в паметта за съхраняване на използваните от програмата данни. Затова в cope-кода трябва да се отдели памет за всички данни с помощта на директиви за дефиниране на данни. При срещането на такава директива в процеса на асемблиране Асемблерът отделя заявената от директивата памет и евентуално записва в нея някаква начална стойност (инициализиране на данните). Така данните се подреждат по реда на дефинирането им в програмата, обикновено само в сегментите за данни и в допълнителни сегменти.

Константи и променливиС директивите за дефиниране на данни могат да бъдат определени както константи, така и променливи. При дефинирането на константа в директивата се посочва стойността, с която тя се инициализира от Асемблера, докато при дефинирането на променлива отделената за нея памет не получава начална стойност, а се очаква програмата да запише в нея някаква стойност или да я промени.При асемблирането на тези директиви Асемблерът ще отдели един байт за cost и ще запише в него начална стойност 100 и един байт за total, който няма да се инициализира - в него ще се намира някаква случайна стойност.Всъщност константите и променливите в асемблерния език са логически понятия, защото паметта, отделена за константите, може да променя съдържанието си от действието на програмата, а стойността на променлива (каквато и да е тя) може да бъде използвана преди инициализацията й. Затова се препоръчва програмистът сам да следи

17

Page 18: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

използването на дефинираните от него данни като константи или като променливи, например ако дефинира константа, да не променя с инструкции стойността й, а ако дефинира променлива, да не й задава начална стойност по време на асемблиране, а с инструкции в програмата.Понеже всяка дефинирана с начална стойност константа може да промени стойността си от действие на инструкция при изпълнение на програмата, всички елементи на данните, зададени с директиви за дефиниране на данни, се наричат по-нататък в книгата променливи.

Прости типове данниВсеки тип на данните определя множество от стойности, които променлива от този тип може да приема. Променлива от прост тип на данните може да съдържа само една стойност в даден момент от изпълнение на програмата. В асемблерния език няма вградени типове за масиви, низове и структури, но те могат да се дефинират като последователност от елементи от прост тип.Имената на стандартните прости типове данни са BYTE, SBYTE, WORD, SWORD, DWORD, SDWORD, FWORD, QWORD, TBYTE, REAL4, REALS, REAL10. Версиите на MASM преди 6.1 имат отделни запазени думи за типове и директиви за дефиниране на данни, например BYTE е име на тип, a DB - запазена дума за директива за дефиниране на данни от тип BYTE. За версия 6.1 това ограничение не важи - всеки от стандартните типове или дефинираните от програмиста имена на типове може да се използва като директива за дефиниране на данни.

Директиви за дефиниране на целочислени данниСинтаксис:

[име] дupekmuвa начална_стойност [,начална_стойност]…

При дефинирането на цяла променлива Асемблерът отделя необходимото количество памет, зададено от директивата, а името в полето на етикета на

Директива Размер в байтове Обхват на целочислените данниBYTE или DB 1 Цели без знак от 0 до 255SBYTE 1 Цели със знак от -128 до 127WORD или DW 2 Цели без знак от 0 до 65535 (2^16 -1)

SWORD 2 Цели със знак от -32768 до 32767

DWORD или DD 4 Цели без знак от 0 до 4294967295 (2^32 -1)

SDWORD 4 Цели със знак от –2^31 до 2^31 - 1

FWORD или DF 6 Цели 48-разредни числа, използвани като променливи-указатели за процесори 1386+

QWORD или DQ 8 Цели 64-разредни числа, използвани с инструкциите на копроцесора 8087

TBYTE или DT 10 Цели 80-разредни числа, използвани с инструкциите на копроцесора 8087

директивата става име на променливата. Показаните в таблицата директиви определят обема на заделената памет и обхвата на стойностите за съответния тип.Типовете данни SBYTE, SWORD и SDWORD дават указание на Асемблера да разглежда инициализиращите стойности в директивите като цели числа със знак, които се използват съвместно с директивите от високо ниво .IF, .WHILE и .REPEAT и директивите PROTO и INVOKE.

18

Page 19: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Инициализиране на даннитеПри дефинирането на данните те могат да получат начални стойности чрез посочване на константи или константни изрази, чиято стойност Асемблерът изчислява в процеса на асемблиране. Ако началната стойност е извън допустимия обхват за съответния тип, генерира се грешка. Числата за начални стойности могат да са десетични, двоични, осмични или шестнадесетични.При дефиниране на променливи обикновено се посочва неопределена стойност чрез символа за въпросителна (?), при което Асемблерът заделя необходимата за съответния тип памет, но не записва стойност в нея. Затова ? се използва при дефиниране на буферни области и променливи, които ще получат стойности по време на изпълнение на програмата.С една директива могат да се дефинират няколко елемента данни, като се запишат няколко начални стойности, разделени със запетаи. Асемблерът ще задели памет за толкова променливи, колкото са инициализиращите стойности, всяка разположена в необходимия брой байтове според типа на данните.

Дефиниране на повторими стойностиКогато трябва да се дефинират множество данни с една и съща начална стойност, може да се използва само една директива, но като начална стойност да се посочи повторим израз с оператора DUP (съкращение от DUPIicate).Синтаксис:

брояч DUP (начална_стойност [ , начална_стойност]

Броячът (цяла константа) задава броя на повторенията на всички начални стойности в скобите. Всяка начална стойност може да бъде цяла константа, символна константа, неопределена начална стойност или друг повторим израз.

Директиви за дефиниране на символи, низове и масивиДирективата BYTE (или DB) за дефиниране на данни от тип BYTE може да се използва за дефиниране на един байт, инициализиран със символна константа (един символ, ограден с апострофи или кавички). При заделянето на един байт Асемблерът записва в него ASCII кода на символа.За дефиниране на низове (последователност от символи, разположени непрекъсната област от паметта) се използва отново директивата BYTE. Това може да стане по два начина: като се изброят един след друг всички символи от низа кат отделни символни стойности в полето за инициализация или се запише константа низ. Вторият начин е по-кратък и е за предпочитане.Двете дефиниции са еквивалентни - за всяка от тях Асемблерът ще отдели последователни байта, в които ще запише ASCII кодовете на символите от низа.По този начин могат да се дефинират низове с дължина до 255 символа. Ако низа не се събира на един ред, той може да се пренесе на следващ или да се разпростре на няколко реда. В този случай редът с продължение трябва да завършва със запетая.С директивата WORD (или DW) за дефиниране на данни от тип WORD също могат да се дефинират низове, но само с дължина 1 или 2 байта.Низовете с дължина два символа, дефинирани с директиви BYTE и WORD се различават по начина на разполагане на ASCII кодовете им в паметта. При директива BYTE първият символ от низа се разполага в младшия байт, а вторият - в старшия байт от двата байта, отделени за низа. При директива WORD се спазва правилото за разполагане на младшата част на стойността в младшия байт (Little Endian Format) и

19

Page 20: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

поради това в младшия байт на думата, отделена за confirm, ще се запише ASCII кодът на буквата 'k1 (6Bh), а в старшия - на буквата 'О' (4Fh), докато в младшия байт на думата, отделена за onechar, ще се запише ASCII кодът на символа '!' (21 h), а в старшия - стойност ООh.Масивите са структури от данни, обединяващи под общо име набор от променливи от един и същ тип, наречени елементи на масива. Елементите на масива се разполагат в последователни области от паметта, така че програмата може да се обръща към тях относително първия елемент. Низовете са масиви от символи.За да се дефинира масив, трябва с директива за дефиниране на данни да се зададе име на масива и последователност от инициализиращи стойности и/или повторими изрази. Списъкът от начални стойности може да заеме няколко последователни реда, като редовете с продължение трябва да завършват със запетая.

Обръщане към даннитеКогато името на променлива, определена с директива за дефиниране на данни, се използва в инструкция, то се отнася за отместването (OFFSET) на първия байт от заделената за променливата памет и при изпълнение ще доведе до копиране на стойността на първия байт от масива ) в регистър АН. Ако трябва да се направи обръщане към някой от следващите байтове, като адрес може да се посочи израз с използване на оператора +.Когато данните са дефинирани с директива, различна от BYTE и SBYTE, тогава изместването на всеки следващ елемент няма да е 1, а размера на паметта, която се заделя от Асемблера за съответния тип (2 за WORD и SWORD, 4 за DWORD и SDWORD и т.н.).Друг възможен начин за адресиране на елементи на масив е с посочване на индекс! в квадратни скоби след името на масива, например 1st [1], wist [4]. Има обаче съществена разлика между индексите при езиците от високо ниво и индексите в асемблерния език. При езиците от високо ниво стойността на индекса винаги отговаря на позицията на елемента в масива, например в езика С а[9] се отнася за десетия елемент на масива а, независимо дали елементите са с дължина 18 байта.В асемблерния език стойността на индекса е равна на броя на байтовете между! елемента и началото на масива. Тази разлика може да се пренебрегне, само ако данните са от тип BYTE или SBYTE - lst[l] се отнася за втория елемент на масива 1st, 1st [2] - за третия и т.н., но wist [4] представлява третия елемент на масива wist, защото той има отместване 4 байта спрямо началото на масива.Двата начина за адресиране на елементи на масив - с индекс и с добавяне на константа към името на масива - са еквивалентни, например wist [6] и wlst+6 представляват един и същ елемент на масива wist (четвъртия). Последователната обработка на всички елементи на масива се прави с помощта на индексен регистър, чиято стойност се използва като индекс, например wist [di]. При начална стойност 0 на DI и последователно увеличаване с константа 2 (типа на елементите) ще се получи достъп до всички елементи на масива.

Адресиране на неименувани данниПри дефинирането на данни не е задължително винаги да им се дава име - към неименуваните данни може да се прави обръщане чрез посочване на адрес относително дефинирано преди или след тях име. Асемблерът разполага данните в паметта по реда на дефинирането им и затова неименуваните данни могат да се адресират с изрази от вида:

име + Константа

20

Page 21: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

име - Константа

Атрибути на даннитеВсички дефинирани данни имат пет специфични характеристики, наречени атрибути на данните: тип (TYPE), дължина (LENGTH), размер (SIZE), сегментен адрес (SEG) и отместване (OFFSET). Атрибутът за тип е равен на броя на байтовете, заделен за всеки елемент от дефинираните данни (определя се от директивата, използвана при дефиницията -1 за BYTE или DB, 2 за WORD или DW и т.н.). Атрибутът за дължина е равен на броя на дефинираните елементи с една дефиниция. Размерът на данните се определя от общия брой байтове, заделени за дефинираните с една директива данни и е равен на типа на данните, умножен по дължината му. Последните два атрибута са съответно началният адрес на сегмента, в който са дефинирани данните и отместването на първия байт на данните спрямо този адрес. Променливата temp има тип WORD (2), дължина 200 и размер 400; zeros има тип BYTE (1), дължина 50 и размер 50, a value - съответно SDWORO (4), 1 и 4. И трите променливи имат един и същ сегментен адрес, защото са дефинирани в един сегмент, temp има отместване 0, защото е дефинирана в началото на сегмента zeros има отместване 400,защото е разположена 400 байта след началото на сегмента, a value - 450.

Оператори за използване на атрибутиОператорът е символ, използван по време на асемблиране на програмата за определяне на стойността на операнд. Операторите се използват в изрази, чиято стойност се изчислява при асемблиране и не се променя по време на работа на програмата.С асемблерния език има 7 оператора, които изчисляват атрибути на данните: TYPE, LENGTH, LENGTHOF, SIZE, SIZEOF, SEG и OFFSET. За да се използва определената от тях стойност като операнд, трябва да се постави съответният символ пред името на променлива или израз.Синтаксис:

TYPE израз

Изчислява се типът на израза.

LENGTH променлива

Изчислява се броят на елементите данни в променливата, създадени от първата инициализираща стойност.

LENGTHOF променлива

Изчислява се броят на елементите данни в променливата

. SIZE променлива

Изчислява се броят на байтовете в променливата, създадени от първата инициализираща стойност.

SIZEOF {променлива I тип}

Изчислява се броят на байтовете в променливата или броят на байтовете, необходими за разполагане на променлива от посочения тип.

21

Page 22: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

SEG израз

Изчислява се сегментния адрес на израза.

OFFSET изразИзчислява се отместването на израза.

Както се вижда от примерите, LENGTH определя дължината на променлива, отчитайки само първата инициализираща стойност (за примера на променливата abc) или най-външния брояч на повторим израз (за примера на променливата-масив matrix), докато операторът LENGTHOF отчита цялата заделена за променливата памет. Операторът SIZE изчислява стойност по формулата TYPE*LENGTH, a SIZEOF - TYPE*LENGTHOF.

Аритметични оператори над операндиСъществуват няколко аритметични оператора за изпълнение на прости аритметични действия над операнди: + (събиране), - (изваждане), * (умножение), / (целочислено делене) и MOD (остатък от целочислено делене). При съставянето на изрази могат да се използват и скоби (действията в тях се изпълняват най-напред).Освен аритметичните оператори и операторите за изчисляване на атрибути при съставянето на изрази могат да се използват още и логически и поразредни логически оператори. Въпреки че изграждането на много сложни изрази се прилага рядко, в табл.5.2 са показани всички допустими оператори в MASM 6.1 с приоритета при изпълнението им (операторите на един ред имат еднакъв приоритет).

Оператор PTR

Когато в инструкция се прави обръщане към дефинирана в паметта променлива, Асемблерът проверява дали двата операнда са от един и същ тип и ако не са, генерира грешка. Например, променливата value е дефинирана от тип WORD, a се използва в инструкция от вида:ah, valueТук двата операнда не са от един и същ тип - регистърът АН е с дължина 1 байт, а променливата value - 2 байта. Обикновено това е грешка при програмиране, но понякога логиката на програмата може да изисква двата байта на променлива от тип WORD да се обработят поотделно. В такива случаи се налага прилагането на оператора PTR (PoinTeR - указател). Операторът PTR принуждава Асемблера да разглежда израза като израз от посочения пред PTR тип. За да се получи достъп до първия байт на променливата value, може да се използва изразът BYTE PTR value, а до втория - BYTE PTR value+1.

Директива LABEL

Директивата LABEL дава възможност да се дефинира име с конкретни атрибути.

Синтаксис:

име LABEL тип

22

Page 23: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

С тази директива не се заделя нова памет, а се създава друго име за обръщане към определено място на програмата. Дефинираното по този начин име се нарича етикет.

Брояч на адреси - символ $ и директива ORG

В процеса на транслиране на програмата Асемблерът поддържа една стойност наречена брояч на адресите, която е равна на отместването на следващия свободен байт от паметта. Когато започне асемблирането на програмата, за всеки но сегмент броячът на адресите се установява в 0. При всяко заделяне на памет за данни или инструкции стойността на брояча на адресите се увеличава с цяла константа, равна на броя на байтовете, необходими за разполагане на данните или инструкциите.В програмата достъп до стойността на брояча на адресите може да се получи с използване на символа $. Например, при срещане на директивата

Current EQU $

Асемблерът ще приравни константата current на стойността на брояча на адресите.Символът $ се използва най-често като прост начин за определяне на дължината на последователности от символи (низове). Например, следващите директиви дефинират низ с име msg и константа с име L_msg, равна на дължината на низа (броя на символите в него):На брояча на адресите може да се зададе конкретна стойност чрез директивата ORG.

Синтаксис:

ORG цял_израз

Тази директива дава указание на Асемблера да започне асемблирането на следващата инструкция от отместване, равно на стойността на целия израз. Използва се обикновено в началото на сегмента за кода, за да се разположи първата инструкция не от отместване 0, а от по-голяма стойност. Това е особено важно за .СОМ файлове.

ORG 100h

Поставена в началото на сегмента за кода, тази директива ще накара Асемблера да разположи първата инструкция на отместване 100h,' а не 0 (всъщност ще остави първите 256 байта от програмата празни).

Достъп до данните с адреси и указатели

Указателят е променлива, съдържаща адреса на друга променлива - адресът в указателя "указва" към другия обект. Указателите са удобни при предаването на големи по обем данни (например масиви) като действителни параметри на процедури. Повикващата програма записва в стека само указател към първия елемент на масива и чрез него повикваната процедура получава достъп до масива, вместо стойностите на всички елементи на масива да се предават чрез стека.Съществува разлика между "далечен адрес" и "далечен указател". Далечният адрес е адрес на променлива, дефинирана в отдалечен сегмент за данни (с

23

Page 24: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

директива .FARDATA или .FARDATA?). Далечният указател е променлива, съдържаща сегментния адрес и отместването на някакви други данни (за разлика от близкия указател, съдържащ само отместването, без сегментния адрес). Както всички останали променливи, така и указателят (близък или далечен) може да бъде разположен в сегмента за данни по подразбиране (близък) или в някой отдалечен сегмент

Десетична аритметика

Освен цели двоични числа и двоични числа с плаваща запетая в асемблерния език могат да се изпълняват и действия над десетични числа, които се представят:. десетични цифри, кодирани с двоични кодове. Тези числа се наричат двоично кодирани десетични числа (Binary Coded Decimals) или още BCD-числа. Употребата им се налага главно за финансови изчисления, при които грешките от закръглени! при преобразуване на числата от десетични в двоични и обратно и при изчисления с двоични числа са недопустими. BCD-числа се използват и при въвеждането и извеждането на числени данни - при въвеждане числата трябва да се въведат като последователност от символи (непакетирани BCD-числа) и да се преобразуван двоични числа, а при извеждане - да се направи обратно преобразуване, за да могат символите на десетичните числа да се изведат на екрана или принтера.Въпреки че по принцип с BCD-числа могат да се представят числа с произволна точност, копроцесорите на основата на i8087 могат да представят само 18-цифрени BCD-числа в памет с размерност 10 последователни байта.За кодирането на десетичните цифри с двоични числа са необходими 4 бита (кодове 0000, 0001, 0010 и т.н. до 1001 за цифрите 0,1 ,...,9) затова в един байт могат да а запишат две десетични цифри. Този начин на кодиране се нарича пакетиран формат за представяне на BCD-числа. Другият възможен начин е да се отдели по един байт за всяка десетична цифра, в който кодът на цифрата да се запише в младшите 4 бита - този формат се нарича непакетиран формат. При въвеждане на числени данни (например от клавиатурата) те се представят в ASCII код. Инструкциите за десетична аритметика изискват числата да бъдат в пакетиран или непакетиран формат, така че въведените ASCII кодове трябва да се преобразуват в един от тези два формата, за да могат да се изпълнят изчисленията над тях. При извеждане BCD-числата трябва се преобразуват обратно, за да могат да се подадат ASCII кодовете им към външните устройства.Преобразуването от ASCII код на число до BCD-число в непакетиран или пакетиран формат може да стане с подходящи логически инструкции и инструкции за преместване. ASCII кодовете на цифрите от 0 до 9 са 30h, 31h,..., 39h, а на непакетираните BCD-числа - OOh, 01h,...,09h. Тогава ASCII код на число може да се преобразува до BCD-число в непакетиран формат с инструкция AND с маска 00001111b (OFh). Преобразуването на две еднобайтови непакетирани числа в еднобайтово пакетирано число може да стане с преместване на 4 разряда вляво на числото със старшата цифра и после обединяване на двете цифри в едно число с операция OR.

Дефиниране на BCD константи и променливи

Непакетираните BCD-числа се състоят от байтове, съдържащи една десетична цифра в младшите 4 бита на всеки байт. При пакетираните BCD-числа във всеки байт се записват по две цифри - една в младшите 4 бита и една в старшите 4 бита.

24

Page 25: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Пакетираните BCD-числа могат да имат до 18 цифри, по две цифри в байт. За всяко BCD-число се отделят 10 байта, като в най-старшия бит на най-левия байт се кодира знакът на числото - 0 за положително и 1 за отрицателно.Пакетирани BCD-числа се дефинират с директивата TBYTE. Ако е посочена инициализираща стойност, Асемблерът попълва с 0 старшите цифри на число, което има по-малко от 18 цифри. Друга възможност е дефиниране с директива BYTE и шестнадесетични начални стойности. Непакетираните BCD-числа могат да бъдат дефинирани с директивата BYTE, Цифрите за началната стойност могат да бъдат подредени в прав или обратен ред в зависимост от програмите за обработка на BCD-числата. В примера по-долу са показани два възможни начина за инициализиране на непакетирани BCD-числа - в числото unpackedr младшите цифри са записани в младшите разреди, а в unpackedf - в старшите.

Действие на инструкциите за десетична аритметика

Процесорът няма инструкции за пряко изпълнение на аритметични действия над десетични числа. Използват се стандартните инструкции ADD, SUB, MUL и DIV за действия над двоични числа, но полученият двоичен резултат се коригира до десетично число със специални инструкции. Десетичните действия се изпълняват байт по байт (с едноцифрени числа при непакетиран формат и с двуцифрени при пакетиран формат). При събиране, изваждане и умножение действието се изпълнява със стандартната инструкция и след това се коригира. При делене първо се коригира делимото, изпълнява се деленето и се коригира частното. При изпълнение на инструкциите за събиране и изваждане се променя флагът за допълнителен пренос AF, който показва дали е необходима корекция на резултата до десетично число.Инструкциите за корекция на резултата не приемат операнд и винаги се изпълняват над стойността в AL, затова първият операнд трябва да бъде винаги в AL. Друго ограничение при изпълнението на действия над BCD-числа е пакетираните числа да участват само в операциите събиране и изваждане.Инструкциите за десетична корекция за непакетирани BCD-числа са: ААА (ASCII Adjust after Addition - ASCII корекция след събиране), AAS (ASCII Adjust after Subtraction - ASCII корекция след изваждане), ААМ (ASCII Adjust after Multiplication-ASCII корекция след умножение) и AAD (ASCII Adjust after Division - ASCII корекция след делене), а за пакетирани BCD-числа: DAA (Decimal Adjust after Addition) десетична корекция след събиране) и DAS (Decimal Adjust after Subtraction) -десетична корекция след изваждане).

Десетично събиранеПървият операнд трябва за се запише в AL, към него да се прибави вторият и след това да се коригира резултатът с инструкция ААА или DAA. Ако резултатът от събирането е твърде голям, за да се побере в AL (по-голям от 9 за непакетирани числа и от 99 - за пакетирани), ААА или DAA вдига флага CF в 1 (ААА увеличава и АН с 1), за да отрази преноса при събирането.Многобайтови BCD-числа се събират байт по байт, започвайки от младшите разреди, като се коригира резултатът от събирането на всеки байт и се отчита стойността на флага за пренос CF за всяко събиране, освен първото. Това обикновено се прави в цикъл, но примерът по-долу показва последователно събиране цифра по цифра на две трицифрени непакетирани числа.При събиране на две трибайтови пакетирани числа действията са аналогични със | следните разлики: вместо инструкцията ААА трябва да се използва DAA, а преносът от най-старшата цифра се определя не от стойността на АН, а от флага CF.

25

Page 26: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Десетично изважданеИзваждането на BCD-числа е аналогично на събирането, но корекцията след изпълнението на SUB или SBB става с инструкциите AAS и DAS. Действието на инструкциите AAS и DAS е аналогично на действието на инструкциите ААА и DAA ако умаляемото е no-малко от умалителя, флагът CF се установява в 1, освен това при AAS стойността на АН се намалява с 1.

Десетично умножениеМогат да се умножават само непакетирани BCD-числа, понеже има само такава инструкция за корекция след умножение (ААМ), която се прилага след MUL, но не и след IMUL. Тъй като всеки от операндите може да има стойност от 0 до 9, резултатът може да е от 0 до 81 и след корекцията той се записва в АХ - старшата цифра в АН, а младшата - в AL. Пренос не може да има, затова флагът CF не се. влияе от инструкцията ААМ (повлияват се само SF и ZF).Инструкцията ААМ коригира резултата от умножението по следния начин: дели числото в AL на 10 и записва частното в АН, а остатъка - в AL Затова тя може да се използва за по-бързо делене на еднобайтово число (записано в AL) на 10.

Десетично деленеЗа разлика от останалите операции над BCD-числа деленето изисква корекция и преди действителното делене. Дели се двуцифрено непакетирано число на едноцифрено. Инструкцията AAD предполага, че в регистрите AH:AL се съдържат двете цифри на делимото (старшата в АН, а младшата в AL) и ги преобразува в двоично цяло число, което се използва като делимо от инструкцията DIV. След изпълнението на DIV частното се записва в AL, а остатъкът - в АН. Ако частното е 9 или по-малко, двоичната стойност в AL е равна на непакетираното BCD-число. При частно по-голямо от 9 стойността в AL трябва да се коригира до двуцифрено непакетирано число с помощта на инструкцията ААМ. Ако остатъкът също е необходим за по-нататъшните изчисления, той трябва да се прехвърли от АН в някой друг регистър.

Директиви за разклонения и цикли

Освен с инструкциите за преходи и цикли основните управляващи структури за разклонения и цикли могат да се програмират и с помощта на директиви (т.нар. точкови директиви, защото имената им започват с точка), които водят до генериране на машинни инструкции за реализация на съответните управляващи структури. Тези директиви отговарят на управляващите структури, използвани от езиците от високо ниво и силно облекчават съставянето на разклонени и циклични програми.

Директиви за генериране на разклонения Директивите .IF, .ELSEIF и .ELSE генерират условни преходи.

Синтаксис:

.IF условие_1последователност_1

[.ELSEIF условие_2последователност_2 ]

[.ELSEпоследователност_3 ] .

26

Page 27: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

ENDIF

Изпълнението започва с оценка на условие_1 след .IF и ако то е вярно, изпълнява-се всички инструкции до срещане на следващата директива .ELSEIF, .ELSE или .ENDIF. Последователност_3 се изпълнява, ако условие_1 не е вярно. Директива .ELSEIF добавя ново условие и свързана с него последователност в алтернативна част на .IF, осигурявайки възможност за съставяне на управляваща структура SWITCH.

Макроси

Макросът е символично име, което се присвоява от програмиста на последователност от символи (текстов макрос) или последователност от изречения (макро процедура или макро функция). При асемблиране на програмата, Асемблерът преглежда сорс кода за предварително дефинирани макроси. При срещане на име на макрос, Асемблерът го замества с текста от макро дефиницията. По този начин може да се избегне многократното повторение на един и същ или близък код в сорса на програмата.Макро процедурите и макро функциите са подобни на процедурите – те се състоят от последователност от инструкции и директиви. Разликата между тях е в това, че процедурата се изпълнява с обръщение към нея с инструкцията CALL, докато инструкциите на макроса се вмъкват в текста на програмата в мястото на поява на името на макроса. На практика с макроса се създава нова инструкция или директива за Асемблера. Макро последователностите се изпълняват по-бързо от процедурите, защото при тях липсват инструкциите CALL и RET. От друга страна те водят до раздуване на кода на програмата, защото се вмъкват във всяко място на срещане на името на макроса Поради това използването на макроси се препоръчва за програми, за които основни изисквания са бързодействието и ефективността.

В MASM 6.1 могат да се използват могат да се използват следните видове макроси:

Текстови макроси, които се разширяват до текст в сорса на програмата

Макро процедури, които се разширяват до едно или повече изречения и могат да имат и параметри

Повторими блокове, които генерират група от изречения зададен брои пъти или докато зададено условие е вярно

Макро функции, които са подобни на макро процедурите, но могат да връщат и стойност

Предварително дефинирани макро функции и директиви за операции над низове

Текстови макросиНай-простата макро директива е TEXTEQU. С нея може да се присвои символично име на произволен текст (последователност от символи) и след това да се използва това име вместо текста в сорса на програмата.Синтаксис:

27

Page 28: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

име TEXTEQU <mekcm>име TEXTEQU uмe_нa_cmap_мakpocиме TEXTEQU константен_израз

В първата синтактична форма текстът е последователност от символи, затворени в ъглови скоби. Във втората като име на стар макрос може да се посочи име на предварително дефиниран текстов макрос или на макро функция. В третата форма константният израз се оценява и се замества с текст.Текстовите макроси са удобни за именуване на низове, които не представят цели числа. С текстов макрос може да даде име на реална константа или на израз.

Макро процедуриАко програмата трябва да изпълни някаква задача многократно, записването на една и съща последователност от инструкции на много места в сорса може да се избегне със създаването на макро процедура. На местата в програмата, където трябва да се запише последователността от инструкции, се поставя името на макро процедурата. В такъв случай се говори за „обръщане към макро процедура", въпреки че това коренно се различава от обръщането към подпрограма. „Обръщането към макрос" означава, че при асемблиране на програмата името на макроса се замества с последователността от изречения, включени в дефиницията на макроса. Казва се още, че макросът се разширява и се получава т.нар. макро разширение.

Създаване на макро процедуриМакро процедура без параметри може да се дефинира, като се поставят необходимите изречения между директивите MACRO и ENDM.

Синтаксис:

име MACROпоследователност_от_изречения

ENDM

Името пред ключовата дума MACRO става име на макроса. Пример за макро процедура без параметри е показан по-долу - в макрос с име веер са обединени инструкции за включване на звуковия сигнал. След това името веер може да се включи на произволно място в програмата като самостоятелно изречение.Коментарите в макро дефинициите могат да започват с двоен символ ';;' и тогава се появяват в листинга на програмата само в макро дефиницията, но не и в макро разширението, което не претрупва листинга с излишни повтарящи се коментари. Това не е в сила за обикновените коментари, обозначени с ';' - те се включват във всички макро разширения.

Параметри на макро процедуриСъс задаване на параметри на макрос може да се дефинира една по-обща задача и след това да се изпълняват различни нейни варианти с предаване на различни параметри при обръщане към макроса. В пълния синтаксис на макро процедура се включва и списък от параметри:

име MACRO списък_от__параметрипоследователност_от_изреченияENDM

28

Page 29: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Списъкът от параметри може да съдържа произволен брой параметри, разделени със запетаи. Като имена на параметри не могат да се използват ключови думи.За да се предадат параметри към макрос, при обръщането към него след името му се записват действителните параметри:

Име_Ha_макрос списък_от_действителни_параметри

Макро параметрите са подобни на параметрите на процедура, но са налице и съществени разлики. Параметърът на процедура има тип и място в паметта, неговата стойност може да бъде променена вътре в процедурата. Параметърът на макрос е само запазено място за текста на действителния параметър, стойността му може само да бъде присвоявана на друго име или директно използвана - тя не може да бъде променена.

Задължителни параметри и параметри по подразбиране

Параметрите на макрос могат да имат специални атрибути, които да направят по-гъвкаво използването им и да подобрят обработката на грешки. Параметрите могат да бъдат дефинирани като задължителни, да им се дават стойности по подразбиране или да се променя броят им. Променлив брой параметри може да се използва само с директивата FOR и тази възможност се разглежда по-долу.Параметър на макро процедура може да се дефинира като задължителен със ключовата дума REQ (REQuired) пo следния начин:параметър:REQАко при обръщането към макрос със задължителен параметър той не се посочи, асемблерът генерира грешка в реда на програмата, включващ обръщането. Например, макро процедурата writechar може да се подобри от гледна точка на откриване на грешки, като параметърът char се направи задължителен:От друга страна, някои параметри могат да се пропуснат при обръщането към макроса, ако при дефинирането на макроса им се зададат стойности по подразбиране:параметър:=<mekcm>Например, ако макросът writechar се използва многократно в програмата за включване на звуковия сигнал и в по-редки случаи за извеждане на друг символ, той може да се преработи така, че при пропускане на действителен параметър Асемблерът да използва по подразбиране символа с ASCII код 7:Сега при обръщане към writech без действителен параметър Асемблерът ще използва в макро разширението стойността по подразбиране.Пропуснати параметри в обръщането към макрос могат да бъдат обработвани и с директивите за условно асемблиране IFB, IFNB, .ERRB и .ERRNB, поставени вътре в тялото на макро процедура или функция. Директивата IFB връща стойност „истина", ако посоченият параметър липсва, a IFNB - ако не липсва. .ERRB и .ERRNB генерират грешка съответно при липсващ и не липсващ параметър. Пълният синтаксис на директивите е представен в глава 12.Макросът служи за скроулинг на екрана на монитора надолу или нагоре в зависимост от стойността на първия параметър - положителна или отрицателна.

Управление на клавиатурата и дисплея

В тази глава се разглеждат въпросите за използването на клавиатурата и дисплея, свързани към персоналния компютър, който работи под управлението на MS-DOS ми

29

Page 30: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

PC DOS, чрез използването на системните функции на DOS и BIOS. Обръщането към тези функции се прави с инструкция за прекъсване INT, като номерът на избраната функция се зарежда в инструкция АН. Въвеждане на данни от клавиатурата

Стандартен вход/изход с клавиатурата и дисплеяЗа да бъдат програмите преносими за различните компютри, работещи под DOS, те трябва да използват стандартните методи за въвеждане на данни от клавиатурата и извеждане на данни към дисплея. Тези стандартни методи използват функциите на DOS с прекъсване INT 21 h. Съществуват няколко функции за тази цел, най-простите от които четат един символ от стандартното входно устройство (обикновено клавиатурата) и извеждат един символ към стандартното изходно устройство (обикновено екрана на дисплея). Един символ от клавиатурата с ехо на екрана (въведеният символ се появява на екрана в позицията на курсора) се въвежда с функция с номер 01h, подаден като входен параметър в регистър АН. ASCII кодът на въведения символ се връща в ALАко стандартното входно устройство е клавиатурата, тези две инструкции ще въведат един символ от нея. Входът и изходът на DOS могат, обаче, да се пренасочват. Тогава символът може да не се прочете от клавиатурата, а например от дисков файл myfile.txt, ако входът е пренасочен по следния начин:C:\program <myfile.txtDOS функцията 01 h проверява за въвеждане на CTRL+C (спиране на програмата) и в този случай се изпълнява прекъсване с номер 23h, с което се прекратява изпълнението на програмата и управлението се връща към DOS.За извеждане на един символ към стандартното изходно устройство се използва функцията 02h, като преди обръщане към DOS в регистър DL се зарежда извежданият символ.Функцията 02h реагира на CTRL+C по същия начин, както и 01 h и отново може да бъде пренасочена, например към дисков файл с командата:С: \program >newf ile.txtПредимството на функциите на DOS за въвеждане и извеждане на данни със стандартните устройства се състои в това, че програмата не трябва да предвижда каквито и да е допълнителни действия за промяна на устройството, от което да постъпват входните данни или към което да се извеждат. При пренасочване на входа и изхода на DOS те продължават да работят по предназначението си.

Управление на клавиатурата

При натискане на клавиш от клавиатурата тя изработва сигнал, наречен клавишен код. Клавишният код показва само кой клавиш е бил натиснат, а не стойността, която се присвоява на клавиша. Преминаването от този клавишен код към ASCII код става на по-късен етап в компютърната система. Самата клавиатура работи само с клавишните кодове, които зависят от типа и конструкцията й.Клавиатурата има вграден микропроцесор, който приема генерираните от самата клавиатура клавишни кодове. Той изпълнява няколко операции, включително тестването на операцията и целостта на веригата, но най-важната е интерфейсът с централния процесор. Типът на този микропроцесор зависи от типа на компютъра, но независимо от него основната му функция е да освободи централния процесор от необходимостта на управлява работата на клавиатурата. Той осигурява както

30

Page 31: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

изпращането на клавишните кодове към процесора, така и повторението на клавишни кодове при продължително натискане на клавиш.'Микропроцесорът в клавиатурата обменя данни с централния процесор чрез съгласуващ (handshaking) сериен протокол, според който всеки байт, изпратен към процесора, изисква сигнал за потвърждение и на всеки байт, изпратен към клавиатурата, също се изпраща потвърждение. Затова от страната на компютъра към клавиатурния кабел е включен контролер на клавиатурата. И отново типът на този контролер зависи от компютъра, но основното му предназначение е да преобразува получените от клавиатурата клавишни кодове до стойност, която се свързва с всеки клавиш или комбинация от клавиши. Тази стойност е с дължина един байт, нарича се скан-код и се подава към централния процесор чрез входно-изходните портове.След като централният процесор получи цял байт от контролера (скан-кода), автоматично се предизвиква BIOS прекъсване INT 09h. Програмата за обработка на това прекъсване получава влизащия байт от изходния порт на контролера на клавиатурата (с адрес 60п) и определя какво да прави с него. В зависимост от стойността на този байт се изпълняват различни действия, а именно:* Ако е бил натиснат някой системен клавиш като Shift, Alt, Ctrl, Caps Lock, Num Lock или Scroll Lock, се вдига специален вътрешен флаг, отразяващ състоянието на съответния клавиш;* Ако е бил натиснат клавиш Pause или Ctrl+Num Lock, системата преминава в състояние на изчакване, от което може да се излезе чрез натискане на клавиш, различен от Caps Lock, Num Lock, Scroll Lock, Sys Request, Alt или Ctrl;» При натискане на клавиш Sys Request, се предизвиква прекъсване INT 15/85/00, а при отпускането му - INT 15/85/01;* При едновременно натискане на клавишите Ctrl, Alt и Del системата се рестартира;4 При натискане на клавиш Print Screen се предизвиква прекъсване Int 05h;* При едновременно натискане на клавишите Ctrl и Break се предизвика прекъсване INT 1Bh, което стартира DOS програмата за обработка сигнала Ctrl+Break;* Ако се натискат клавиши от допълнителната цифрова клавиатура п; задържан клавиш Alt, цифрите се натрупват и от тях се изработва ASCII код при отпускане на Alt;* При натискане на някой друг клавиш неговият скан-код се преобразува: ASCII код, като се отчита състоянието на флаговете на клавишите Q Shift и Alt.При натискане на клавиш или комбинация от клавиши изработеният от програма-за обработка на прекъсване INT 09h код се помества в специална област: паметта, наречена буфер на клавиатурата. Буферът на клавиатурата е с дължина 32 байта (16 думи) и достъпът до него става с два указателя - един за началото буфера и един за края му. Указателят към началото на буфера сочи към адрес; който може да се разположи кодът на следващия въведен от клавиатурата символ указателят към края на буфера - към адреса, в който е записан кодът на ft. отдавна въведения символ (този, който може да се прочете от някоя от функции за въвеждане на данни от клавиатурата). Може да се каже, че въвеждането на един символ от клавиатурата попълва пореден елемент от буфера, а четенето на символ от програмата изтрива един елемент от буфера. Поради по-особената си цикли1 структура буферът на клавиатурата може да поеме до 15 кода, които да се чете един по един по реда на въвеждането им. При препълване на буфера се издава предупредителен звуков сигнал.Всеки код за въведен символ се разполага в една дума от буфера. В младшия бас на думата се разполага ASCII кодът на въведения символ, а в старшия - скан-кода му. При някои комбинации от клавиши изработеният от INT 09h ASCII код е 00' например ASCII кодът на символа 1 е 31 п, а на Alt-1 е ООh. Двата клавиша с: различават по

31

Page 32: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

стойностите на скан-кода: за 1 той е 02h, a за Alt-1 e 78h. При четен на един символ се прочита стойността на младшия байт, т.е. ASCII кодът к символа. Ако тази стойност е ООh, тогава трябва да се прочете и стойността старшия байт на думата (скан-кода) и по нея да се определи коя комбинация . клавиши е въведена. Често съвкупността от ASCII код OOh и скан-код се нария разширен A SCII код.

Функции за въвеждане на един символ от клавиатурата

Въвеждане на един символ с ехо на екранаЗа въвеждане на един символ с ехо на екрана може да се използва разгледаната вече no-горе DOS функция 01 п. Връщане от функцията става само след въвеждане на символ. Въвеждане на един символ без ехо на екранаЗа тази цел могат да се използват три функции на DOS с номера 06h, 07h и 08h.Функция 06h е предназначена за директен достъп до конзолата и също може да чете кодове на специални символи. Освен номера на функцията в АН, преди обръщане към INT 21 h в регистър DL трябва да се зареди стойността OFFh. За разлика от функция 01 h, функцията 06h връща управлението от INT 21 h даже и ако не е въведен символ от клавиатурата и в този случай флагът ZF е вдигнат.Функция 07h и функция 08h са аналогични на 01 h , но без да показват въведения символ на екрана (няма ехо). Съществува още едно разграничение на функциите на DOS за въвеждане на един символ: функциите 01 h и 08h разпознават въвеждането на Ctrl+C или Ctrl+Break, докато 06h и 07h го пренебрегват. Освен това функциите 01 h, 07h и 08h могат да четат символи и от пренасочено входно устройство на DOS.Въвеждане на един символ с BIOS прекъсване INT 16hОсвен функциите на DOS, за въвеждане на един символ може да се използва и BIOS прекъсване INT 16h. При AH=OOh (за 83-клавишна клавиатура) или 10h (за 101-клавишна клавиатура) прекъсване INT 16h чете един символ от буфера на клавиатурата и връща ASCII кода му в AL, а скан-кода му - в АН. Ако буферът на клавиатурата е празен, се изчаква въвеждане на символ от клавиатурата. Функцията не реагира на Ctrl+C и не предоставя ехо на екрана.

Проверка за натиснат клавишВ приложното програмиране често се срещат ситуации, при които програмата трябва да реагира на натискането на клавиш, но да продължава изпълнението си, ако няма въведени входни данни, например текстообработваща програма изпълнява операция „Търсене и заместване", която трябва да може да бъде прекъсната с клавиша ESC. В такива случаи програмата трябва често да проверява дали са постъпили данни от клавиатурата и да ги чете само ако в буфера на клавиатурата има чакащи данни.За проверка за натиснат клавиш могат да се използват две функции: DOS функция OBh и BIOS прекъсване 16h, функция 01 h (83 клавиша) или 11h (101 клавиша). DOS функция OBh връща в регистър AL стойност 0, ако няма чакащи за четене данни в буфера или OFFh в противен случай. С нея може да се направи прост цикъл, изпълняващ отново и отново някаква процедура, като въведените от клавиатурата символи се четат едва след появата им, а не се чака въвеждането на клавиш. Често при разработване на приложни програми, свързани с въвеждането на голямо количество данни, разработчиците се опасяват от преждевременно напускане л програмата при случайно натискане на ESC от страна на потребителя. В такива случаи се предпочита натискането на някаква „екзотична" комбинация от клавиши* например Ctrl + Alt + ляв SHIFT. За целта може да се използва BIOS прекъсване IN1 16h, функция 02h, която проверява състоянието на основните специални клавишни връща в AL статуса им: дали

32

Page 33: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

са включени клавишите Insert, Caps Lock, Num Lock г Scroll Lock и дали са натиснати клавишите Alt, Ctrl, ляв или десен Shift.

Ако е необходимо да се установи дали са едновременно натиснати клавишите

Ctrl + Alt + ляв SHIFT, трябва да се изпълни BIOS прекъсване INT 16h,

функция 02h и след това да се провери дали битове с номера 1, 2 и 3 на

регистър AL имат стойност 1.

Почистване на буфера на клавиатуратаПри програмиране на подкана за отговор от страна на потребителя от вида "да/не" за опасни операции (например изтриване на важен дисков файл) е добре първоначално да се почисти („изпразни") буферът на клавиатурата, за да няма в него останали непрочетени символи. Единият възможен начин за това е да се прочетат последователно всички символи от буфера, докато той се изпразни. Друга възможност е да се използва специална DOS функция OCh, която почиства буфера и след това изпълнява една операция за четене на символ. Както и в останалите случаи на обръщане към INT 21 h, номерът на функцията OCh се зарежда в AH, a в AL се записва номерът на функция за въвеждане на данни: 01h, 06h, 07h, 08h или OAh.

Въвеждане на един ред от символиС DOS функция OAh може да се прочете наведнаж цял ред от символи (до 255 символа), завършващи с клавиша Enter (ODh), с ехо на екрана. Преди обръщане към нея в DS:DX трябва да се съдържа адресът на област от паметта, в която да се запишат ASCII кодовете на въведените символи. Първият байт на тази област трябва да съдържа число, равно на максималния брой на символите, които могат да се запишат в областта, а вторият се попълва от функцията с действителния брой на медените символи. Самите символи се разполагат от третия байт на областта, така че ако трябва да се въведе низ с дължина до 80 байта, за него трябва да се запазят 82 байта. Тази функция не осъществява връщане от DOS, докато не се въведе Enter. Ако дефинираната област се запълни преди срещане на Enter и се въвеждат още символи, се включва предупредителен звуков сигнал.

Извеждане на данни на екрана на дисплея в текстов режим с DOS и BIOS функции

DOS и BIOS предоставят множество функции за извеждане на отделни символи, цели низове, за позициониране на курсора на екрана на дисплея, за промяна на размера на курсора и др., при които отпада необходимостта от директно адресиране на видео паметта. По-долу са разгледани най-често използваните от тях.

Извеждане на един ASCII символОсвен с DOS функция 02h (вижте Приложение 2.2), на екрана на дисплея е позицията на курсора може да се изведе един символ и с функция О6h. Тази функция може да се използва както за въвеждане на един символ, така и за извеждане. Действието се определя от стойността на регистър DL: ако тя е OFFh, м чете един символ от стандартното входно устройство, а ако е в интервала от 0 де 254 (0 - OFEh), стойността на DL се счита за ASCII код на символа, който се извежда към стандартното изходно устройство.

33

Page 34: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Извеждане на низ от символиС DOS функция 09h на екрана на дисплея в позицията на курсора може да се изведа низ от символи - последователност от ASCII кодове на символи. Низът трябва да завършва със символа $ (24h), който не се извежда, а само служи като сигнал за край на низа. Преди обръщане към функцията регистрите DS:DX трябва да съдържат началния адрес на низа.

Алгоритъм за преобразуване на буквите

След въвеждането на низа от потребителя, той се намира в масива buf.Преобразуването на буквите протича на следните стъпки:1. Проверява се дали buf[1], в който се съдържа броя въведени символи, е равен на 0. Ако е равен се излиза от програмата.2. В CX се записва броя въведени символи.3. В BX се записва адреса в паметта, от който започва низа (buf[2]).4. Проверява се дали символа записан на адреса, сочен от BX, е малка буква(дали ASCII кода му е между 61h и 7Аh).5. Ако е изпълнена т.4 от ASCII кода се изважда 20h, с което се получава ASCII кода на главна буква.6. Увеличава се BX с 1, така че да сочи към следващия символ.7. Изпълняват се стъпки 4-6, докато има още символи.

34

Page 35: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Изчислителен метод , организация на данните и сорс на програмата

За изпълнение на задачата се използва следния метод:За по-голяма ефективност на програмата се дава вазможност за превращането и на стринг , а символа е като частен случай на стринга. Затова в програмата работя с стрингове, а не с символи. В data segment се дефинират два стринга и максималната далжина на стринга:

.DATAМAXLENGTHEQU 1000

StringToReverse DB MAXLENGTHDUP(?) ReverseString DB MAXLENGTHDUP(?)След като се ваведе стринга от клавиатурата се проверява дали са ваведени символи ако не са се преминава към край на програмата. Това се прави чрез акумулатора АХ: and ax,ax jz Done ................................ Done: mov ah,4ch int 21h .EXIT

Стринга се вавежда в StringToReverse . След това се намалява акумулатора АХ (който съдържа дължината на стринга) с две понеже се чете символа край на ред и се записва в регистъра СХ, който се използва като брояч.

dec ax dec ax mov cx,ax push cx

В регистъра ВХ се записва адреса на стринга, който сме въвели,a в Si готовия стринг, т. е. сочат първите елементи:

mov bx,OFFSET StringToReverse mov si,OFFSET ReverseString

След това се започва цикъл за обхождане на масива. Цикъла се оправлява от СХ , в който е записана дължината на стринга, който сме въвели. В AL се зарежда първия символ от стринга. Прави се проверка дали символа е буква и дали е малка, ако не е не се превраща в голяма а се преминава към четене на следващия символ от стринга. Самото прeвръщане в голяма буква се прави с 20 прекъсване ( 20h ). Голямата буква, която е в AL , се прехварля в регистара Si, на първа позиция на готовия стринг. След това се прави четене на следващия символ от стринга , чрез увеличаване на ВХ и Si, след което те сочат следващите символи:

ReverseLoop: mov al,[bx] cmp al,'a' jb @2

35

Page 36: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

cmp al,'z' ja @2 sub al,20h@2: mov [si],al inc bx inc si

loop ReverseLoop pop cx

Цикъла се изпълнява толкова колкото е дължината на ваведения стринг т. е. ‘ СХ пъти ’.

Стринга ,който сочи Si – ReverseString e вече с големи букви. След това следва извеждане на този стринг : mov ah,40h mov bx,1 mov dx,OFFSET ReverseString int 21h

Програмата се прекасва след извеждането на стринга.

Сорс на програмата:

icrosoft (R) Macro Assembler Version 6.1a 12/05/01 13:30:19KURSOVAF.ASM Page 1 - 1

;MASM 04 -KURSOVA ZADACHA;DIANA DIMITROVA 76gr. Fak.N PK027738

.MODEL small

.STACK 100h 0000 .DATA = 03E8 MAXLENGTHEQU 1000 0000 03E8 [ StringToReverse DB MAXLENGTHDUP(?) 00 ] 03E8 03E8 [ ReverseString DB MAXLENGTHDUP(?) 00 ] 0000 .CODE

.STARTUP 0017 B8 ---- R mov ax,@data 001A 8E D8 mov ds,ax 001C B4 3F mov ah,3fh 001E BB 0000 mov bx,0

36

Page 37: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

0021 B9 03E8 mov cx,MAXLENGTH

0024 BA 0000 R mov dx,OFFSET StringToReverse 0027 CD 21 int 21h 0029 23 C0 and ax,ax 002B 74 28 jz Done 002D 48 dec ax 002E 48 dec ax 002F 8B C8 mov cx,ax

0031 51 push cx 0032 BB 0000 R mov bx,OFFSET StringToReverse 0035 BE 03E8 R mov si,OFFSET ReverseString

0038 ReverseLoop: 0038 8A 07 mov al,[bx] 003A 3C 61 cmp al,'a' 003C 72 06 jb @2 003E 3C 7A cmp al,'z' 0040 77 02 ja @2 0042 2C 20 sub al,20h 0044 @2: 0044 88 04 mov [si],al 0046 43 inc bx 0047 46 inc si

0048 E2 EE loop ReverseLoop 004A 59 pop cx

004B B4 40 mov ah,40h 004D BB 0001 mov bx,1 0050 BA 03E8 R mov dx,OFFSET ReverseString 0053 CD 21 int 21h 0055 done: 0055 B4 4C mov ah,4ch 0057 CD 21 int 21h

.EXITEND

Microsoft (R) Macro Assembler Version 6.1a 12/05/01 13:30:19KURSOVAF.ASM Symbols 2 - 1

Segments and Groups:

N a m e Size Length Align Combine Class

DGROUP . . . . . . . . . . . . . GROUP

37

Page 38: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

_DATA . . . . . . . . . . . . . 16 Bit 07D0 Word Public 'DATA'STACK . . . . . . . . . . . . . 16 Bit 0100 Para Stack 'STACK' _TEXT . . . . . . . . . . . . . 16 Bit 005D Word Public 'CODE'

Symbols:

N a m e Type Value Attr

@2 . . . . . . . . . . . . . . . L Near 0044 _TEXT@CodeSize . . . . . . . . . . .Number 0000h @DataSize . . . . . . . . . . . Number 0000h @Interface . . . . . . . . . . . Number 0000h @Model . . . . . . . . . . . . . Number 0002h @Startup . . . . . . . . . . . . L Near 0000 _TEXT@code . . . . . . . . . . . . . Text _TEXT@data . . . . . . . . . . . . . Text DGROUP@fardata? . . . . . . . . . . . Text FAR_BSS@fardata . . . . . . . . . . . . Text FAR_DATA@stack . . . . . . . . . . . . . Text DGROUPMAXLENGTH . . . . . . . . . . . Number 03E8h ReverseLoop . . . . . . . . . .L Near 0038 _TEXTReverseString . . . . . . . . . Byte 03E8 _DATAStringToReverse . . . . . . . . Byte 0000 _DATAdone . . . . . . . . . . . . . . L Near 0055 _TEXT

0 Warnings 0 Errors

Избор на тестови стойности за данните

За тестване са подбрани следните стрингове и символи:

1. StringToRevers = ‘s’ - един символ малка буква2. StringToRevers = ‘1’ – един символ, който не е буква3. StringToRevers = ‘simeon’ – стринг от малки букви 4. StringToRevers = ‘Simeon!’ – стринг, в който има и малки и големи букви, и символи,

които не са букви.

Резултатите трябва да са следните:

1. ReverseString = ‘S’2. ReverseString = ‘1’3. ReverseString = ‘SIMEON’4. ReverseString = ‘SIMEON!’

Символите, които не са букви и главните букви си остават непроменени след изпълнение на програмата.

План за трасиране на програмата и наблюдавани регистри .

38

Page 39: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

1 Стартиране на програмата и задаване на тестови стойности от клавиатурата

2 Поставяне на Breakpoint за трасиране

3 Наблюдаване на регистрите

39

Page 40: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

4 Трасиране

Трасирането се извършва чрез последователно преминаване през програмата с F8.

5 Изходни данни – символа ( стринга ) превърнат в голема (и) буква .

40

Page 41: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Наблюдаваните регистри са:AH - стандартни функции извиквани от програматаAX - съхранение на дължината на AL – съхранение на символ от стринга SI,BX – пойнтери CX – брояч

Резултати от изпълнението и коментар за трасираните стойности Задачата е изпълнена на MASM 6.1 , а за трасиране е използван CV на същия асемблер. Трасирането се извършва последователно за четирите случая изброени по горе. StringToRevers = ‘1’ – това е най-лесния за трасиране случай понеже превръщане не се осъществява и след проверката се излиза от цикъла. StringToRevers = ‘s’ – при този случаи цикъла се осъществява един път и се пременава към изход от програмата. StringToRevers = ‘simeon’ – цикъла се изпълнява шест пъти като превръщане се осъществява при всеки символ StringToRevers = ‘Simeon!’ – цикъла се изпълнява седем пъти като при проверка на първия и последния символ се прескача превращането .

41

Page 42: Да се тества и трасира изпълнима програма за процесор Intel X86 – превръщане на малка ASCII буква в голяма

Литература

1. Егоров А., Стоянова Р., Програмиране на асемблерен език за 32-битови микропроцесори Intel част 1 и 2, Paraflow Ltd., София, 1997 и 1998.

2. доц. д-р А. Егоров, Организация на компютри - лабораторен практикум , версия 6.01 ФКСУ ТУ София, NEXT, София, 2001

Opcodes2.hlp Cpu_lab4.pdf

42