121
1 Сборник задачи по програмиране на Асемблер 08 септември 2011 16:04:09 http://x86.hit.bg/ Сборник задачи по програмиране на Асемблер гл. ас. инж. Лъчезар Илиев Георгиев , катедра КНТ (ИТ), ФИТА, ТУ–Варна (ВМЕИ–Варна) гр. Варна, 2011 г.

Сборник задачи по програмиране на Асемблер

Embed Size (px)

DESCRIPTION

Сборник за начинаещи

Citation preview

Page 1: Сборник задачи по програмиране на Асемблер

1Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Сборник задачи по програмиране на

Асемблергл. ас. инж. Лъчезар Илиев Георгиев , катедра КНТ (ИТ), ФИТА, ТУ–Варна (ВМЕИ–Варна)

гр. Варна, 2011 г.

Page 2: Сборник задачи по програмиране на Асемблер

2Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0. Съдържание

1. Увод2. Прости изчисления

1. Намиране на кода на Грей на двоично число2. Намиране на двоично число по даден код на Грей3. Намиране на най-малко общо кратно (НОК)4. Намиране на най-голям общ делител (НОД)

3. Контролни суми на масиви от данни1. Намиране на контролна сума на Адлер („Adler-32“)2. Намиране на контролен цикличен код с излишък („CRC32“)3. Генерация на таблица на контролни циклични кодове с излишък („CRC32“)

4. Използване на таблици от константи1. Компресия на звукови отчети по µ-закон2. Компресия на звукови отчети по A-закон3. Декомпресия на звукови отчети по µ-закон4. Декомпресия на звукови отчети по A-закон

5. Манипулация на битове1. Огледално обръщане (реверсиране) на битовете2. Транспониране на квадратна матрица от битове

6. Обработка на символни низове1. Въвеждане на ASCIIZ-низ от клавиатурата2. Извеждане на ASCIIZ-низ на екрана3. Намиране на дължината на ASCIIZ-низ4. Намиране на броя на думите в ASCIIZ-низ5. Намиране на броя на думите в ASCIIZ-низ (ускорено)6. Преобразуване на число в ASCIIZ-низ от римски цифри7. Проверка на парола във вид на ASCIIZ-низ8. Търсене на ASCIIZ-подниз в ASCIIZ-низ9. Търсене на ASCIIZ-подниз в ASCIIZ-низ (без разлика на малки/главни букви)

10. Огледално обръщане на низ7. Низове от пакетирани двоично-десетични числа

1. Емулация на командата ADD4S на NEC V20/25/30/33/35/40/41/50/51/53/552. Емулация на командата SUB4S на NEC V20/25/30/33/35/40/41/50/51/53/553. Емулация на командата CMP4S на NEC V20/25/30/33/35/40/41/50/51/53/554. Въвеждане от клавиатурата на низ от пакетирани двоично-десетични цифри5. Извеждане на низ от пакетирани двоично-десетични цифри на екрана

8. Обработка на интервали от време1. Намиране на датата на източноправославния Великден за дадена година2. Намиране на датата на римокатолическия Великден за дадена година3. Намиране на деня от седмицата за дадена дата4. Преобразуване на UNIX-време в дата и време на файл в ДОС5. Преобразуване на дата и време на файл в ДОС в UNIX-време

9. Подреждане на масиви от данни1. Сортиране с приоритет на простотата2. Сортиране с приоритет на бързодействието

10. Величини с променлива дължина1. Четене на величина с променлива дължина2. Запис на величина с променлива дължина

11. Цели числа с произволна дължина1. Събиране на цели числа с произволна дължина2. Изваждане на цели числа с произволна дължина3. Смяна на знака на цяло число с произволна дължина

Page 3: Сборник задачи по програмиране на Асемблер

3Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

4. Сравнение на цели числа с произволна дължина5. Преобразуване от ASCIIZ-низ в цяло число с произволна дължина6. Преобразуване на цяло число с произволна дължина в ASCIIZ-низ

12. Преобразуване на числа от една бройна система в друга1. Преобразуване от десетична в шестнадесетична бройна система2. Преобразуване от шестнадесетична в десетична бройна система

13. Операции върху числа с двойна и четворна разрядност1. Извличане на квадратен корен2. Беззнаково умножение на две 32-битови числа3. Умножение 32 × 32 бита с приоритет на бързодействието4. Беззнаково делене на 64-битово число на 32-битово5. Деление 64 : 32 бита с приоритет на бързодействието

14. Асемблери, директиви и синтаксис на командите1. Съвместими с MASM версия 5.1 или по-нова

1. Microsoft QuickAssembler (QASM)

2. Microsoft Macro Assembler (MASM)

3. Watcom Macro Assembler (WASM)

4. Japheth ’s Watcom Macro Assembler (JWASM)

5. Light Macro Assembler (LASM)

6. Turbo Assembler (TASM)

7. Paradigm Assembler (PASM)

8. IBM Assembly Language Processor (ALP)

2. Съвместими с по-стари версии на MASM1. IBM Personal Computer Assembler (ASM)

2. Turbo Editasm (TASMB)

3. Turbo Editasm Macro Assembler (TASMC)

4. Optimizing Assembler (OPTASM)

5. ASM86 Macro Assembler

6. A86 Assembler

7. A386 Assembler

8. Phar Lap Macro Assembler (386 |ASM)

3. Несъвместими с MASM1. Lazy Assembler (LZASM)

2. Netwide Assembler (NASM)

3. NewBasic Assembler (NBASM)

4. Relocatable Assembler (RASM-86)

1. Увод

Настоящият сборник съдържа 50 решени задачи във вид на изпробвани и работоспособни 16-битови програмина езика Асемблер, които могат да се изпълняват на микропроцесорите и микроконтролерите от долнататаблица (наричани по-долу „x86“), със следните 2 изключения:

1. Програмите, включващи директивата „.186“, не могат да се изпълняват на микропроцесорите от първотопоколение 8086/8088 и техните аналози (подчертани в таблицата и наричани по-долу „8086“). Те изискватпоне 80186/80188 или някой от аналозите им (наричани по-долу „186“), включително тези от изключение№2.

2. Програмите, използващи командата „aad“ с непосредствен операнд, дават грешни резултати намикропроцесорите и микроконтролерите на фирмата NEC V20, V25, V30, V33, V35, V40, V41, V50, V51,V53, V55 и аналозите им (в таблицата дадени с номиналното си обозначение в получер шрифт). Това еотбелязано и в коментара на съответните програми.

Page 4: Сборник задачи по програмиране на Асемблер

4Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

№Фирма-

производителкаСтрана Микропроцесор / микроконтролер

1. Advantech Co., Ltd. Тайван EVA -X4150, EVA -X43002. ALi Тайван M1386, M6117

3. AMD САЩ8086, 8088, Am186, Am188, Am286, Am386, Am486, Am5x86, K5, K6,K7, K8, K9, K10, K11, Bobcat, Élan SC300, SC310, SC400, SC410, SC520

4. Ангстрём СССР/Русия К1847ВМ2865. Chips & Technologies САЩ Super386, F86806. Cyrix САЩ Cx486, 5x86, 6x86, MII, MXi, MediaGX7. DEC САЩ Am4868. DM&P Тайван M6117, Vortex86, PDX-600, PMX-10009. Eteq Microsystems САЩ ET486

10. Fujitsu ЯпонияMBL8086, MBL8088, MBL80186, MBL80188, MBL80C188, MBL80286,Pentium

11. Genesis Microchip Канадаgm1601, gm2621, gm5621, gm5626, gm5766, gm5822, gm5828, gm5861,gm5862, gm5868, FLI5921, FLI5961, FLI5962

12. Harris Semiconductor САЩ 80C86, 80C88, 80C28613. Hitachi Япония H80C8814. IBM САЩ 80286, 386, 486, 5x86, 6x8615. IDT САЩ WinChip C6, WinChip 216. Infinior Microsystems Южна Корея IMS16, IMS5016

17.InnovasicSemiconductor

САЩ IA186, IA188, IA80C186, IA80C188

18. Интеграл СССР/Беларус К1834ВМ86, К1834ВМ88, К1835ВМ86, К1847ВМ286

19. Intel САЩ8086, 8088, 80C86, 80C88, 80186, 80188, 80C186, 80C188, 80286,80C286, 80386, 80486, RapidCAD, P5, P6, P68, Xeon, Core, Core 2, Corei3, Core i5, Core i7, Atom, EP80579

20. Intersil САЩ 80C86, 80C88, 80C186, 80C286

21.KombinatMikroelektronikErfurt

ГДР U80601

22. КванторСССР/Украйна

К1810ВМ86, К1810ВМ88

23. КвазарСССР/Украйна

К1810ВМ86, К1810ВМ88

24. Lantronix САЩ DSTni-EX, DSTni-LX, XChip25. Matsushita Япония MN8086, MN8088

26.Matra Harris semi-conducteurs

Франция 8086, 80C86, 8088, 80C88

27. Mitsubishi Япония M5L8086, M5L8088, Straker

28.NationalSemiconductor

САЩGeode GXm, GXLV , GX1, GX2, GX, LX, NX, SC1100, SC1200, SC1400,SC2200, SC3200

29. NEC Япония

µPD8086, µPD8088, µPD70108, µPD70116, µPD70136, µPD70166,µPD70208, µPD70216, µPD70236, µPD70270, µPD70280, µPD70320,

µPD70322, µPD70325, µPD70327, µPD70330, µPD70332, µPD70335,

µPD70337, µPD70433

30. NexGen САЩ Nx58631. OKI Япония MSM80C86, MSM80C8832. Pixelworks САЩ PW111, PW164

33. RDC Semiconductor ТайванR8610, R8800, R8810, R8820, R8822, R8830, HB301, HB303, R3308,IAD 100

34. Rise САЩ mP6

Page 5: Сборник задачи по програмиране на Асемблер

5Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

35. Rochester Electronics САЩ8086, 8088, 80186, 80188, 80C186, 80C188, Am186, Am188, 80286,80C286, Élan SC300

36. РодонСССР/Украйна

К1810ВМ86

37. Sharp Япония LH70108, LH70116

38. Siemens ФРГSAB8086, SAB8088, SAB80C88, SAB80186, SAB80188, SAB80C186,SAB80C188, SAB80286, SAB80C286

39. SiS Тайван SiS550, SiS551, SiS55240. Sony Япония CXQ70108, CXQ70116

41. Space Electronics Inc. САЩ 80C186, 80386, 80486

42. STMicroelectronicsИталия-Франция

ST486, ST5x86, ST6x86, STPC, STDP5300, STDP6018, STDP6026,STDP6028, STDP6036, STDP6038

43. Tesla Чехословакия MHB8086, MHB808844. Texas Instruments САЩ 48645. UMC Тайван U546. Vadem САЩ VG230, VG330

47. VIA Тайван Cyrix III, C3, C7, Antaur, Eden, CoreFusion, Nano, QuadCore48. VM Technology Япония VM386, VM860, VM860049. ZF Micro Solutions САЩ ZFx8650. Zilog САЩ Z70108, Z70116

Горната таблица включва само полупроводникови прибори, които могат да изпълняват командите пряко (т.е.това е техният собствен набор от команди). Освен тях има и такива, които не могат, но за тях са разработенипрограми за двоична транслация (Transmeta „Crusoe“, Елбрус-2000 и Loongson-3). Съществуват и т.н. „мекипроцесори“ – програми за програмируеми логически матрици (FPGA), които ги превръщат в съответниямикропроцесор, способен да изпълнява тези команди (CPU86, Zet). И на последно място (но не по значение),програмните емулатори на x86 (Bhole, Bochs, DOSBox, EMU8086, JPC, QEMU, RealPC / SoftPC) също могат даизпълняват тези команди .

Микропроцесорите на Intel 80376 и на National Semiconductor NS486 не поддържат реален режим и на тяхпредставените тук програми няма да работят. Ето защо те не са включени в горната таблица.

Решенията на всички задачи без №№49–50 с цел опростяването им и поставяне на ударението върху командите(а не върху асемблера) имат следните ограничения и особености:

Използваните регистри не се съхраняват (освен в задачи №№23–25).Не се използват макроси.Не се използват управляващите конструкции от високо ниво на MASM версия 6 и по-новите от нея (вж. т.14.1.2).От директивите се използват само „equ“ (дефиниция на константи и изрази), „db“ (дефиниция на байтове)и „dw“ (дефиниция на думи).Избран е най-простият 16-битов модел на паметта – „мъничкият“ (на англ. „tiny“). При него всичкисегментни регистри сочат към един и същ сегмент от 64 KB. Това е достатъчно за учебни цели, но ненепременно за реално приложение . Ако се избере „компактен “ или „голям“ модел на паметта с т.н.„далечни“ указатели на данните, състоящи се сегмент и отместване, могат да се адресират данни до 1 MB.Но и тогава ограничението за максимален размер на масивите от 64 KB се запазва. В случай, че и това не едостатъчно, трябва да се използва един от следните режими или модели на паметта:

„Разширен“ („усъвършенстван“) режим, в който се адресират 16 MB. Той се поддържа отмикроконтролерите на дадените с курсив в таблицата фирми-производителки ).„Огромен“ модел, в който указателите се състоят от сегмент и отместване и всяка промяна наотместването се придружава от проверка и евентуална промяна на сегмента.„Нереален“ („вуду“) режим. Това е недокументиран вариант на реалния режим при 32-битовитемикропроцесори и микроконтролери , в който могат да се адресират 4 GB.Защитен режим – от 80286 и неговите аналози (наричани по-долу „286“) нататък.

Page 6: Сборник задачи по програмиране на Асемблер

6Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Само при последните 3 варианта се преодолява ограничението от 64 KB максимален размер на 1 масив, но теизлизат извън рамките на настоящия сборник.

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

1. Am186™ and Am188™ Family InstructionSet Manual2. 80C186EC/80C188EC MicroprocessorUser’s Manual, приложения C и D3. Дейвид Брадли, „Програмиране на Асемблер за персонален компютър IBM PC“, Държавно издателство

„Техника “, София, 1989 г., стр. 1–103

Авторът изказва своята благодарност на следните автори:

Eric Isaacson – за асемблера „A386“ и за първоначалната реализация на зад. №№42–43Henry Warren и Donald Knuth – за алгоритъма на зад. №50Ingo Wegener – за алгоритъма на зад. №34Jack Crenshaw – за алгоритъма на зад. №46Камен Фильов, Иван Зарков и Николай Велчев – за алгоритъма на зад. №47Mark Adler – за алгоритъма на зад. №5Paul Hsieh – за реализацията на зад. №4Peter Norton – за алгоритмите на зад. №№48–49Terje Mathisen – за реализацията на зад. №13Томохико Сакамото – за алгоритъма на зад. №30.

2. Прости изчисления

В тази група са включени задачи с прости аритметични и логически операции: преобразуване в и от код наГрей и изчисляване на най-голям общ делител (НОД) и най-малко общо кратно (НОК).

2.1. Намиране на кода на Грей на двоично число

Зад. 1. Да се напише подпрограма, която да връща в регистър AX кода на Грей на двоичното число в регистърAX.

Решение: Задачата се решава с прост линеен алгоритъм. Пресмята се сумата по модул 2 на изходното число иизместената му с 1 бит надясно стойност:

A386 V4.05 assembly of BIN2GRAY.COM 2011-07-30 11:46Source: BIN2GRAY.A86 Page 1

1 ; върни в AX кода на Грей на двоичното число в AX2 0100 8B D0 mov dx,ax ; unsigned bin2gray(unsigned n)3 0102 D1 EA shr dx,1 ; {4 0104 31 D0 xor ax,dx ; return n ^ (n >> 1);5 0106 C3 ret ; }

2.2. Намиране на двоично число по даден код на Грей

Зад. 2. Да се напише подпрограма, която да връща в регистър AX двоичното число, чийто код на Грей е даден врегистър AX.

Решение: За съжаление задачата не може да се реши с линеен алгоритъм. Необходим е прост цикъл, в който сепресмята сумата по модул 2 на последователно изместваната с 1 бит надясно стойност на изходното число.

Page 7: Сборник задачи по програмиране на Асемблер

7Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of GRAY2BIN.COM 2011-07-30 11:46Source: GRAY2BIN.A86 Page 1

1 ; върни в AX двоичното число, чийто код на Грей е бил в AX2 0100 33 D2 xor dx,dx ; unsigned gray2bin(unsigned n)3 0102 31 C2 l1: xor dx,ax ; {4 0104 D1 E8 shr ax,1 ;\__ for (unsigned b = 0; n; n >>= 1)5 0106 75 FA jnz l1 ; \_ b ^= n;6 0108 92 xchg ax,dx ; return b;7 0109 C3 ret ; }

Заб.: Кодът на Грей има важно свойство: всеки 2 последователни комбинации се различават само с 1 бит. Тойсе използва при нужда от по-добра шумоустойчивост спрямо паразитни импулси, например в т. нар. „кодовиключета“ или шифратори за задаване на входна комбинация на различни устройства в цифровата техника.

2.3. Намиране на най-малко общо кратно (НОК)

Зад. 3. Да се напише подпрограма, която да връща в регистър AX най-малкото общо кратно на естественитечисла в регистрите AX и DX.

Решение: При известен НОД (вж. следващата задача), НОК може да се намери лесно като частно напроизведението на числата и НОД. Подпрограмата за изчисляване на НОД е включена с директивата „include“ исе извиква с командата „call“.

A386 V4.05 assembly of LCM.COM 2011-07-30 11:46Source: LCM.A86 Page 1

1 ; върни в AX най-малкото общо кратно (НОК) на числата в AX и DX2 0100 8B D8 mov bx,ax ; съхрани числото A3 0102 8B CA mov cx,dx ; съхрани числото B4 0104 F7 E2 mul dx ; DX:AX = A * B5 0106 93 xchg ax,bx ; AX = A, BX = младша_дума(A * B)6 0107 87 CA xchg dx,cx ; DX = B, CX = старша_дума(A * B)7 0109 E8 06 00 call gcd ; намери НОД(A, B)8 010C 93 xchg ax,bx ; AX = младша_дума(A * B), BX = НОД9 010D 8B D1 mov dx,cx ; DX = старша_дума(A * B)10 010F F7 F3 div bx ; AX = A * B / НОД(A, B) = НОК11 0111 C3 ret12 gcd:13 include gcd.a8614 i ; върни в AX най-големия общ делител на числата в AX и DX15 0112 F7 D8 i neg ax ; unsigned gcd(unsigned a, unsigned b)16 0114 74 09 i je l3 ; {17 0116 F7 D8 i l1: neg ax ; if (a == 0 && b == 0) return 1;18 0118 92 i xchg ax,dx ;\ if (b == 0) return a;19 0119 29 D0 i l2: sub ax,dx ; \ if (a != 0)20 011B 77 FC i ja l2 ; \_ while (a != b)21 011D 72 F7 i jb l1 ; if (a < b)22 011F 01 D0 i l3: add ax,dx ;\ b -= a;23 0121 75 01 i jne l4 ; \_ else a -= b;24 0123 40 i inc ax ; return b;25 0124 C3 i l4: ret ; }

2.4. Намиране на най-голям общ делител (НОД)

Зад. 4. Да се напише подпрограма, която да връща в регистър AX най-големия общ делител на естественитечисла в регистрите AX и DX.

Решение: Задачата може да се реши с алгоритъма на Евклид – цикъл с разклонение в тялото на цикъла.Алгоритъмът на език C е даден в коментара на втората половина на листинга на предходната задача.

Заб.: Реализацията на Асемблер е на Paul Hsieh. Тя се състои само от 11 команди – по-малко от операциите вреализацията на език C! Обърнете внимание , че са взети всички мерки НОД никога да не стане 0.

Page 8: Сборник задачи по програмиране на Асемблер

8Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

3. Контролни суми на масиви от данни

В тази група са включени задачи, в които се пресмятат контролни суми на масиви от данни („Adler-32“ и„CRC32“) и се генерира таблица на „CRC32“ на 256-те възможни 8-битови числа.

3.1. Намиране на контролна сума на Адлер („Adler-32“)

Зад. 5. Да се напише подпрограма, която да връща в регистри DX:AX контролната сума на Адлер на масива отCX байта, указан от регистър SI.

Решение: Алгоритъмът на език C е даден като коментар в листинга. Той е цикличен с толкова итерации,колкото байта има в буфера. Обърнете внимание , че се събират 8-битови числа, а контролната сума е 32-битова.Затова първо се събират младшите байтове, после старшите и накрая старшата дума. (Може и първо да се нулирастаршият байт на младшата дума и после да се събират първо младшите, а след това старшите думи.)

A386 V4.05 assembly of ADLER32.COM 2011-07-30 11:46Source: ADLER32.A86 Page 1

1 ; върни в DX:AX контролната сума „Adler-32“ на масива от CX байта, указан от SI2 0100 BD F1 FF mov bp,65521 ; uint32_t adler32(uint8_t* buf, size_t len) {3 0103 BB 01 00 mov bx,1 ; uint32_t s1 = 1;4 0106 33 FF xor di,di ; uint32_t s2 = 0;5 0108 AC l1: lodsb ; for (size_t n = 0; n < len; n++) {6 0109 33 D2 xor dx,dx ; s1 = (s1 + buf[n]) % 65521;7 010B 00 C3 add bl,al8 010D 80 D7 00 adc bh,09 0110 83 D2 00 adc dx,010 0113 93 xchg ax,bx11 0114 F7 F5 div bp12 0116 89 D3 mov bx,dx13 0118 33 D2 xor dx,dx ; s2 = (s2 + s1) % 65521;14 011A 03 FB add di,bx15 011C 83 D2 00 adc dx,016 011F 97 xchg ax,di17 0120 F7 F5 div bp18 0122 89 D7 mov di,dx19 0124 E2 E2 loop l1 ; }20 0126 89 FA mov dx,di ; return (s2 << 16) + s1;21 0128 93 xchg ax,bx22 0129 C3 ret ; }

3.2. Намиране на контролен цикличен код с излишък („CRC32“)

Зад. 6. Да се напише подпрограма, която да връща в регистри DX:AX „CRC32“ на масива от CX байта, указанот регистър SI.

Решение: Задачата се различава от тази за кода „Adler-32“ най-вече по това, че използва предварителносъставена таблица за по-бързо пресмятане. Затова и цикълът тук е много по-прост. Подпрограмата за генерацияна таблицата е включена с директивата „include“ и се извиква с командата „call“ преди началото на цикъла.

Page 9: Сборник задачи по програмиране на Асемблер

9Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of CRC32.COM 2011-07-30 11:46Source: CRC32.A86 Page 1

1 ; върни в DX:AX „CRC32“ на масива от CX байта, указан от SI. CRC-таблицата: от DI.2 .186 ; unsigned long crc(unsigned char *buf, int len)3 0100 51 push cx ; {4 0101 57 push di ; static unsigned long crc_table[256];5 0102 E8 29 00 call mkcrctbl; mkcrctbl(crc_table);6 0105 5F pop di7 0106 59 pop cx8 0107 89 CD mov bp,cx9 0109 B9 FF FF mov cx,-1 ; unsigned long c = -1L;10 010C 8B D1 mov dx,cx11 010E 33 C0 xor ax,ax ; for (int n = 0; n < len; n++)12 0110 AC l0: lodsb ; c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);13 0111 8B D8 mov bx,ax14 0113 30 CB xor bl,cl15 0115 88 E9 mov cl,ch16 0117 8A EA mov ch,dl17 0119 88 F2 mov dl,dh18 011B 88 FE mov dh,bh19 011D C1 E3 02 shl bx,220 0120 33 09 xor cx,[bx+di]21 0122 33 51 02 xor dx,[bx+di+2]22 0125 4D dec bp23 0126 75 E8 jnz l024 0128 91 xchg ax,cx25 0129 F7 D0 not ax ; return c ^ 0xffffffffL;26 012B F7 D2 not dx27 012D C3 ret ; }28 mkcrctbl:29 include mkcrctbl.a8630 i ; генерирай таблицата на „CRC-32“ от адреса, указан от DI31 012E FD i std ; void make_crc(unsigned long crc_table[])32 012F 81 C7 FE 03 i add di,4*256-2 ; {33 0133 BB FF 00 i mov bx,255 ; for (int n = 255; n >= 0; n--) {34 0136 B9 08 00 i l1: mov cx,835 0139 89 DA i mov dx,bx ; unsigned long c = (unsigned long)n;36 013B 33 C0 i xor ax,ax37 013D D1 E8 i l2: shr ax,1 ; for (int k = 8; k > 0; k--) {38 013F D1 DA i rcr dx,1 ; if (c & 1)39 0141 73 07 i jnc l3 ; c = 0xedb88320L ^ (c >> 1);40 0143 81 F2 20 83 i xor dx,8320h ; else41 0147 35 B8 ED i xor ax,0EDB8h ; c >>= 1;42 014A E2 F1 i l3: loop l2 ; }43 014C AB i stosw ; crc_table[n] = c;44 014D 92 i xchg ax,dx45 014E AB i stosw46 014F 4B i dec bx47 0150 79 E4 i jns l1 ; }48 0152 FC i cld49 0153 C3 i ret ; }

3.3. Генерация на таблица на контролни циклични кодове с излишък („CRC32“)

Зад. 7. Да се напише подпрограма, която да генерира таблица на „CRC32“ на всичките 256 възможни 8-битовичисла от адреса, указан от регистър DI.

Решение: Алгоритъмът на тази задача (цикличен с разклонение в тялото на цикъла) е даден на език C вкоментара на втората половина на листинга на предходната задача.

4. Използване на таблици от константи

В тази група са включени задачи, в които се използват предварително съставени таблици от константи с целускоряване и опростяване на програмите. Тя включва компресия и декомпресия на звукови отчети по µ- и A-закон.

Page 10: Сборник задачи по програмиране на Асемблер

10Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

4.1. Компресия на звукови отчети по µ-закон

Зад. 8. Да се напише подпрограма, която да връща в регистър AL компресирания по µ-закон звуков отчет, койтое бил подаден в AX.

Решение: Алгоритъмът на език C е даден в листинга на програмата. Той има 2 малки разклонения, сведени до 1в реализацията. Обърнете внимание , че с първите 3 команди без никакви разклонения се получава абсолютнатастойност на входния отчет. Тук таблицата е точно такава, каквато е необходима за командата „xlat“ (256 байта),затова тази удобна команда може да се използва.

A386 V4.05 assembly of MULAW.COM 2011-07-30 11:46Source: MULAW.A86 Page 1

1 ; върни в AL компресирания по μ-закон байт на входния отчет в AX2 .186 ; unsigned char LinearToMuLawSample(short sample)3 0100 99 cwd ; {4 0101 31 D0 xor ax,dx ; int sign = (sample >> 8) & 0x80;5 0103 29 D0 sub ax,dx ; if (sign)6 0105 3D 7B 7F cmp ax,32635;\_ sample = -sample;7 0108 76 03 jbe l1 ;\_ if (sample > 32635)8 010A B8 7B 7F mov ax,32635; sample = 32635;9 010D 05 84 00 l1: add ax, 132; sample = (short)(sample + 0x84);10 0110 89 C1 mov cx,ax ; int exponent = (int)uLawTbl[(sample>>7) & 0xFF];11 0112 C1 E8 07 shr ax,712 0115 BB 2F 01 mov bx,offset uLawTbl13 0118 D7 xlat14 0119 91 xchg ax,cx ; int mantissa = (sample >> (exponent+3)) & 0x0F;15 011A 88 CD mov ch,cl16 011C 80 C1 03 add cl,317 011F D3 E8 shr ax,cl18 0121 24 0F and al,0Fh19 0123 88 EC mov ah,ch ; int cmpByte = ~(sign | (exponent << 4) | mantissa);20 0125 D5 10 aad 16 ; няма да работи на NEC V20/25/30/33/35/40/41/50/51/53/55!21 0127 80 E6 80 and dh,80h22 012A 08 F0 or al,dh23 012C F6 D0 not al ; return (unsigned char)cmpByte;24 012E C3 ret ; }25 012F 00 00 01 01 02 uLawTbl db 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,326 013F 04 04 04 04 04 db 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,427 014F 05 05 05 05 05 db 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,528 015F 05 05 05 05 05 db 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,529 016F 06 06 06 06 06 db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,630 017F 06 06 06 06 06 db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,631 018F 06 06 06 06 06 db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,632 019F 06 06 06 06 06 db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,633 01AF 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,734 01BF 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,735 01CF 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,736 01DF 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,737 01EF 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,738 01FF 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,739 020F 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,740 021F 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7

4.2. Компресия на звукови отчети по A-закон

Зад. 9. Да се напише подпрограма, която да връща в регистър AL компресирания по A-закон звуков отчет, койтое бил подаден в AX.

Решение: Тази задача е същата като предходната , но за европейския, а не за американския стандарт закомпресия на звукови отчети, чийто алгоритъм е малко по-друг. Таблицата тук е само 128 байта, но пък истойността в AL преди „xlat“ е само 7-битова (защо?).

Page 11: Сборник задачи по програмиране на Асемблер

11Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of ALAW.COM 2011-07-30 11:46Source: ALAW.A86 Page 1

1 ; върни в AL компресирания по A-закон байт на входния отчет в AX2 .186 ; unsigned char LinearToALawSample(short sample)3 0100 99 cwd ; {4 0101 31 D0 xor ax,dx ; int cmpByte, sign = (~sample >> 8) & 0x80;5 0103 29 D0 sub ax,dx ; if (!sign)6 0105 3D 7B 7F cmp ax,32635;\_ sample = -sample;7 0108 76 03 jbe l1 ;\_ if (sample > 32635)8 010A B8 7B 7F mov ax,32635; sample = 32635;9 010D 0A E4 l1: or ah,ah ; if (sample >= 256) {10 010F 74 18 jz l211 0111 89 C1 mov cx,ax ; int exponent = (int)aLawTbl[(sample>>8) & 0x7F];12 0113 88 E0 mov al,ah13 0115 BB 35 01 mov bx,offset aLawTbl14 0118 D7 xlat15 0119 91 xchg ax,cx ; int mantissa = (sample >> (exponent+3)) & 0x0F;16 011A 88 CD mov ch,cl17 011C 80 C1 03 add cl,318 011F D3 E8 shr ax,cl19 0121 24 0F and al,0Fh20 0123 88 EC mov ah,ch ; cmpByte = (exponent << 4) | mantissa;21 0125 D5 10 aad 16 ; няма да работи на NEC V20/25/30/33/35/40/41/50/51/53/55!22 0127 EB 03 jmp short l3; } else23 0129 C0 E8 04 l2: shr al,4 ; cmpByte = sample >> 4;24 012C 80 E6 80 l3: and dh,080h ; cmpByte ^= sign ^ 0x55;25 012F 80 F6 D5 xor dh,0D5h26 0132 30 F0 xor al,dh ; return (unsigned char)cmpByte;27 0134 C3 ret ; }28 0135 01 01 02 02 03 aLawTbl db 1,1,2,2,3,3,3,3,4,4,4,4,4,4,4,429 0145 05 05 05 05 05 db 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,530 0155 06 06 06 06 06 db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,631 0165 06 06 06 06 06 db 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,632 0175 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,733 0185 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,734 0195 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,735 01A5 07 07 07 07 07 db 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7

4.3. Декомпресия на звукови отчети по µ-закон

Зад. 10. Да се напише подпрограма, която да връща в регистър AX декомпресираната стойност накомпресирания по µ-закон звуков отчет, който е бил подаден в AL.

Решение: Алгоритъмът е линеен и пределно прост. За съжаление поради 16-битовите данни в таблицатакомандата „xlat“ не може да се използва.

Page 12: Сборник задачи по програмиране на Асемблер

12Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of MULAWDEC.COM 2011-07-30 11:46Source: MULAWDEC.A86 Page 1

1 ; върни в AX декодираната дума на кодирания по μ-закон байт в AL2 0100 93 xchg ax,bx ; BL = кодиран байт3 0101 32 FF xor bh,bh ; изчисти старшия байт4 0103 D1 E3 shl bx,1 ; премини към размер на елемента дума5 0105 8B 87 0A 01 mov ax,[uLawDT+bx] ; получи декодираната стойност и я върни6 0109 C3 ret7 010A 84 82 84 86 84 uLawDT dw -32124,-31100,-30076,-29052,-28028,-27004,-25980,-249568 011A 84 A2 84 A6 84 dw -23932,-22908,-21884,-20860,-19836,-18812,-17788,-167649 012A 84 C1 84 C3 84 dw -15996,-15484,-14972,-14460,-13948,-13436,-12924,-1241210 013A 84 D1 84 D3 84 dw -11900,-11388,-10876,-10364, -9852, -9340, -8828, -831611 014A 04 E1 04 E2 04 dw -7932, -7676, -7420, -7164, -6908, -6652, -6396, -614012 015A 04 E9 04 EA 04 dw -5884, -5628, -5372, -5116, -4860, -4604, -4348, -409213 016A C4 F0 44 F1 C4 dw -3900, -3772, -3644, -3516, -3388, -3260, -3132, -300414 017A C4 F4 44 F5 C4 dw -2876, -2748, -2620, -2492, -2364, -2236, -2108, -198015 018A A4 F8 E4 F8 24 dw -1884, -1820, -1756, -1692, -1628, -1564, -1500, -143616 019A A4 FA E4 FA 24 dw -1372, -1308, -1244, -1180, -1116, -1052, -988, -92417 01AA 94 FC B4 FC D4 dw -876, -844, -812, -780, -748, -716, -684, -65218 01BA 94 FD B4 FD D4 dw -620, -588, -556, -524, -492, -460, -428, -39619 01CA 8C FE 9C FE AC dw -372, -356, -340, -324, -308, -292, -276, -26020 01DA 0C FF 1C FF 2C dw -244, -228, -212, -196, -180, -164, -148, -13221 01EA 88 FF 90 FF 98 dw -120, -112, -104, -96, -88, -80, -72, -6422 01FA C8 FF D0 FF D8 dw -56, -48, -40, -32, -24, -16, -8, -123 020A 7C 7D 7C 79 7C dw 32124, 31100, 30076, 29052, 28028, 27004, 25980, 2495624 021A 7C 5D 7C 59 7C dw 23932, 22908, 21884, 20860, 19836, 18812, 17788, 1676425 022A 7C 3E 7C 3C 7C dw 15996, 15484, 14972, 14460, 13948, 13436, 12924, 1241226 023A 7C 2E 7C 2C 7C dw 11900, 11388, 10876, 10364, 9852, 9340, 8828, 831627 024A FC 1E FC 1D FC dw 7932, 7676, 7420, 7164, 6908, 6652, 6396, 614028 025A FC 16 FC 15 FC dw 5884, 5628, 5372, 5116, 4860, 4604, 4348, 409229 026A 3C 0F BC 0E 3C dw 3900, 3772, 3644, 3516, 3388, 3260, 3132, 300430 027A 3C 0B BC 0A 3C dw 2876, 2748, 2620, 2492, 2364, 2236, 2108, 198031 028A 5C 07 1C 07 DC dw 1884, 1820, 1756, 1692, 1628, 1564, 1500, 143632 029A 5C 05 1C 05 DC dw 1372, 1308, 1244, 1180, 1116, 1052, 988, 92433 02AA 6C 03 4C 03 2C dw 876, 844, 812, 780, 748, 716, 684, 65234 02BA 6C 02 4C 02 2C dw 620, 588, 556, 524, 492, 460, 428, 39635 02CA 74 01 64 01 54 dw 372, 356, 340, 324, 308, 292, 276, 26036 02DA F4 00 E4 00 D4 dw 244, 228, 212, 196, 180, 164, 148, 13237 02EA 78 00 70 00 68 dw 120, 112, 104, 96, 88, 80, 72, 6438 02FA 38 00 30 00 28 dw 56, 48, 40, 32, 24, 16, 8, 0

4.4. Декомпресия на звукови отчети по A-закон

Зад. 11. Да се напише подпрограма, която да връща в регистър AX декомпресираната стойност накомпресирания по A-закон звуков отчет, който е бил подаден в AL.

Решение: Задачата е същата като предходната , но таблицата е с други константи .

Page 13: Сборник задачи по програмиране на Асемблер

13Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of ALAWDEC.COM 2011-07-30 11:46Source: ALAWDEC.A86 Page 1

1 ; върни в AX декодираната дума на кодирания по A-закон байт в AL2 0100 93 xchg ax,bx ; BL = кодиран байт3 0101 32 FF xor bh,bh ; изчисти старшия байт4 0103 D1 E3 shl bx,1 ; премини към размер на елемента дума5 0105 8B 87 0A 01 mov ax,[aLawDT+bx] ; получи декодираната стойност и я върни6 0109 C3 ret7 010A 80 EA 80 EB 80 aLawDT dw -5504, -5248, -6016, -5760, -4480, -4224, -4992, -47368 011A 80 E2 80 E3 80 dw -7552, -7296, -8064, -7808, -6528, -6272, -7040, -67849 012A 40 F5 C0 F5 40 dw -2752, -2624, -3008, -2880, -2240, -2112, -2496, -236810 013A 40 F1 C0 F1 40 dw -3776, -3648, -4032, -3904, -3264, -3136, -3520, -339211 014A 00 AA 00 AE 00 dw -22016,-20992,-24064,-23040,-17920,-16896,-19968,-1894412 015A 00 8A 00 8E 00 dw -30208,-29184,-32256,-31232,-26112,-25088,-28160,-2713613 016A 00 D5 00 D7 00 dw -11008,-10496,-12032,-11520, -8960, -8448, -9984, -947214 017A 00 C5 00 C7 00 dw -15104,-14592,-16128,-15616,-13056,-12544,-14080,-1356815 018A A8 FE B8 FE 88 dw -344, -328, -376, -360, -280, -264, -312, -29616 019A 28 FE 38 FE 08 dw -472, -456, -504, -488, -408, -392, -440, -42417 01AA A8 FF B8 FF 88 dw -88, -72, -120, -104, -24, -8, -56, -4018 01BA 28 FF 38 FF 08 dw -216, -200, -248, -232, -152, -136, -184, -16819 01CA A0 FA E0 FA 20 dw -1376, -1312, -1504, -1440, -1120, -1056, -1248, -118420 01DA A0 F8 E0 F8 20 dw -1888, -1824, -2016, -1952, -1632, -1568, -1760, -169621 01EA 50 FD 70 FD 10 dw -688, -656, -752, -720, -560, -528, -624, -59222 01FA 50 FC 70 FC 10 dw -944, -912, -1008, -976, -816, -784, -880, -84823 020A 80 15 80 14 80 dw 5504, 5248, 6016, 5760, 4480, 4224, 4992, 473624 021A 80 1D 80 1C 80 dw 7552, 7296, 8064, 7808, 6528, 6272, 7040, 678425 022A C0 0A 40 0A C0 dw 2752, 2624, 3008, 2880, 2240, 2112, 2496, 236826 023A C0 0E 40 0E C0 dw 3776, 3648, 4032, 3904, 3264, 3136, 3520, 339227 024A 00 56 00 52 00 dw 22016, 20992, 24064, 23040, 17920, 16896, 19968, 1894428 025A 00 76 00 72 00 dw 30208, 29184, 32256, 31232, 26112, 25088, 28160, 2713629 026A 00 2B 00 29 00 dw 11008, 10496, 12032, 11520, 8960, 8448, 9984, 947230 027A 00 3B 00 39 00 dw 15104, 14592, 16128, 15616, 13056, 12544, 14080, 1356831 028A 58 01 48 01 78 dw 344, 328, 376, 360, 280, 264, 312, 29632 029A D8 01 C8 01 F8 dw 472, 456, 504, 488, 408, 392, 440, 42433 02AA 58 00 48 00 78 dw 88, 72, 120, 104, 24, 8, 56, 4034 02BA D8 00 C8 00 F8 dw 216, 200, 248, 232, 152, 136, 184, 16835 02CA 60 05 20 05 E0 dw 1376, 1312, 1504, 1440, 1120, 1056, 1248, 118436 02DA 60 07 20 07 E0 dw 1888, 1824, 2016, 1952, 1632, 1568, 1760, 169637 02EA B0 02 90 02 F0 dw 688, 656, 752, 720, 560, 528, 624, 59238 02FA B0 03 90 03 F0 dw 944, 912, 1008, 976, 816, 784, 880, 848

5. Манипулация на битове

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

5.1. Огледално обръщане (реверсиране) на битовете

Зад. 12. Да се напише подпрограма, която да връща в регистри DX:AX числото, което е било подадено в същитерегистри, но с огледално обърнати битове.

Решение: Задачата може да се реши с циклично изместване и ротация (как?), но тук е дадено много по-бързорешение – с таблица на огледалните образи на всички възможни 8-битови числа. Таблицата е точно такава,каквато е необходима за командата „xlat“ (256 байта), затова тази удобна команда може да се използва и тук,свеждайки задачата само до 8 команди за прехвърляне на данни. Обърнете внимание на директивата .radix, спомощта на която се избягва необходимостта от добавяне на суфикса „h“ към всяка 1 отделна константа втаблицата.

Page 14: Сборник задачи по програмиране на Асемблер

14Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of BITREV.COM 2011-07-30 11:46Source: BITREV.A86 Page 1

1 ; върни в DX:AX 32-битовото число в DX:AX с всички битове подредени в обратен ред2 0100 BB 0D 01 mov bx,offset bittabl3 0103 D7 xlat ; (досегашните) битове 7-0 стават 0-74 0104 86 C4 xchg ah,al ; битовете 7-0 стават 8-15, а 15-8 – 7-05 0106 D7 xlat ; битовете 15-8 стават 0-76 0107 92 xchg ax,dx ; битовете 7-0 стават 24-31, 15-8 – 16-23, а 31-16 – 15-07 0108 D7 xlat ; битовете 23-16 стават 0-78 0109 86 C4 xchg ah,al ; битовете 23-16 стават 8-15, а 31-24 – 7-09 010B D7 xlat ; битовете 31-24 стават 0-710 010C C3 ret11 .radix 1612 010D 00 80 40 C0 20 bittabl db 000,080,040,0C0,020,0A0,060,0E0,010,090,050,0D0,030,0B0,070,0F013 011D 08 88 48 C8 28 db 008,088,048,0C8,028,0A8,068,0E8,018,098,058,0D8,038,0B8,078,0F814 012D 04 84 44 C4 24 db 004,084,044,0C4,024,0A4,064,0E4,014,094,054,0D4,034,0B4,074,0F415 013D 0C 8C 4C CC 2C db 00C,08C,04C,0CC,02C,0AC,06C,0EC,01C,09C,05C,0DC,03C,0BC,07C,0FC16 014D 02 82 42 C2 22 db 002,082,042,0C2,022,0A2,062,0E2,012,092,052,0D2,032,0B2,072,0F217 015D 0A 8A 4A CA 2A db 00A,08A,04A,0CA,02A,0AA,06A,0EA,01A,09A,05A,0DA,03A,0BA,07A,0FA18 016D 06 86 46 C6 26 db 006,086,046,0C6,026,0A6,066,0E6,016,096,056,0D6,036,0B6,076,0F619 017D 0E 8E 4E CE 2E db 00E,08E,04E,0CE,02E,0AE,06E,0EE,01E,09E,05E,0DE,03E,0BE,07E,0FE20 018D 01 81 41 C1 21 db 001,081,041,0C1,021,0A1,061,0E1,011,091,051,0D1,031,0B1,071,0F121 019D 09 89 49 C9 29 db 009,089,049,0C9,029,0A9,069,0E9,019,099,059,0D9,039,0B9,079,0F922 01AD 05 85 45 C5 25 db 005,085,045,0C5,025,0A5,065,0E5,015,095,055,0D5,035,0B5,075,0F523 01BD 0D 8D 4D CD 2D db 00D,08D,04D,0CD,02D,0AD,06D,0ED,01D,09D,05D,0DD,03D,0BD,07D,0FD24 01CD 03 83 43 C3 23 db 003,083,043,0C3,023,0A3,063,0E3,013,093,053,0D3,033,0B3,073,0F325 01DD 0B 8B 4B CB 2B db 00B,08B,04B,0CB,02B,0AB,06B,0EB,01B,09B,05B,0DB,03B,0BB,07B,0FB26 01ED 07 87 47 C7 27 db 007,087,047,0C7,027,0A7,067,0E7,017,097,057,0D7,037,0B7,077,0F727 01FD 0F 8F 4F CF 2F db 00F,08F,04F,0CF,02F,0AF,06F,0EF,01F,09F,05F,0DF,03F,0BF,07F,0FF

5.2. Транспониране на квадратна матрица от битове

Зад. 13. Да се напише подпрограма, която да транспонира матрицата от 16 × 16 бита, указана от DS:SI,записвайки новата матрица по ES:DI.

Решение: Алгоритъмът е от типа „цикъл в цикъл“ (външният цикъл е от етикет „L1“, а вътрешният – от „L2“).Реализацията е на Terje Mathisen. В края на програмата изходната матрица се възстановява. Обърнете вниманиена използването на базово-индексна адресация с отместване (най-сложната възможна адресация) в командата„rol“.

A386 V4.05 assembly of TRANSP.COM 2011-07-30 11:46Source: TRANSP.A86 Page 1

1 ; транспонирай матрицата от 16 × 16 бита, указана от DS:SI, запиши новата по ES:DI2 0100 B9 10 00 mov cx,16 ; брояч на редовете на новата матрица3 0103 BB E0 FF l1: mov bx,-32 ; индекс на реда на изходната матрица4 0106 D1 40 20 l2: rol word ptr [si+bx+32],1;CF = текущ бит от следв. ред на изх. матр.5 0109 13 C0 adc ax,ax ; измести реда на новата матрица и добави този бит6 010B 83 C3 02 add bx,2 ; обнови индекса на реда на изходната матрица7 010E 75 F6 jnz l2 ; още редове? Прехвърли бита от следващия ред8 0110 AB stosw ; обнови текущия ред на новата матрица9 0111 E2 F0 loop l1 ; повтори външния цикъл за всички редове10 0113 C3 ret

6. Обработка на символни низове

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

6.1. Въвеждане на ASCIIZ-низ от клавиатурата

Зад. 14. Да се напише подпрограма, която да въвежда от клавиатурата ASCIIZ-низ в буфер, указан от ES:DI.

Page 15: Сборник задачи по програмиране на Асемблер

15Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Решение: Алгоритъмът е от типа „цикъл с разклонение в тялото на цикъла“. Има 2 еднакво лесни за използваненачина за въвеждане на 1 символ: BIOS (INT 16h, AH=0) и ДОС (INT 21h, AH=1). Тук е избран вторият.Въвеждането завършва с натискане на клавиша „Return“, чийто код се замества с нула. Това е и смисълът насъкращението „ASCIIZ“ („ASCII-Zero“). Такъв низ може да се обработва с функциите за обработка на низове встандартната библиотека на език C.

Заб.: В тази функция е възможно препълване на буфера за съхранение на низа – точно както при библиотечнатафункцията на C „gets()“. Това може да се избегне по два начина:

Използване на функция 0Ah на ДОС за въвеждане на низ. Тя „знае“ размера на буфера и прекратявавъвеждането при напълване. Но низът не е ASCIIZ и е ограничен до 127 B.Изменение на представената по-долу реализация, като се отчита размерът на буфера, който да се предавакато аргумент (как?); може дори да се спести 1 команда и 1 етикет (как?).

A386 V4.05 assembly of GETSTR.COM 2011-07-30 11:46Source: GETSTR.A86 Page 1

1 ; въведи от конзолата ASCIIZ-низ в буфер, указан от ES:DI2 0100 B4 01 mov ah,1 ; функция „въведи символ“3 0102 CD 21 g1: int 21h ; получи следващия символ4 0104 3C 0D cmp al,13 ; CR?5 0106 74 03 jz g2 ; да, изход6 0108 AA stosb ; не, съхрани го7 0109 EB F7 jmp g1 ; и продължи8 010B C6 05 00 g2: mov byte ptr [di],09 010E C3 ret

6.2. Извеждане на ASCIIZ-низ на екрана

Зад. 15. Да се напише подпрограма, която да извежда на екрана ASCIIZ-низ, указан от DS:SI.

Решение: И този алгоритъм е от типа „цикъл с разклонение в тялото на цикъла“. Има няколко начина заизвеждане на 1 символ, от които тук е избрано недокументираното прекъсване на ДОС за вътрешна употребаINT 29h. Това прекъсване от своя страна извиква прекъсване 10h на BIOS с AH=0Eh и ASCII-кода на символа вAL (т.н. „телетайпен изход“).

Заб.: Неизвестно защо, в някои варианти на ДОС версия 3.30 в изходния текст на програмата за обслужване наINT 29h (файл „MSBIO1.ASM“, процедура „OUTCHR “) не се съхранява и възстановява регистър BX. Порадитова след изпълнение на INT 29h той има стойност 7, а старото му съдържание се губи. Това трябва да се имапредвид при използване на INT 29h.

A386 V4.05 assembly of PUTSTR.COM 2011-07-30 11:46Source: PUTSTR.A86 Page 1

1 ; изведи на екрана ASCIIZ-низа, указан от DS:SI2 0100 AC p1: lodsb ; получи следващия символ3 0101 0A C0 or al,al ; NUL?4 0103 74 04 jz p2 ; да, изход5 0105 CD 29 int 29h ; не, изведи го6 0107 EB F7 jmp p1 ; и продължи7 0109 C3 p2: ret

6.3. Намиране на дължината на ASCIIZ-низ

Зад. 16. Да се напише подпрограма, която да връща в регистър CX дължината на ASCIIZ-низ, указан от DS:SI.

Решение: Задачата се решава с прост линеен алгоритъм, използвайки командата „scasb“ за търсене на символ вниз.

Page 16: Сборник задачи по програмиране на Асемблер

16Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of STRLEN.COM 2011-07-30 11:46Source: STRLEN.A86 Page 1

1 ; върни в CX дължината на ASCIIZ-низ, указан от ES:DI2 0100 B9 FF FF mov cx,-1 ; инициализирай брояча3 0103 B0 00 mov al,0 ; NUL-терминатор4 0105 F2 AE repne scasb ; намери го, броейки обратно символите5 0107 F7 D1 not cx ; изключи го от броя им и смени знака на броя6 0109 49 dec cx ; компенсирай първоначалната му стойност от -17 010A C3 ret

6.4. Намиране на броя на думите в ASCIIZ-низ

Зад. 17. Да се напише подпрограма, която да връща в регистър AX броя на думите в ASCIIZ-низ, указан отDS:SI.

Решение: Алгоритъмът (даден на език C в коментара на листинга) е цикъл с разклонение в тялото на цикъла.Използва се фактът, че ASCII-кодът (32) на символа „интервал“ (' ') е по-голям от ASCII-кодовете (9–13) наостаналите символи-разделители ('\t', '\n', '\v', '\f' и '\r'). Преброяване се извършва, само ако предходният символ ебил разделител, а текущият не е.

A386 V4.05 assembly of WORDS1.COM 2011-07-30 11:46Source: WORDS1.A86 Page 1

1 ; върни в AX броя на думите в низа, указан от DS:SI2 0100 B4 20 mov ah,' ' ; unsigned words(char *src)3 0102 33 DB xor bx,bx ; {4 0104 AC l1: lodsb ; \ char c, old = ' '; // AL = c, AH = old5 0105 3C 00 cmp al,0 ; \_ unsigned count = 0; // BX6 0107 74 0E jz l3 ; while ((c = *src++) != '\0') {7 0109 3C 20 cmp al,' ' ; if (!isspace(c) && isspace(old))8 010B 76 06 jbe l29 010D 80 FC 20 cmp ah,' '10 0110 77 01 ja l211 0112 43 inc bx ; count++;12 0113 88 C4 l2: mov ah,al ; old = c;13 0115 EB ED jmp l1 ; }14 0117 93 l3: xchg ax,bx ; return count;15 0118 C3 ret ; }

6.5. Намиране на броя на думите в ASCIIZ-низ (ускорено)

Зад. 18. Да се намери по-бързо решение на предходната задача.

Решение: По-голямо бързодействие може да се постигне чрез премахване на променливата за предходниясимвол („old“) и усложняване на алгоритъма до 2 вътрешни цикъла с разклонения в телата, вложени в 1 външенцикъл. Така за всеки символ вече е необходимо да се правят не 2 проверки, а само 1.

A386 V4.05 assembly of WORDS2.COM 2011-07-30 11:46Source: WORDS2.A86 Page 1

1 ; върни в AX броя на думите в низа, указан от DS:SI2 0100 33 DB xor bx,bx ; unsigned words(char *src) {3 0102 AC l1: lodsb ;\_ unsigned count = 0;4 0103 3C 00 cmp al,0 ; char c;5 0105 74 10 jz l3 ;\ for ( ; ; ) {6 0107 3C 20 cmp al,' ' ; \ do7 0109 76 F7 jbe l1 ;\ \_ if ((c = *src++) == '\0')8 010B 43 inc bx ; \ return count;9 010C AC l2: lodsb ;\ \_ while (isspace(c));10 010D 3C 00 cmp al,0 ;\\_ count++;11 010F 74 06 jz l3 ; \ do12 0111 3C 20 cmp al,' ' ; \_ if ((c = *src++) == '\0')13 0113 77 F7 ja l2 ;\ return count;14 0115 EB EB jmp l1 ; \_ while (!isspace(c));15 0117 93 l3: xchg ax,bx ; }16 0118 C3 ret ; }

Page 17: Сборник задачи по програмиране на Асемблер

17Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

6.6. Преобразуване на число в ASCIIZ-низ от римски цифри

Зад. 19. Да се напише подпрограма, която да запълва буфера, указан от DS:SI, с ASCIIZ-низ от римски цифри,чиято числена стойност е равна на числото в регистър AX.

Решение: Използват се 2 масива: от указатели към низове – римски числа („roman“) и от числените им сойности(„decimal“). Така алгоритъмът се свежда до 2 вложени 1 в друг цикъла. Но в коментара, който е на C++, е даденаоперация от високо ниво – конкатенация на низове. Реализацията ѝ на ниско ниво предполага въвеждане наоще 1 цикъл.

Заб.: За получаване на верен резултат числото в AX трябва да е в интервала от 1 до 3999. При AX=0 и DL≠0няма да се получи ASCIIZ-низ, но това лесно може да се поправи (как?).

A386 V4.05 assembly of ROMAN.COM 2011-07-30 11:46Source: ROMAN.A86 Page 1

1 ; запълни буфера, указан от ES:DI, с низ от римски цифри със стойност AX (1-3999)2 0100 92 xchg ax,dx ; DX = romanvalue3 0101 B9 0D 00 mov cx,13 ; for (i = 0; i < 13; i++) {4 0104 33 DB xor bx,bx5 0106 3B 97 43 01 l1: cmp dx,[decimal+bx] ; while (input >= decimal) {6 010A 72 10 jb l37 010C 2B 97 43 01 sub dx,[decimal+bx] ; input -= decimal[i];8 0110 8B B7 5D 01 mov si,[roman+bx] ; romanvalue += roman[i];9 0114 AC l2: lodsb ; получи следващия символ10 0115 0A C0 or al,al ; терминатор ('\0')?11 0117 74 ED jz l1 ; да, продължи12 0119 AA stosb ; не, запиши символа13 011A EB F8 jmp l2 ; }14 011C 83 C3 02 l3: add bx,215 011F E2 E5 loop l1 ; }16 0121 AA stosb ; терминирай така получения низ със символ '\0'17 0122 C3 ret18 0123 4D 00 _1000 db 'M',019 0125 43 4D 00 _900 db 'CM',020 0128 44 00 _500 db 'D',021 012A 43 44 00 _400 db 'CD',022 012D 43 00 _100 db 'C',023 012F 58 43 00 _90 db 'XC',024 0132 4C 00 _50 db 'L',025 0134 58 4C 00 _40 db 'XL',026 0137 58 00 _10 db 'X',027 0139 49 58 00 _9 db 'IX',028 013C 56 00 _5 db 'V',029 013E 49 56 00 _4 db 'IV',030 0141 49 00 _1 db 'I',031 0143 E8 03 84 03 F4 decimal dw 1000,900,500,400,100,90,50,40,10,9,5,4,132 015D 23 01 25 01 28 roman dw offset _1000,offset _900,offset _500,offset _40033 0165 2D 01 2F 01 32 dw offset _100, offset _90, offset _50, offset _4034 016D 37 01 39 01 3C dw offset _10, offset _9, offset _5, offset _435 0175 41 01 dw offset _1

6.7. Проверка на парола във вид на ASCIIZ-низ

Зад. 20. Да се напише подпрограма, която да нулира флага за пренос, ако ASCIIZ-низът, указан от DS:DX,съдържа поне AX символа, от които поне по AX/8 цифри, малки букви, главни букви и препинателни знаци. Впротивен случай да установява флага за пренос в лог. 1.

Решение: Използват се 2 предварително подготвени низа („punct“ и „alnum“) от двойки символи. Това сасъответно първият и последният символ в четирите групи препинателни знаци в ASCII-таблицата и в групитена цифрите, главните букви и малките букви. По този начин, след като се провери общата дължина на низа(цикъл „L1“), има възможност броят на всички групи символи да се провери в 1 общ цикъл („L2“). Този цикъл евъншен за вътрешния цикъл („L3“) с по 1 итерация на символ. И външният, и вътрешният цикъл съдържатразклонения („L4“ и „L5“). При първото („L4“) се прескача преброяването, ако символът не е в границите насъответната група. При второто („L5“) се прескача проверката и нулирането на брояча CX за препинателнитезнаци, на които за разлика от цифрите и буквите се смята общият брой в четирите групи знаци.

Заб. 1: Символът „$“ означава текущата стойност на адресния брояч (в случая 14Ch).

Page 18: Сборник задачи по програмиране на Асемблер

18Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Заб. 2: Ако подпрограмата се извиква от програма на език от високо ниво, може да се добавинедокументираната команда „salc“ (0D6h), която установява регистър AL в 0, ако CF=0, и в −1, ако CF=1, без дапроменя флаговете. Тази команда я има още в микропроцесорите от първото поколение (8086), но не и вмикропроцесорите и микроконтролерите на NEC и аналозите им (вж. т. 1).

A386 V4.05 assembly of PASSWORD.COM 2011-07-30 11:46Source: PASSWORD.A86 Page 1

1 ; върни CF=0, ако ASCIZ-низът, указан от DS:DX, е с дължина поне AX символа2 ; и съдържа поне AX/8 цифри, малки букви, главни букви и препинателни знаци.3 ; Иначе върни CF=1.4 .186 ; позволи непосредствен брой измествания > 15 0100 93 xchg ax,bx ; получи броя в BX6 0101 33 C9 xor cx,cx ; изчисти брояча7 0103 89 D6 mov si,dx ; указател към низа8 0105 AC l1: lodsb ; получи следващия символ9 0106 3C 00 cmp al,0 ; край на низа?10 0108 E0 FB loopne l1 ; не, върти се в цикъл, докато не свърши низът11 010A F7 D1 not cx ; да, получи дължината на низа12 010C 39 D9 cmp cx,bx ; по-къс ли е от необходимото?13 010E 72 2D jb l6 ; да, върни CF=114 0110 C1 EB 03 shr bx,3 ; не, получи нужния брой символи от всяка група15 0113 BD 3E 01 mov bp,offset punct ; зареди указател към таблицата на групите символи16 0116 33 C9 xor cx,cx ; изчисти брояча17 0118 89 D6 l2: mov si,dx ; указател към низа18 011A 8B FD l3: mov di,bp ; указател към групата символи19 011C AC lodsb ; получи следващия символ20 011D AE scasb ; сравни с долната граница21 011E 72 04 jb l4 ; ако е под нея, не го брой22 0120 AE scasb ; сравни с горната граница23 0121 77 01 ja l4 ; ако е над нея, не го брой24 0123 41 inc cx ; в рамките на границите - преброй го25 0124 3C 00 l4: cmp al,0 ; край на низа?26 0126 75 F2 jne l3 ; не, продължи със следващия символ27 0128 83 C5 02 add bp,2 ; да, указател към следващата група символи28 012B 81 FD 46 01 cmp bp,offset alnum ; да сравня ли натрупания брой?29 012F 72 06 jb l5 ; не, продължи30 0131 39 D9 cmp cx,bx ; да, под необходимия брой?31 0133 72 08 jb l6 ; да, върни CF=132 0135 33 C9 xor cx,cx ; не, изчисти брояча33 0137 81 FD 4C 01 l5: cmp bp,offset t_end ; край на таблицата на групите от символи?34 013B 72 DB jb l2 ; не, продължи със следващата група35 013D C3 l6: ret ; да, изход36 013E 20 2F 3A 40 5B punct db ' /:@[`{~' ; препинателни знаци (долна/горна граница ∀ група)37 0146 30 39 41 5A 61 alnum db '09AZaz' ; цифри, главни букви и малки букви38 = : 014C t_end equ $ ; край на таблицата на групите от символи

6.8. Търсене на ASCIIZ-подниз в ASCIIZ-низ

Зад. 21. Да се напише подпрограма, която да връща в регистър AX указател към първия открит подниз, указанот SI, в низа, указан от DI, или −1, ако не е открит.

Решение: Решението е по алгоритъма на функцията „strstr()“ в библиотеката на „Borland C++“ – цикличен сразклонение в тялото на цикъла. Първо се намират дължините на низа и подниза, а след това в низа се търсиподнизът. Ако е намерена само част от него, започва ново търсене от следващия символ в низа. Поднизът можеда се среща в низа и повече от 1 път, но търсенето спира след първото му срещане.

Заб.: Тази функция има 2 различия от функцията „strstr()“ на стандартната библиотека на C:

Ако поднизът не е открит в низа, връща −1, а не 0. Причината е, че е възможно низът да започва и наотместване 0 от началото на сегмента, но не може да продължава до отместване 65535 (−1) – там може дабъде само терминиращият нулев байт.Ако поднизът има нулева дължина, връща указател към терминатора в края на низа, а не към началото му(как може да се промени това?).

Page 19: Сборник задачи по програмиране на Асемблер

19Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of STRSTR.COM 2011-07-30 11:46Source: STRSTR.A86 Page 1

1 ; претърси низа, указан от DI, за първото срещане на подниза, указан от SI. Върни2 ; в AX указател към него (ptr) или -1, ако не е открит (не NULL като strstr(3))3 0100 89 FB mov bx,di ; BX = указател към низа4 0102 33 C0 xor ax,ax ; терминатор на низа ('\0')5 0104 B9 FF FF mov cx,-16 0107 F2 AE repnz scasb ; намери го, CX = -L - 27 0109 F7 D1 not cx ; CX = -(-L - 2) - 1 = L + 2 - 1 = L + 18 010B 8B D1 mov dx,cx ; DX = дължина на низа (L) + 19 010D 89 F7 mov di,si10 010F 8B EF mov bp,di ; BP = DI = указател към подниза11 0111 B9 FF FF mov cx,-112 0114 F2 AE repnz scasb ; намери дължината на подниза по същия начин13 0116 41 inc cx ; CX = -Ls - 114 0117 F7 D1 not cx ; CX = -(-Ls) = дължина на подниза (Ls, а не Ls + 1!)15 0119 8B F5 s1: mov si,bp ; SI = указател към подниза16 011B AC lodsb ; AL = първи символ на подниза, SI = указател към втория17 011C 89 DF mov di,bx ; DI = BX = мястото в низа, откъдето започва търсенето18 011E 33 DB xor bx,bx ; BX = 0 (ако поднизът не е открит, тази 0 ще стане -1)19 0120 87 D1 xchg cx,dx ; CX = дължина на низа (L) + 1, DX = дължина на подниза Ls20 0122 F2 AE repne scasb ; търси в низа първия символ от подниза21 0124 75 12 jne s2 ; ако не е открит, върни -122 0126 39 D1 cmp cx,dx ; остават ли поне Ls (дължина на подниза) символа?23 0128 72 0E jb s2 ; ако не, върни -124 012A 89 FB mov bx,di ; BX = DI = указ. (ptr) към подниза в низа (ако е там) + 125 012C 87 D1 xchg cx,dx ; CX = дължина на подниза Ls, DX = оставащи символи в низа26 012E E3 08 jcxz s2 ; ако поднизът има нулева дължина (Ls = 0), изход27 0130 89 C8 mov ax,cx ; AX = CX = дължина на подниза (Ls)28 0132 49 dec cx ; отчети вече обработения първи символ на подниза29 0133 F3 A6 repe cmpsb ; сравни останаващите части от низовете30 0135 91 xchg ax,cx ; CX = дължина на подниза (Ls)31 0136 75 E1 jne s1 ; ако не е открит, опитай пак от следващата позиция в низа32 0138 8D 47 FF s2: lea ax,[bx-1];тъй като BX = ptr + 1, AX = ptr + 1 - 1 = ptr33 013B C3 ret ; върни ptr (или -1, невъзможен ptr, ако не е открит)

6.9. Търсене на ASCIIZ-подниз в ASCIIZ-низ (без разлика на малки/главни букви)

Зад. 22. Да се реши предходната задача, като при търсенето не се прави разлика между малки и главни букви.

Решение: За съжаление команди за работа с низове без отчитане на регистъра на буквите няма. Затова в товарешение, във всичко друго идентично с решението на предходната задача, командите „scasb“ и „scasb“ сазаменени с цикли („s2“ и „s3“). Бит 5 и на двата сравнявани символа се установява в лог. 1. Така евентуалнитеглавни букви се превръщат в малки и след това се сравняват. Алтернативно е възможно бит 5 да се нулира(как?), преобразувайки евентуалните малки букви в главни преди сравнението.

Заб.: Този начин на смяна на регистъра на буквите е валиден само за основните ASCII-символи, в които бит7=0. При разширените ASCII-кодове , където бит 7=1 (кирилица , гръцки букви, ANSI, катакана и др.) това невинаги е така – зависи от използваната кодова таблица.

Page 20: Сборник задачи по програмиране на Асемблер

20Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of STRISTR.COM 2011-07-30 11:46Source: STRISTR.A86 Page 1

1 ; претърси без разл. малки-главни букви низа [DI] за I-то срещане на подниза [SI].2 ; Върни в AX указ.към него (ptr), а не е ли открит,-1 (не NULL като strcasestr(3))3 0100 89 FB mov bx,di ; BX = указател към низа4 0102 33 C0 xor ax,ax ; терминатор на низа ('\0')5 0104 B9 FF FF mov cx,-16 0107 F2 AE repnz scasb ; намери го, CX = -L - 27 0109 F7 D1 not cx ; CX = -(-L - 2) - 1 = L + 2 - 1 = L + 18 010B 8B D1 mov dx,cx ; DX = дължина на низа (L) + 19 010D 89 F7 mov di,si10 010F 8B EF mov bp,di ; BP = DI = указател към подниза11 0111 B9 FF FF mov cx,-112 0114 F2 AE repnz scasb ; намери дължината на подниза по същия начин13 0116 41 inc cx ; CX = -Ls - 114 0117 F7 D1 not cx ; CX = -(-Ls) = дължина на подниза (Ls, а не Ls + 1!)15 0119 8B F5 s1: mov si,bp ; SI = указател към подниза16 011B AC lodsb ; AL = първи символ на подниза, SI = указател към втория17 011C 89 DF mov di,bx ; DI = BX = мястото в низа, откъдето започва търсенето18 011E 33 DB xor bx,bx ; BX = 0 (ако поднизът не е открит, тази 0 ще стане -1)19 0120 87 D1 xchg cx,dx ; CX = дължина на низа (L) + 1, DX = дължина на подниза Ls20 0122 8A 25 s2: mov ah,[di] ; търси в низа първия символ от подниза21 0124 47 inc di ; обнови указателя (REPNE SCASB без отчитане на регистъра)22 0125 0D 20 20 or ax,10000000100000b ; сравни AL с AH без разлика малки/главни букви23 0128 3A C4 cmp al,ah ; открит ли е символът?24 012A E0 F6 loopne s2 ; не, продължи25 012C 39 D1 cmp cx,dx ; да, остават ли поне Ls (дължина на подниза) символа?26 012E 72 1B jb s4 ; ако не, върни -127 0130 89 FB mov bx,di ; BX = DI = указ. (ptr) към подниза в низа (ако е там) + 128 0132 87 D1 xchg cx,dx ; CX = дължина на подниза Ls, DX = оставащи символи в низа29 0134 E3 15 jcxz s4 ; ако поднизът има нулева дължина (Ls = 0), изход30 0136 89 C8 mov ax,cx ; AX = CX = дължина на подниза (Ls)31 0138 49 dec cx ; отчети вече обработения първи символ на подниза32 0139 E3 10 jcxz s4 ; ако няма повече символи в подниза, изход33 013B 50 push ax ; иначе съхрани акумулатора34 013C AC s3: lodsb ; и сравни оставащите части от низовете35 013D 8A 25 mov ah,[di] ; (REPE CMPSB без отчитане на регистъра на буквите)36 013F 47 inc di ; обнови указателя37 0140 0D 20 20 or ax,10000000100000b38 0143 3A C4 cmp al,ah39 0145 E1 F5 loope s340 0147 58 pop ax ; възстанови акумулатора41 0148 91 xchg ax,cx ; CX = дължина на подниза (Ls)42 0149 75 CE jne s1 ; ако не е открит, опитай пак от следващата позиция в низа43 014B 8D 47 FF s4: lea ax,[bx-1];тъй като BX = ptr + 1, AX = ptr + 1 - 1 = ptr44 014E C3 ret ; върни ptr (или -1, невъзможен ptr, ако не е открит)

6.10. Огледално обръщане на низ

Вж. зад. №43 (в т. 11.6).

7. Низове от пакетирани двоично-десетични числа

В тази група са включени задачи за събиране, изваждане, сравнение (емулация на съответните команди на NEC), въвеждане и извеждане на низове от двоично-десетични числа в код 8–4–2–1. Те намират приложение вточните целочислени изчисления (например в калкулаторите ), тъй като преобразуването им в и от символнинизове е много лесно.

Заб.: Действителното поведение на микропроцесорите и микроконтролерите на NEC при изпълнение накомандите ADD4S, SUB4S и CMP4S може да се различава от действието на представените в тт. 7.1–7.3емулиращи ги подпрограми. Първите 2 от тях работят само в „мъничък“ (англ. „tiny“) модел на паметта – вж. т.1.

7.1. Емулация на командата ADD4S на NEC V20/25/30/33/35/40/41/50/51/53/55

Зад. 23. Да се напише подпрограма, която да емулира действието на командата ADD4S (събиране на 2 низа отCL пакетирани двоично-десетични цифри, указани от DS:SI и ES:DI, със запис на резултата във втория низ).

Page 21: Сборник задачи по програмиране на Асемблер

21Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Решение: Алгоритъмът е от типа „цикъл с разклонение в тялото на цикъла“. Тъй като се емулира команда,трябва да се съхранят и възстановят използваните регистри. Основна трудност тук е правилното установяванена флага ZF. Той трябва да се нулира, дори и ако само 1 бит от резултата е ненулев .

Заб.: Както и в оригиналната команда ADD4S, при нечетен брой двоично-десетични цифри броят им сезакръгля нагоре до четно число и се приема, че пред най-старшата има нула.

A386 V4.05 assembly of ADD4S.COM 2011-07-30 11:46Source: ADD4S.A86 Page 1

1 ; емулирай команда ADD4S на микропроцесорите NEC V20/25/30/33/35/40/41/50/51/53/552 0100 50 push ax ; съхрани използваните регистри3 0101 53 push bx4 0102 51 push cx5 0103 56 push si6 0104 57 push di7 0105 B5 00 mov ch,0 ; брои се само CL8 0107 41 inc cx ; закръгли нагоре до следващия четен брой9 0108 D1 E9 shr cx,1 ; превърни броя тетради в брой байтове10 010A 32 DB xor bl,bl ; приеми, че резултатът е нула и изчисти CF11 010C AC a0: lodsb ; зареди байт от низ №112 010D 12 05 adc al,[di] ; събери байт от низ №213 010F 27 daa ; направи десетична корекция14 0110 9F lahf ; съхрани флаговете15 0111 08 C3 or bl,al ; установи BL, ако AL е ненулев16 0113 74 03 jz a1 ; все още нула?17 0115 80 E4 BF and ah,10111111b ; ако не, нулирай ZF18 0118 9E a1: sahf ; възстанови флаговете19 0119 AA stosb ; съхрани байта на резултата в низ №220 011A E2 F0 loop a0 ; повтаряй до края на низовете21 011C 5F pop di ; възстанови регистрите22 011D 5E pop si23 011E 59 pop cx24 011F 5B pop bx25 0120 58 pop ax26 0121 C3 ret ; изход

7.2. Емулация на командата SUB4S на NEC V20/25/30/33/35/40/41/50/51/53/55

Зад. 24. Да се напише подпрограма, която да емулира действието на командата SUB4S (изваждане на 2 низа отCL пакетирани двоично-десетични цифри, указани от DS:SI и ES:DI, като първият низ се изважда от втория, съсзапис на резултата във втория низ).

Решение: Решението е аналогично на предходната задача с изключение на това, че се налага командата „lodsb“да се замени с „mov“ и „inc si“, тъй като от втория низ се изважда първият, а не обратното.

Page 22: Сборник задачи по програмиране на Асемблер

22Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of SUB4S.COM 2011-07-30 11:46Source: SUB4S.A86 Page 1

1 ; емулирай команда SUB4S на микропроцесорите NEC V20/25/30/33/35/40/41/50/51/53/552 0100 50 push ax ; съхрани използваните регистри3 0101 53 push bx4 0102 51 push cx5 0103 56 push si6 0104 57 push di7 0105 B5 00 mov ch,0 ; брои се само CL8 0107 41 inc cx ; закръгли нагоре до следващия четен брой9 0108 D1 E9 shr cx,1 ; превърни броя тетради в брой байтове10 010A 32 DB xor bl,bl ; приеми, че резултатът е нула и изчисти CF11 010C 8A 05 s1: mov al,[di] ; зареди байт от низ №212 010E 1A 04 sbb al,[si] ; извади байт от низ №113 0110 2F das ; направи десетична корекция14 0111 9F lahf ; съхрани флаговете15 0112 46 inc si ; обнови указателя16 0113 08 C3 or bl,al ; установи BL, ако AL е ненулев17 0115 74 03 jz s2 ; все още нула?18 0117 80 E4 BF and ah,10111111b ; ако не, нулирай ZF19 011A 9E s2: sahf ; възстанови флаговете20 011B AA stosb ; съхрани байта на резултата в низ №221 011C E2 EE loop s1 ; повтаряй до края на низовете22 011E 5F pop di ; възстанови регистрите23 011F 5E pop si24 0120 59 pop cx25 0121 5B pop bx26 0122 58 pop ax27 0123 C3 ret ; изход

7.3. Емулация на командата CMP4S на NEC V20/25/30/33/35/40/41/50/51/53/55

Зад. 25. Да се напише подпрограма, която да емулира действието на командата CMP4S (сравнение на 2 низа отCL пакетирани двоично-десетични цифри, указани от DS:SI и ES:DI, като първият низ се изважда от втория).

Решение: За разлика от предходните 2 задачи, тук може да се използва линеен алгоритъм и командата „repecmpsb“ за извършване на самото сравнение. Налага се само да се разменят двата индексни регистъра, за да можеда се извади първият низ от втория (а не вторият от първия, както прави „cmpsb“), както и да се обърне посокатана сравнението – от старшите към младшите цифри.

A386 V4.05 assembly of CMP4S.COM 2011-07-30 11:46Source: CMP4S.A86 Page 1

1 ; емулирай команда CMP4S на микропроцесорите NEC V20/25/30/33/35/40/41/50/51/53/552 0100 50 push ax ; съхрани използваните регистри3 0101 53 push bx4 0102 51 push cx5 0103 56 push si6 0104 57 push di7 0105 B5 00 mov ch,0 ; брои се само CL8 0107 41 inc cx ; закръгли нагоре до следващия четен брой9 0108 D1 E9 shr cx,1 ; превърни броя тетради в брой байтове10 010A 49 dec cx ; получи указатели към старшите байтове11 010B 01 CE add si,cx12 010D 01 CF add di,cx13 010F 87 FE xchg si,di ; CMP4S изважда [SI] от [DI], а CMPSB - [DI] от [SI]14 0111 FD std ; сравнението започва от старшите байтове15 0112 F3 A6 repe cmpsb ; направи сравнението16 0114 FC cld ; възстанови DF17 0115 5F pop di ; възстанови регистрите18 0116 5E pop si19 0117 59 pop cx20 0118 5B pop bx21 0119 58 pop ax22 011A C3 ret

7.4. Въвеждане от клавиатурата на низ от пакетирани двоично-десетични цифри

Зад. 26. Да се напише подпрограма, която да въвежда от клавиатурата в буфер, указан от ES:DI, низ от не повечеот CX двоично-десетични цифри, запълвайки го с водещи нули до CX цифри.

Page 23: Сборник задачи по програмиране на Асемблер

23Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Решение: В сравнение със задача №14 (въвеждане на низ) тук има 2 трудности. Първата е, че след въвежданетонизът трябва да бъде подравнен и допълнен с водещи нули, за да могат да се извършват аритметични действия снего и евентуален друг такъв низ. Втората е, че е при нечетен брой въведени десетични цифри се изисквадопълнително изместване с 1 десетична цифра. В алгоритъма има 2 последователни цикъла – „L1“ за въвежданеи „L3“ за допълнително изместване при нечетен брой десетични цифри. В цикъла „L1“ цифрите се въвеждат,пакетират и съхраняват, започвайки от старшата тетрада на най-старшия байт на низа (последния адрес).Въвеждането завършва с натискане на клавиша „Return“. В случай на нечетен брой въведени цифри в цикъла„L3“ всички въведени цифри се изместват с 4 бита (1 цифра), така че най-младшата цифра да заеме младшатетрада. Това позволява подравняването и запълването с водещи нули (от „L4“ нататък) да се извърши побайтове.

Заб.: В тази и в следващата задача дължината на низа се задава в CX, а не в CL, както е в предходните 3 задачи(как може да се отстрани това несъответствие?).

A386 V4.05 assembly of GETBCD.COM 2011-07-30 11:46Source: GETBCD.A86 Page 1

1 ; въведи низ от до CX двоично-десетични цифри по ES:DI и го допълни с водещи нули2 0100 41 inc cx ; закръгли нагоре до следващия четен брой3 0101 D1 E9 shr cx,1 ; превърни броя тетради в брой байтове4 0103 8B D9 mov bx,cx ; съхрани дължината на низа (L)5 0105 8B EF mov bp,di ; съхрани указателя към низа6 0107 01 CF add di,cx ; придвижи се напред L байта7 0109 4F dec di ; укажи обратно към най-старшия байт8 010A 89 FE mov si,di ; съхрани указателя9 010C FD std ; ще се движим назад10 010D 33 C9 xor cx,cx ; изчисти брояча на байтове11 010F B4 01 l1: mov ah,1 ; въвеждане на 1 символ12 0111 CD 21 int 21h ; получи старшата тетрада (цифра)13 0113 3C 0D cmp al,13 ; CR?14 0115 74 26 je l4 ; да, премини към подравняване на низа и допълване с нули15 0117 8A D0 mov dl,al ; не, съхрани я16 0119 41 inc cx ; преброй още 1 байт17 011A CD 21 int 21h ; получи младшата тетрада (цифра)18 011C 3C 0D cmp al,13 ; CR?19 011E 74 09 je l2 ; да, имаме нечетен (т.е. неподравнен) брой цифри!20 0120 8A E2 mov ah,dl ; не, премини към преобразуване, пакетиране и запис21 0122 24 0F and al,1111b; превърни от ASCII (старшата цифра няма защо) и пакетирай22 0124 D5 10 aad 16 ; няма да работи на NEC V20/25/30/33/35/40/41/50/51/53/55!23 0126 AA stosb ; съхрани така пакетирания байт24 0127 EB E6 jmp l1 ; продължи със следващия25 0129 8A C2 l2: mov al,dl26 .18627 012B C0 E0 04 shl al,4 ; направи оставащата цифра старша тетрада (ще бъде младша)28 012E AA stosb29 012F AC l3: lodsb ; сега измести целия низ 1 десетична цифра надясно:30 0130 C0 EC 04 shr ah,4 ; * измести предната мл., сега старша на AH, до мл. на AH31 0133 C1 C8 04 ror ax,4 ; * предната мл. -> ст. на AH, ст.-> мл., мл. -> ст. на AH32 0136 88 44 01 mov [si+1],al33 0139 3B F7 cmp si,di ; дойдохме ли пак до най-младшия байт?34 013B 77 F2 ja l3 ; не, продължи35 013D 8D 75 01 l4: lea si,[di+1];започни от най-младшия байт36 0140 8B FD mov di,bp ; възстанови указателя към началото на низа37 0142 FC cld ; възстанови DF38 0143 29 CB sub bx,cx ; BX = брой байтове за запълване39 0145 F3 A4 rep movsb ; подравни низа вдясно40 0147 8B CB mov cx,bx ; установи брояча41 0149 B0 00 mov al,0 ; допълни с водещи нули42 014B F3 AA rep stosb43 014D C3 ret

7.5. Извеждане на низ от пакетирани двоично-десетични цифри на екрана

Зад. 27. Да се напише подпрограма, която да извежда на екрана низ от CX двоично-десетични цифри, указан отDS:SI.

Решение: Задачата е обратна на предходната и се решава много по-лесно от нея с прост цикличен алгоритъм.Особеното тук е само това, че извеждането става от най-големия адрес (най-старшия байт) към най-малкия (най-младшия).

Page 24: Сборник задачи по програмиране на Асемблер

24Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Заб.: При това решение се извеждат и водещите нули. То може да се видоизмени така, че вместо тях да сеизвеждат интервали (как?).

A386 V4.05 assembly of PUTBCD.COM 2011-07-30 11:46Source: PUTBCD.A86 Page 1

1 ; извеждане на екрана на низ от CX двоично-десетични цифри, указан от DS:SI2 0100 41 inc cx ; закръгли нагоре до следващия четен брой3 0101 D1 E9 shr cx,1 ; превърни броя тетради в брой байтове4 0103 01 CE add si,cx ; придвижи се напред L байта (L = дължина)5 0105 4E dec si ; укажи обратно към най-старшия байт6 0106 FD std ; ще се движим назад7 0107 AC l0: lodsb ; получи следващия байт8 0108 D4 10 aam 16 ; разпакетирай го9 010A 05 30 30 add ax,('0' shl 8) or '0' ; преобразувай десетичните цифри в ASCII10 010D 86 C4 xchg ah,al11 010F CD 29 int 29h ; изведи старшата тетрада (цифра)12 0111 88 E0 mov al,ah13 0113 CD 29 int 29h ; изведи младшата тетрада (цифра)14 0115 E2 F0 loop l0 ; повтори за всички цифри15 0117 FC cld ; възстанови DF16 0118 C3 ret

8. Обработка на интервали от време

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

8.1. Намиране на датата на източноправославния Великден за дадена година

Зад. 28. Да се напише подпрограма, която да връща в регистър AL деня (числото), а в регистър AH – месеца наизточноправославния Великден за годината в регистър AX.

Решение: Алгоритъмът е линеен и е даден в коментара на програмата. Той важи за грегорианския календар, т.е.годината в AX трябва да е не по-ранна от годината на приемането му.

Page 25: Сборник задачи по програмиране на Асемблер

25Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of EASTER.COM 2011-07-30 11:46Source: EASTER.A86 Page 1

1 ; върни деня в AL и месеца в AH на православния Великден за год. в AX (1583-4099)2 .1863 0100 8B F0 mov si,ax ; съхрани годината4 0102 33 D2 xor dx,dx ; изчисти старшата дума на делимото5 0104 B9 07 00 mov cx,7 ; делител6 0107 F7 F1 div cx7 0109 89 D7 mov di,dx ; DI = год%78 010B 8B C6 mov ax,si9 010D 33 D2 xor dx,dx10 010F B9 13 00 mov cx,1911 0112 F7 F1 div cx ; DX = год%1912 0114 92 xchg ax,dx ; AX = год%1913 0115 F7 E1 mul cx ; AX = 19 * (год%19), DX = 014 0117 05 10 00 add ax,16 ; AX = 19 * (год%19) + 1615 011A B9 1E 00 mov cx,3016 011D F7 F1 div cx ; DX = n1 = (19 * (год%19) + 16) % 3017 011F 89 F3 mov bx,si ; BX = год18 0121 83 E3 03 and bx,3 ; BX = год%419 0124 D1 E3 shl bx,1 ; BX = 2 * (год%4)20 0126 C1 E7 02 shl di,2 ; DI = 4 * (год%7)21 0129 92 xchg ax,dx ; AX = n122 012A 89 C1 mov cx,ax ; CX = n123 012C BA 06 00 mov dx,624 012F F7 E2 mul dx ; AX = 6 * n1, DX = 025 0131 01 F8 add ax,di ; AX = 4 * (год%7) + 6 * n126 0133 01 D8 add ax,bx ; AX = 2 * (год%4) + 4 * (год%7) + 6 * n127 0135 BB 07 00 mov bx,728 0138 F7 F3 div bx ; DX = n2 = (2 * (год%4) + 4 * (год%7) + 6 * n1) % 729 013A 01 D1 add cx,dx ; CX = n1 + n230 013C 96 xchg ax,si ; AX = год31 013D 33 D2 xor dx,dx32 013F BB 64 00 mov bx,10033 0142 F7 F3 div bx ; AX = век34 0144 03 C8 add cx,ax ; CX = n1 + n2 + век (преобразувай в грегорианския кал.)35 0146 C1 E8 02 shr ax,2 ; AX = век / 4 " " "36 0149 2B C8 sub cx,ax ; CX = n3 = n1 + n2 + век - век / 437 014B 91 xchg ax,cx ;(формула на Гаус за броя на дните след юлианския 19.III.)38 014C 2C 0D sub al,13 ; ден = n3 - 1 - 12 (дните от март след юлианския 19.III.)39 014E D4 1E aam 3040 0150 05 01 04 add ax,401h ; AH = 4/5 (април/май), преобразувай дните в AL към база 141 0153 C3 ret

8.2. Намиране на датата на римокатолическия Великден за дадена година

Зад. 29. Да се напише подпрограма, която да връща в регистър AL деня (числото), а в регистър AH – месеца наримокатолическия Великден за годината в регистър AX.

Решение: Алгоритъмът е разклонен и е даден в коментара на програмата. Той важи за грегорианския календар.Първо се намира датата на пасхалното пълнолуние. След това се извиква с командата „call“ подпрограмата занамиране на деня от седмицата на това пълнолуние (включена с директивата „include“) и оттам се намира дататана неделята след него (Великден).

Page 26: Сборник задачи по програмиране на Асемблер

26Сборник задачи по програмиране на Асемблер

22 март 2012 11:43:11http://x86.hit.bg/assembly.html

A386 V4.05 assembly of WESTER.COM 2011-07-30 11:46Source: WESTER.A86 Page 1

1 ; върни деня в AL и месеца в AH на католическия Великден за год. в AX (1583-4099)2 0100 50 push ax ; съхрани годината3 0101 8B F0 mov si,ax4 0103 33 D2 xor dx,dx5 0105 B9 13 00 mov cx,196 0108 F7 F1 div cx ; DX = год%197 010A 89 D3 mov bx,dx ; BX = год%198 010C 92 xchg ax,dx ; AX = год%199 010D B1 0B mov cl,1110 010F F7 E1 mul cx ; AX = 11 * (год%19), DX = 011 0111 96 xchg ax,si ; SI = 11 * (год%19), AX = год12 0112 B1 64 mov cl,100 ; делител13 0114 F7 F1 div cx ; AX = век14 0116 8B F8 mov di,ax ; DI = век15 0118 2D 0F 00 sub ax,15 ; AX = век - 1516 011B D1 E8 shr ax,1 ; AX = (век - 15) / 217 011D 05 CA 00 add ax,202 ; AX = (век - 15) / 2 + 20218 0120 29 F0 sub ax,si ; n1 = (век - 15) / 2 + 202 - 11 * (год%19)19 0122 83 FF 1A cmp di,26 ; if (век > 26)20 0125 76 07 jbe l121 0127 48 dec ax ; n1--;22 0128 83 FF 26 cmp di,38 ; if (век > 38)23 012B 76 01 jbe l124 012D 48 dec ax ; n1--;25 012E 83 FF 15 l1: cmp di,21 ; if (век == 21 ||26 0131 74 19 je l227 0133 83 FF 18 cmp di,24 ; век == 24 ||28 0136 74 14 je l229 0138 83 FF 19 cmp di,25 ; век == 25 ||30 013B 74 0F je l231 013D 83 FF 21 cmp di,33 ; век == 33 ||32 0140 74 0A je l233 0142 83 FF 24 cmp di,36 ; век == 36 ||34 0145 74 05 je l235 0147 83 FF 25 cmp di,37 ; век == 37 ) {36 014A 75 01 jne l337 014C 48 l2: dec ax ; n1--;38 014D 33 D2 l3: xor dx,dx39 014F B1 1E mov cl,3040 0151 F7 F1 div cx ; DX = n1 %= 30;41 0153 83 FA 1D cmp dx,29 ; if (n1 == 29 ||42 0156 74 0A je l443 0158 83 FA 1C cmp dx,28 ; n1 == 28 &&44 015B 75 06 jne l545 015D 83 FB 0A cmp bx,10 ; год%19 > 10)46 0160 76 01 jbe l547 0162 4A l4: dec dx ; n1--;48 0163 92 l5: xchg ax,dx49 0164 04 14 add al,20 ; AL = дни след февруари - 150 0166 D4 1F aam 3151 0168 05 01 03 add ax,301h ; AH = 3/4 (март/април), превърни дните в AL в база 152 016B 5A pop dx ; възстанови годината53 016C 50 push ax ; съхрани датата и месеца на пасхалното пълнолуние (ПП)54 016D E8 15 00 call weekday ; получи деня от седмицата на ПП в AL (0 = неделя)55 0170 5A pop dx ; DL = дата на ПП, DH = месец на ПП56 0171 80 C2 06 add dl,6 ; DL = дата на ПП + 657 0174 28 C2 sub dl,al ; DL = дата на ПП + 6 - ден от седмицата на ПП (ден на ПП)58 0176 8A C2 mov al,dl ; AL = дата на ПП + 6 - ден от седмицата на ПП (ден на ПП)59 0178 B2 22 mov dl,3460 017A 2A D6 sub dl,dh ; DL = 34 - месец на ПП = 31/30 (март/април)61 017C 98 cbw ; AH = 062 017D F6 F2 div dl ; AH = (дата на ПП + 6 - ден на ПП) % (34 - месец на ПП)63 017F 00 F0 add al,dh ; добави месеца към частното (ако е било 1 -> следв.месец)64 0181 86 E0 xchg al,ah ; AH = месец, AL = дата65 0183 40 inc ax ; преобразувай към база 166 0184 C3 ret67 weekday:68 include weekday.a8669 i ; Вход: AL = дата (d), AH = месец (m), DX = година (y)70 i ; Изход: AL = денят от седмицата (0 = неделя, 1 = понеделник, 2 = вторник и т.н.)71 i ; Алгоритъм: dow(m,d,y){y-=m<3;return(y+y/4-y/100+y/400+"-bed=pen+mad."[m]+d)%7;}72 i ; (пуснат на 3.III.93 в comp.lang.c от [email protected] (Томохико Сакамото))73 i .18674 0185 86 C4 i xchg ah,al ; AH = d, AL = m

Page 27: Сборник задачи по програмиране на Асемблер

27Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

75 0187 3C 03 i cmp al,376 0189 83 DA 00 i sbb dx,0 ; y -= m < 377 018C BB B4 01 i mov bx,offset s178 018F D7 i xlat ; AL = s1[m]79 0190 02 C4 i add al,ah ; AL = s1[m] + d80 0192 B4 00 i mov ah,0 ; (CBW няма да работи!)81 0194 01 D0 i add ax,dx ; AX = y + s1[m] + d82 0196 89 D3 i mov bx,dx ; BX = y83 0198 C1 EA 02 i shr dx,2 ; DX = y/484 019B 01 D0 i add ax,dx ; AX = y + y/4 + s1[m] + d85 019D 93 i xchg ax,bx ; AX = y, BX = y + y/4 + s1[m] + d86 019E 33 D2 i xor dx,dx ; DX = 087 01A0 B9 64 00 i mov cx,100 ; делител88 01A3 F7 F1 i div cx ; AX = y/10089 01A5 29 C3 i sub bx,ax ; BX = y + y/4 - y/100 + s1[m] + d90 01A7 C1 E8 02 i shr ax,2 ; AX = y/40091 01AA 01 D8 i add ax,bx ; AX = y + y/4 - y/100 + y/400 + s1[m] + d92 01AC 33 D2 i xor dx,dx93 01AE B1 07 i mov cl,794 01B0 F7 F1 i div cx95 01B2 92 i xchg ax,dx ; AX = (y + y/4 - y/100 + y/400 + s1[m] + d) % 796 01B3 C3 i ret97 01B4 2D 62 65 64 3D i s1 db '-bed=pen+mad.'

8.3. Намиране на деня от седмицата за дадена дата

Зад. 30. Да се напише подпрограма, която да връща в регистър AL деня от седмицата за дадена дата (число) вAL, месец в AH и година в DX.

Решение: Алгоритъмът е на Томохико Сакамото. Той е линеен и е даден като коментар във втората половинана листинга на предходната задача.

8.4. Преобразуване на UNIX-време в дата и време на файл в ДОС

Зад. 31. Да се напише подпрограма, която да връща в регистър DX датата, а в AX – времето на файл в ДОС зададено UNIX-време в DX:AX.

Решение: Алгоритъмът съдържа 2 цикъла („L1“ и „L3“) с разклонения в тялото и още 1 разклонение („L5“).Масивът „days“ съдържа броя на дните във всеки от 12-те месеца. Обърнете внимание как се изпълнявасравнението (от „L3“ нататък) на 16-битовото число в DX с 8-битовото от масива.

Заб. 1: Отместването в базовата адресация след „L2“ е 16-битово. Могат да се спестят още 1 или дори 2 байта,ако то се направи 8-битово (как?).

Заб. 2: Времето в ОС UNIX (и във функцията „time()“ на стандартната библиотека на език C) се измерва всекунди след 0 ч. 0 мин. и 0 сек. на 1 януари 1970 г. През 2038 г. в системите, където тази стойност е 32-битовочисло със знак, ще настъпи препълване. Това е т.н. „проблем 2038 г.“, който се очертава да бъде значително по-сериозен от „проблема 2000 г.“.

Заб. 3: Във файловете на ДОС (файлова система FAT ) и в ZIP-архивите датата и времето се записват като две16-битови числа, които образуват 32-битово. Годината минус 1980 е в бит 15–9, месецът – в бит 8–5, а денят(числото) – в бит 4–0 на старшата дума. Часът е в бит 15–11, минутата – в бит 10–5, а секундата, разделена на 2 –в бит 4–0 на младшата дума.

Заб. 4: В ДОС се използва местно време, а в UNIX – универсално (гринуичко ) време. Тази часова разлика не севзема предвид тук (т.е. местното време се приема за гринуичко ).

Page 28: Сборник задачи по програмиране на Асемблер

28Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of TIME2DOS.COM 2011-07-30 11:46Source: TIME2DOS.A86 Page 1

1 ; превърни UNIX-времето (сек.след 1.I.70) в DX:AX в DOS-файлови дата:време (DX:AX)2 = : 12CE_A600 tenyrs equ ((365 * 10 + 2) * (24 * 60 * 60)) ; 10 години: 315532800 с3 =bignum _120yrs equ ((365 * 120 + 120 / 4) * (24 * 60 * 60)) ; 120 год.: 3786912000 с4 0100 2D 00 A6 sub ax,tenyrs and 0FFFFh ; секунди след 1.I.1980 г.5 0103 81 DA CE 12 sbb dx,tenyrs shr 166 0107 72 79 jb l5 ; ако е по-рано от 1980 г., изход7 0109 3D 00 B1 cmp ax,_120yrs and 0FFFFh ; година < 2100 ? (1980 + 120 = 2100)8 010C 89 D3 mov bx,dx9 010E 81 DB B7 E1 sbb bx,_120yrs shr 16 ; 2100 или > 2100 (невалидна в ДОС)? Изход10 0112 F5 cmc11 0113 72 6D jb l512 0115 D1 EA shr dx,1 ; преобразувай в двойки секунди13 0117 D1 D8 rcr ax,114 0119 BB C0 A8 mov bx,24 * 60 * (60/2) ; двойки секунди за 1 денонощие15 011C F7 F3 div bx ; AX = дни, DX = двойки секунди16 011E 91 xchg ax,cx ; CX = дни17 011F 33 C0 xor ax,ax18 0121 92 xchg ax,dx ; DX = 0, AX = двойки секунди19 0122 BB 1E 00 mov bx,60/220 0125 F7 F3 div bx ; DX = двойки секунди, AX = минути21 0127 88 D7 mov bh,dl ; съхрани двойките секунди22 0129 D0 E3 shl bl,1 ; = 6023 012B F6 F3 div bl ; AH = минути, AL = часове24 012D 86 C4 xchg ah,al ; AL = минути, AH = часове25 .18626 012F C0 E0 02 shl al,2 ; бит 7-2: минути27 0132 C1 E0 03 shl ax,3 ; бит 15-11: часове, бит 10-5: минути28 0135 08 F8 or al,bh ; бит 4-0: двойки секунди29 0137 91 xchg ax,cx ; CX = време, AX = дни30 0138 BB B5 05 mov bx,4 * 365 + 1 ; дните в 4-годишен период31 013B 33 D2 xor dx,dx32 013D F7 F3 div bx ; AX = 4-г. периоди, DX = дните след посл.33 013F C1 E0 02 shl ax,2 ; години преди последната високосна година34 0142 BB 6E 01 mov bx,366 ; дните в 1 високосна година (I в периода)35 0145 3B D3 l1: cmp dx,bx ; < 1 година ?36 0147 72 08 jb l2 ; да, готово: DX = дни, AX = год. след '8037 0149 2B D3 sub dx,bx ; не, извади дните в 1 година38 014B 40 inc ax39 014C BB 6D 01 mov bx,36540 014F EB F4 jmp l141 0151 33 DB l2: xor bx,bx42 0153 C6 87 84 01 1C mov byte ptr [days+1+bx],28 ; необходимо при многократно извикване43 0158 A8 03 test al,3 ; високосна година?44 015A 75 04 jnz l3 ; не, продължи45 015C FE 87 84 01 inc byte ptr [days+1+bx] ; да, увеличи дните на февруари до 2946 0160 3A 97 83 01 l3: cmp dl,[days+bx] ; < 1 месец?47 0164 8A E6 mov ah,dh48 0166 80 DC 00 sbb ah,049 0169 72 0A jb l4 ; да, готово50 016B 2A 97 83 01 sub dl,[days+bx] ; не, извади дните на този месец51 016F 80 DE 00 sbb dh,052 0172 43 inc bx ; обнови индекса на месеца53 0173 EB EB jmp l354 0175 42 l4: inc dx ; AX = година, DX = ден (база 1)55 0176 43 inc bx ; BX = месец (база 1)56 0177 C1 E0 09 shl ax,9 ; bit 15-9: години след 198057 017A C1 E3 05 shl bx,5 ; bit 8-5: месец58 017D 09 D8 or ax,bx59 017F 09 C2 or dx,ax ; bit 4-0: ден, DX = дата60 0181 91 xchg ax,cx ; AX = време61 0182 C3 l5: ret62 0183 1F 1C 1F 1E 1F days db 31,28,31,30,31,30,31,31,30,31,30,31

8.5. Преобразуване на дата и време на файл в ДОС в UNIX-време

Зад. 32. Да се напише подпрограма, която да връща в регистри DX:AX UNIX-времето за дадени в DX дата и вAX време на файл в ДОС.

Page 29: Сборник задачи по програмиране на Асемблер

29Сборник задачи по програмиране на Асемблер

22 март 2012 11:43:11http://x86.hit.bg/assembly.html

Решение: Това е обратната на предходната задача и в решението има някои общи моменти. Алгоритъмътсъдържа 1 цикъл („L2“) с разклонение в тялото и още 2 разклонения („L1“ и „L4“). Обърнете внимание накомандата „lea dx,[bx+(3 shl 9)]“ (какво се цели с нея?).

A386 V4.05 assembly of DOS2TIME.COM 2011-07-30 11:46Source: DOS2TIME.A86 Page 1

1 ; превърни DOS-файловите дата:време (DX:AX) в UNIX-време (сек.след 1.I.70) в DX:AX2 = : 12CE_A600 tenyrs equ ((365 * 10 + 2) * (24 * 60 * 60)) ; 10 години (315532800 с)3 0100 89 D3 mov bx,dx ; дата4 0102 8B D0 mov dx,ax ; време5 0104 89 C1 mov cx,ax 6 .186 7 0106 C1 E8 0B shr ax,11 ; бит 4-0: час8 0109 6B C0 3C imul ax,60 ; AX = час * 609 010C C1 EA 05 shr dx,5 ; бит 5-0: минута10 010F 83 E2 3F and dx,111111b ; маскирай часа11 0112 01 D0 add ax,dx ; AX = час * 60 + минута12 0114 BA 1E 00 mov dx,60/213 0117 F7 E2 mul dx ; DX = 0, AX = (час * 60 + минута) * 3014 0119 83 E1 1F and cx,11111b ; bit 4-0: двойки секунди15 011C 03 C8 add cx,ax ; CX = двойки секунди до 1 денонощие16 011E 8B C3 mov ax,bx ; дата17 0120 C1 E8 09 shr ax,9 ; бит 6-0: години след 198018 0123 3C 78 cmp al,120 ; ако год. >= 2100 (не се поддържа в ДОС!), изход19 0125 F5 cmc 20 0126 72 4A jc l421 0128 C6 06 74 01 1C mov byte ptr [days+1],28 ; необходимо при многократно извикване22 012D A8 03 test al,3 ; високосна година?23 012F 75 04 jnz l1 ; не, продължи24 0131 FE 06 74 01 inc byte ptr [days+1] ; да, увеличи дните на февруари до 2925 0135 BA 6D 01 l1: mov dx,365 26 0138 F7 E2 mul dx ; DX = 0, AX = дни след 1.I.1980 без датите 29.II.27 013A 8D 97 00 06 lea dx,[bx+(3 shl 9)]28 013E C1 EA 0B shr dx,11 ; bit 4-0: високосни години след 1980 г.29 0141 01 D0 add ax,dx ; AX = дни след 1980 г. до 1 януари30 0143 8D 57 FF lea dx,[bx-1] ; дата без последния ден31 0146 83 E2 1F and dx,11111b ; бит 4-0: ден в месеца32 0149 01 D0 add ax,dx ; AX = дни след 1.I.1980 без месеците на посл.год.33 014B C1 EB 05 shr bx,5 ; bit 3-0: месец34 014E 83 E3 0F and bx,1111b ; маскирай годината35 0151 4B l2: dec bx ; изключи последния месец, още месеци?36 0152 74 09 jz l3 ; не, готово37 0154 02 87 72 01 add al,[days-1+bx] ; да, добави дните на предходните месеци38 0158 80 D4 00 adc ah,039 015B EB F4 jmp l240 015D BA C0 A8 l3: mov dx,24 * 60 * (60/2) ; двойки секунди в денонощие41 0160 F7 E2 mul dx ; DX:AX = двойки секунди до последното денонощие42 0162 03 C1 add ax,cx ; добави двойките секунди на последното денонощие43 0164 83 D2 00 adc dx,044 0167 D1 E0 shl ax,1 ; преобразувай двойките секунди в секунди45 0169 D1 D2 rcl dx,146 016B 05 00 A6 add ax,tenyrs and 0FFFFh ; добави секундите на 10-те години 1970-198047 016E 81 D2 CE 12 adc dx,tenyrs shr 1648 0172 C3 l4: ret 49 0173 1F 1C 1F 1E 1F days db 31,28,31,30,31,30,31,31,30,31,30,31

9. Подреждане на масиви от данни

В тази група са включени задачи за сортиране на масиви от целочислени данни.

9.1. Сортиране с приоритет на простотата

Зад. 33. Да се напише подпрограма, която да сортира във възходящ ред масив от CX (но поне 2) думи, указан отрегистър SI.

Решение: Най-простият известен алгоритъм за сортиране се нарича „глупаво сортиране“ (познат е още подимето „сортиране на джуджето“). Той е цикличен с разклонение в тялото на цикъла и е даден на език C вкоментара на листинга на програмата. Обърнете внимание , че реализацията на Асемблер има приблизителнотолкова операции, колкото и тази на език C!

Page 30: Сборник задачи по програмиране на Асемблер

30Сборник задачи по програмиране на Асемблер

22 март 2012 11:43:11http://x86.hit.bg/assembly.html

A386 V4.05 assembly of STUPDSRT.COM 2011-07-30 11:46Source: STUPDSRT.A86 Page 1

1 ; подреди във възходящ ред масив от CX думи, указани от SI (CX > 1)2 0100 49 dec cx ; void StupidSort(int a[], int n)3 0101 D1 E1 shl cx,1 ; {4 0103 33 DB xor bx,bx ; int tmp, i = 0;5 0105 8B 00 l1: mov ax,[si+bx] ; do {6 0107 3B 40 02 cmp ax,[si+bx+2] ; if (a[i] > a[i+1]) {7 010A 7E 0C jle l2 ; tmp = a[i+1];8 010C 87 40 02 xchg ax,[si+bx+2] ; a[i+1] = a[i];9 010F 89 00 mov [si+bx],ax ; a[i] = tmp;10 0111 0B DB or bx,bx ; if (i)11 0113 74 03 jz l2 ; i--;12 0115 83 EB 04 sub bx,4 ; } else13 0118 83 C3 02 l2: add bx,2 ; i++;14 011B 39 CB cmp bx,cx ; } while (i < n - 1);15 011D 72 E6 jb l1 ; }16 011F C3 ret

9.2. Сортиране с приоритет на бързодействието

Зад. 34. Да се реши предходната задача с използването на бързодействащ алгоритъм.

Решение: Един от най-ефективните алгоритми за сортиране, при който няма съществено забавяне дори принай-лошия случай, се нарича „сортиране на купчина“ (на англ. „heap sort“). Той е тип „цикъл в цикъла“, катовътрешният цикъл се изпълнява от подпрограмата „downheap“. Алгоритъмът на език C е даден в листинга напрограмата.

Заб. 1: Автор на тази разновидност на алгоритъма е Ingo Wegener – вж. неговата статия „Bottom-up Heapsort, anew variant of Heapsort beating, on an average, Quicksort (if n is not very small)“, сп. „Theoretical Computer Science“,том 118, брой 1 (13 септември 1993 г.), стр. 81–98.

Заб. 2: На микропроцесор „Pentium-III“ (1,2 GHz) тази реализация сортира масив от 32000 случайни числа самоза около 9788500 машинни цикъла (8,157 мс). А „глупавото сортиране“, използвано в предходното решение, сесправя със същата задача за цели 7,71 с, или около 945 пъти по-бавно!

Page 31: Сборник задачи по програмиране на Асемблер

31Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of HEAPSORT.COM 2011-07-30 11:46Source: HEAPSORT.A86 Page 1

1 ; подреди във възходящ ред масив от CX думи, указани от SI (CX > 1)2 0100 89 F3 mov bx,si ; a void heapSort(int *a, int n)3 0102 8B F9 mov di,cx ; i = n {4 0104 D1 EF shr di,1 ; i = n / 2 int i, tmp; // di (cx), ax5 0106 4F dec di ; i = n / 2 - 1 for (i = n / 2 - 1; i >= 0; i--)6 0107 E8 15 00 l1: call downHeap; арг.: bx, di, cx downHeap(a, i, n);7 010A 4F dec di ; i--8 010B 79 FA jns l1 ; ако i >= 0, продължи9 010D 47 inc di ; -1 + 1 = 010 010E 49 dec cx ; i = n - 111 010F 8B F1 l2: mov si,cx ; for (i = n - 1; i > 0; i--) {12 0111 D1 E6 shl si,1 ; ук. към дума13 0113 8B 00 mov ax,[bx+si]; tmp = a[i];14 0115 87 07 xchg ax,[bx] ; a[i] = a[0];15 0117 89 00 mov [bx+si],ax; a[0] = tmp;16 0119 E8 03 00 call downHeap; арг.: bx, di, cx downHeap(a, 0, i);17 011C E2 F1 loop l2 ; }18 011E C3 ret ; }19 011F 51 downHeap: push cx ; void downHeap(int *a, int k, int n)// bx, di, cx20 0120 57 push di ; {21 0121 D1 E7 shl di,1 ; ук. към дума int i, tmp; // si, ax22 0123 49 dec cx ; n--;23 0124 8B 01 mov ax,[bx+di]; for (tmp = a[k]; k <= n / 2; k = i) {24 0126 89 FE l3: mov si,di ; 2i = 2k i = 2 * k;25 0128 D1 E6 shl si,1 ; 2i = 2 * 2k26 012A 39 CF cmp di,cx ; 2k < n ? if (i < n27 012C 73 0A jae l4 ; ако не, прескочи &&28 012E 8B 10 mov dx,[bx+si]; a[i] < a[i+1])29 0130 3B 50 02 cmp dx,[bx+si+2]30 0133 7D 03 jge l431 0135 83 C6 02 add si,2 ; i++;32 0138 8B 10 l4: mov dx,[bx+si]; if (tmp >= a[i])33 013A 39 D0 cmp ax,dx34 013C 7D 08 jge l5 ; break;35 013E 89 11 mov [bx+di],dx; a[k] = a[i];36 0140 89 F7 mov di,si ; 2k = 2i }37 0142 39 CF cmp di,cx ; 2k <= n ?38 0144 76 E0 jbe l3 ; ако да, продължи39 0146 89 01 l5: mov [bx+di],ax; a[k] = tmp;40 0148 5F pop di ; }41 0149 59 pop cx42 014A C3 ret

10. Величини с променлива дължина

В тази група са включени задачи за четене и запис на т. нар. „величини с променлива дължина“. Това сапоредици от 1 до 4 байта, от които всички без последния имат установен в лог. 1 бит 7. Така може да се запишечисло с до 28 значещи бита без загуба на място при малки числа, както би станало, ако се използваха винаги 4байта. Такова представяне на числата се използва например в т.н. „стандартни MIDI-файлове“.

10.1. Четене на величина с променлива дължина

Зад. 35. Да се напише подпрограма, която да връща в регистри DX:AX числената стойност на величина спроменлива дължина, указана от регистър SI, в който да връща указател към следващия след нея байт.

Решение: Алгоритъмът, даден на език C в началото на листинга, е разклонен с цикъл в тялото наразклонението. Той не отговаря изцяло на реализацията на Асемблер, при която е преобразуван в цикличен свход в средата на цикъла и се връща стойност и в SI. Обърнете внимание на това, как е направено изместванетосъс 7 бита наляво.

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

Page 32: Сборник задачи по програмиране на Асемблер

32Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of GETVLQ.COM 2011-07-30 11:46Source: GETVLQ.A86 Page 1

1 ;unsigned long getVLQ(unsigned char *src) // прочети величина с променлива дължина2 ;{3 ; unsigned char c;4 ; unsigned long value = *src++;5 ; if (value & 0x80) {6 ; value &= 0x7f;7 ; do8 ; value = (value << 7) + ((c = *src++) & 0x7f);9 ; while (c & 0x80);10 ; }11 ; return value;12 ;} ; src се предава в SI13 0100 33 C0 xor ax,ax ; value се съставя в DX:BX14 0102 33 D2 xor dx,dx ; изчисти старшата15 0104 33 DB xor bx,bx ; и младшата дума16 0106 EB 10 jmp short l2 ; премини към получаване на първия байт17 0108 24 7F l1: and al,7fH ; маскирай бит 7, остави значещите битове18 010A 08 C3 or bl,al ; „сглоби“ най-младшия байт19 010C 88 D6 mov dh,dl ; първо, value << 820 010E 88 FA mov dl,bh21 0110 88 DF mov bh,bl ; (DH <- DL <- AH <- AL <- 0)22 0112 B3 00 mov bl,023 0114 D1 EA shr dx,1 ; после, value >> 124 0116 D1 DB rcr bx,1 ; резултатът е: value << 725 0118 AC l2: lodsb ; получи следващия байт26 0119 84 C0 test al,al ; има ли още байтове?27 011B 78 EB js l1 ; да, продължи28 011D 09 D8 or ax,bx ; не, върни стойността в DX:AX29 011F C3 ret ; и src в SI (BX е променен)

10.2. Запис на величина с променлива дължина

Зад. 36. Да се напише подпрограма, която да записва числото в регистри DX:AX като величина с променливадължина в буфера, указан от ES:DI, където да връща указател към следващия след нея байт.

Решение: Това е обратната на предходната задача, откъдето е взаимстван и принципът на изместването на 7бита (този път надясно). Алгоритъмът, даден на език C в началото на листинга, се състои от два цикъла, вториятот които е с разклонение в тялото. (В реализацията на Асемблер първият цикъл също е с разклонение в тялото исе връща стойност и в DI.) В първия цикъл се формира величината с променлива дължина в буфера, а въввтория тя се записва по указателя „dest“ (ES:DI).

Заб.: Обърнете внимание на реализацията на буфера в стека с мощна базово-индексна адресация. Такаизместването на буфера се свежда до просто увеличаване с 1 на стойността на SI.

Page 33: Сборник задачи по програмиране на Асемблер

33Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of PUTVLQ.COM 2011-07-30 11:46Source: PUTVLQ.A86 Page 1

1 ;void writeVariNum(unsigned long value, unsigned char *dest) // запиши ВПД2 ;{3 ; unsigned long buffer = value & 0x7f;4 ; while ((value >>= 7) > 0) {5 ; buffer <<= 8;6 ; buffer |= 0x80;7 ; buffer += value & 0x7f;8 ; }9 ; for ( ; ; ) {10 ; *dest++ = (unsigned char)buffer;11 ; if (buffer & 0x80)12 ; buffer >>= 8;13 ; else14 ; return;15 ; }16 ;}17 ; Стек: -4 <-- [bp+si] в началото, първо AX се записва тук с „push“18 ; -2 <-- [sp] след „enter 2,0“19 ; 0 <-- [bp]20 ; 2 <-- старият BP е записан тук с „enter 2,0“21 .186 ; value се предава в DX:AX, dest - в DI22 0100 C8 02 00 00 enter 2,0 ; Turbo EditAsm генерира неправилен код за „enter“23 0104 50 push ax ; използвани регистри: DX:AX=value, SI=ук.към буф.24 0105 BE FC FF mov si,-4 ; укажи буфера, където AX отива с „push ax“25 0108 80 22 7F and byte ptr[bp+si],7fH;buffer = value & 0x7f//младшия байт е вече там26 010B D0 E0 l1: shl al,1 ; съхрани бит 7 в CF27 010D 88 E0 mov al,ah ; първо, value >> 828 010F 8A E2 mov ah,dl29 0111 88 F2 mov dl,dh30 0113 B6 00 mov dh,031 0115 D1 D0 rcl ax,1 ; (бившият бит 7 става бит 0)32 0117 D1 D2 rcl dx,1 ; после, value << 1; резултатът е: value >>= 733 0119 89 D3 mov bx,dx34 011B 09 C3 or bx,ax35 011D 74 08 jz l2 ; while ((value >>= 7) > 0) {36 011F 46 inc si ; buffer <<= 8;37 0120 88 02 mov [bp+si],al ; buffer |= value & 0xff;38 0122 80 0A 80 or byte ptr[bp+si],80H; buffer |= 0x80;39 0125 EB E4 jmp l1 ; }40 0127 8A 02 l2: mov al,[bp+si] ; for ( ; ; ) {41 0129 AA stosb ; *dest++ = (unsigned char)buffer;42 012A 4E dec si ; _ if (buffer & 0x80)43 012B 0A C0 or al,al ;X_ buffer >>= 8;44 012D 78 F8 js l2 ; else45 012F C9 leave ; return;46 0130 C3 ret ; } // връщане с dest в DI (BX и SI са променени)

11. Цели числа с произволна дължина

В тази група са включени задачи за събиране, изваждане, смяна на знака, сравнение и преобразуване от и всимволен низ на цели числа, състоящи се от 16-битови думи, чийто брой се указва в регистър CX. Умножениетои деленето на такива числа не са включени в настоящия сборник поради голямата им сложност, но саразгледани частните случаи 32 × 32 и 64 : 32 бита – вж. зад. №№49–50 (тт. 13.4–13.5).

11.1. Събиране на цели числа с произволна дължина

Зад. 37. Да се напише подпрограма за събиране на 2 числа с по CX думи, указани от DS:SI и DS:DI, като записварезултата на мястото на второто число.

Решение: Задачата се решава лесно със събиране с пренос в прост цикъл.

Page 34: Сборник задачи по програмиране на Асемблер

34Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of ADDHUGE.COM 2011-07-30 11:46Source: ADDHUGE.A86 Page 1

1 ; събери числата с CX думи по DS:SI и DS:DI, сбора запиши на мястото на второто2 0100 F8 clc ; започни без пренос3 0101 AD a1: lodsw ; получи първото събираемо4 0102 11 05 adc [di],ax ; събери го с второто5 0104 47 inc di ; обнови указателя6 0105 47 inc di ; (ADD би променила CF!)7 0106 E2 F9 loop a1 ; повтори за всички думи8 0108 C3 ret

11.2. Изваждане на цели числа с произволна дължина

Зад. 38. Да се напише подпрограма за изваждане на 2 числа с по CX думи, указани от DS:SI и DS:DI, катопървото число се изважда от второто и резултатът се записва на мястото на второто число.

Решение: Задачата се решава аналогично на предходната .

A386 V4.05 assembly of SUBHUGE.COM 2011-07-30 11:46Source: SUBHUGE.A86 Page 1

1 ; извади числото с CX думи по DS:SI от това по DS:DI, разликата запиши по DS:DI2 0100 F8 clc ; започни без пренос3 0101 AD s1: lodsw ; получи умалителя4 0102 19 05 sbb [di],ax ; извади го от умаляемото5 0104 47 inc di ; обнови указателя6 0105 47 inc di ; (ADD би променила CF!)7 0106 E2 F9 loop s1 ; повтори за всички думи8 0108 C3 ret

11.3. Смяна на знака на цяло число с произволна дължина

Зад. 39. Да се напише подпрограма за смяна на знака на число с CX думи, указано от DS:SI.

Решение: В цикъла смяната на знака се извършва чрез отрицание и изваждане с пренос. Съществуваалтернативно решение с 2 команди по-малко (какво?).

A386 V4.05 assembly of NEGHUGE.COM 2011-07-30 11:46Source: NEGHUGE.A86 Page 1

1 ; смени знака на числото с CX думи по DS:SI2 0100 F7 1C neg word ptr [si] ; смени знака на най-младшата дума3 0102 49 dec cx ; още думи?4 0103 74 09 jz n2 ; не, изход5 0105 46 n1: inc si ; да, обнови указателя6 0106 46 inc si ; (ADD би променила CF!)7 0107 F7 14 not word ptr [si] ; смени знака на следващата дума в 2 етапа8 0109 83 1C FF sbb word ptr [si],-1 ; (допълнителният код = обратния код + 1)9 010C E2 F7 loop n1 ; повтори за всички думи10 010E C3 n2: ret

11.4. Сравнение на цели числа с произволна дължина

Зад. 40. Да се напише подпрограма за сравнение 2 на числа с по CX думи, указани от DS:SI и ES:DI. Присравнението второто число да се изважда от първото.

Решение: Задачата се решава с прост линеен алгоритъм чрез използване на командата „repe cmpsw“ от големитеадреси (старшите думи) към малките (младшите).

Page 35: Сборник задачи по програмиране на Асемблер

35Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of CMPHUGE.COM 2011-07-30 11:46Source: CMPHUGE.A86 Page 1

1 ; сравни числото с CX думи по DS:SI с това по ES:DI, изваждайки второто от първото2 0100 FD std ; ще се движим назад3 0101 8B D9 mov bx,cx ; копирай броя думи в BX, за да запазиш CX4 0103 4B dec bx ; премини към база 05 0104 D1 E3 shl bx,1 ; превърни броя думи в брой байтове6 0106 03 F3 add si,bx ; получи указатели към старшите думи7 0108 03 FB add di,bx8 010A F3 A7 repe cmpsw ; извърши сравнението9 010C FC cld ; възстанови DF10 010D C3 ret

11.5. Преобразуване от ASCIIZ-низ в цяло число с произволна дължина

Зад. 41. Да се напише подпрограма, която да преобразува ASCIIZ-низа от десетични цифри, указан от SI, в цялочисло без знак, което да запише в буфера, указан от DI, връщайки броя на думите му в CX.

Решение: Алгоритъмът е тип „цикъл в цикъл“. Във външния цикъл („g1“) се обработва по 1 цифра от низа наитерация, а във вътрешния („g2“) натрупаното число се умножава по 10 и към него се прибавя тази десетичнацифра. Обърнете внимание , че тъй като след командата „mul“ в DX се получава число от 0 до 9, след командата„adc dx,0“ никога няма пренос. Така всички думи на натрупваното число се умножават по 10, започвайки отнай-младшата, като към нея се добавя текущата десетична цифра, а към всяка следваща – старшата дума отпроизведението на предишната. Ако след края на вътрешния цикъл (т.е. след последната му итерацияумножение / събиране) старшата дума в DX не е 0, тя се добавя към натрупваното число и броят на думите му сеувеличава с 1.

A386 V4.05 assembly of GETHUGE.COM 2011-07-30 11:46Source: GETHUGE.A86 Page 1

1 ; превърни десетичен ASCIIZ-низ по SI в цяло число по DI, върни броя думи в CX2 0100 B9 01 00 mov cx,1 ; започни с 1 дума3 0103 C7 05 00 00 mov word ptr [di],0 ; изчисти натрупваната сума4 0107 AC g1: lodsb ; получи следващата ASCII-цифра5 0108 0A C0 or al,al ; NUL?6 010A 74 1E jz g4 ; да, изход7 010C 25 0F 00 and ax,1111b ; не, превърни в двоично-десетична цифра8 010F 93 xchg ax,bx ; съхрани като събираемо9 0110 57 push di ; съхрани указателя към числото10 0111 51 push cx ; съхрани броя думи11 0112 B8 0A 00 g2: mov ax,10 ; зареди множителя12 0115 F7 25 mul word ptr [di] ; умножи натрупваната сума13 0117 01 D8 add ax,bx ; добави текущата цифра14 0119 83 D2 00 adc dx,0 ; пренос от DX e невъзможен15 011C 89 D3 mov bx,dx ; съхрани събираемото16 011E AB stosw ; съхрани текущата дума на числото17 011F E2 F1 loop g2 ; извърши умножението и събирането на всички думи18 0121 59 pop cx ; възстанови броя думи19 0122 74 03 jz g3 ; ако най-старшата дума е 0, продължи20 0124 89 15 mov [di],dx ; иначе я съхрани21 0126 41 inc cx ; и увеличи броя на думите22 0127 5F g3: pop di ; възстанови указателя към числото23 0128 EB DD jmp g1 ; обработи следващата цифра24 012A C3 g4: ret

11.6. Преобразуване на цяло число с произволна дължина в ASCIIZ-низ

Зад. 42. Да се напише подпрограма, която да преобразува цялото число без знак от CX думи, указано от SI, вASCIIZ-низ от десетични цифри, който да запише в буфера, указан от DI.

Page 36: Сборник задачи по програмиране на Асемблер

36Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Решение: Първоначалната реализация е на Eric Isaacson (съавтор на асемблера на Intel „ASM86“ (т. 14.2.5) иавтор на асемблера „A386“ (т. 14.2.7), с който са асемблирани задачите в настоящия сборник). Алгоритъмът има3 цикъла. В първия („p1“) се прочита низът и се записва в стека в обратен ред (първо най-младшата дума). Въввтория („p2“) се обработва по 1 дума на итерация. В третия („p3“), който е вложен във втория, се извършваделенето на числото на 10. При всяка негова итерация частното се записва на мястото на делимото, а остатъкътсе използва като старша дума в следващата итерация. Остатъкът след края на цикъла на делене се преобразува вASCII-код и се записва като поредната десетична цифра. Така полученият низ започва с най-младшата изавършва с най-старшата цифра. Ето защо накрая му се прави огледално обръщане, като се извиква с командата„call“ включената с директивата „include“ подпрограма „strrev“.

Заб.: За да работи тази реализация е необходимо свободно стеково пространство = дължината на числото + 4допълнителни байта.

A386 V4.05 assembly of PUTHUGE.COM 2011-07-30 11:46Source: PUTHUGE.A86 Page 1

1 ; запиши като десетичен ASCIIZ-низ по DI двоичното число с CX думи по SI2 0100 89 CD mov bp,cx ; съхрани размера3 0102 AD p1: lodsw ; получи следващата дума4 0103 50 push ax ; запиши дума в стековия буфер (записват се в обратен ред:5 0104 E2 FC loop p1 ; най-старшата дума е в началото, а най-младшата - в края)6 0106 8B F4 mov si,sp ; указател към началото на стековия буфер7 0108 89 E9 mov cx,bp ; установи брояча на външния цикъл8 010A BB 0A 00 mov bx,10 ; база на бройната система за цифрите в изходящия низ9 010D 57 push di ; съхрани указателя към низа10 010E EB 12 jmp short p4; започни със скок в тялото на външния цикъл11 0110 51 p2: push cx ; съхрани броя на оставащите думи12 0111 AD p3: lodsw ; извлечи думата от числото13 0112 F7 F3 div bx ; раздели (остатък:дума) на базата14 0114 89 44 FE mov [si-2],ax;съхрани частното обратно в буфера15 0117 E2 F8 loop p3 ; продължи с деленето на следващата дума16 0119 92 xchg ax,dx ; прехвърли остатъка (последната цифра) в AL17 011A 04 30 add al,'0' ; преобразувай в ASCII18 011C AA stosb ; запиши десетичната цифра19 011D 59 pop cx ; възстанови броя думи20 011E 29 CE sub si,cx ; върни указателя на числото към следващата по-младша дума21 0120 29 CE sub si,cx ; трябват 2 изваждания, тъй като CX е брой думи22 0122 33 D2 p4: xor dx,dx ; първоначалният остатък е нула23 0124 39 14 cmp [si],dx ; старшата дума на числото нула ли е?24 0126 75 E8 jnz p2 ; влез в цикъла, ако не е, за да разпакетираш друга цифра25 0128 83 C6 02 add si,2 ; придвижи указателя до следващата старша дума26 012B E2 E3 loop p2 ; преброй назад оставащите думи, прод. за всяка изх. цифра27 012D 5E pop si ; възстанови първоначалния указател към изходящия низ28 012E D1 E5 shl bp,1 ; преобразувай броя думи в брой байтове29 0130 03 E5 add sp,bp ; освободи временния буфер в стека30 0132 C6 05 00 mov byte ptr [di],0 ; терминирай изходящия низ31 0135 E8 01 00 call strrev ; и го обърни наопаки, за да е с нормален ред на цифрите32 0138 C3 ret33 strrev:34 include strrev.a8635 i ; обърни огледално (наопаки) низа, указан от DS:SI и завършващ по DS:[DI-1]36 0139 4F i l0: dec di ; придвижи „опашката“ назад37 013A AC i lodsb ; получи следв. символ, придвижи „главата“ напред38 013B 86 05 i xchg [di],al ; размени символите в „главата“ и „опашката“39 013D 88 44 FF i mov [si-1],al40 0140 3B F7 i cmp si,di ; срещнали ли са се указателите?41 0142 72 F5 i jb l0 ; не, продължи42 0144 C3 i ret ; да, върни се

Зад. 43. Да се напише подпрограма, която да обръща огледално символния низ, започващ на адрес DS:SI изавършващ на DS:[DI−1].

Решение: Реализацията с прост цикличен алгоритъм е отново на Eric Isaacson и е дадена в края на листинга напредходната задача.

Заб.: Мястото на тази задача е в раздела за обработка на низове (вж. т. 6.10), но тъй като се извиква отпредходната , тя е дадена тук.

Page 37: Сборник задачи по програмиране на Асемблер

37Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

12. Преобразуване на числа от една бройна система в друга

В тази група са включени задачи за преобразуване на въведено като низ десетично число в изведено като низшестнадесетично и обратно. Те са подобни на зад. №№41–42 (тт. 11.5–11.6), но са само за 32-битови числа ивсяка включва както преобразуване на низ в число, така и на число в низ.

12.1. Преобразуване от десетична в шестнадесетична бройна система

Зад. 44. Да се напише подпрограма, която да преобразува в шестнадесетична бройна система и извежда като низ32-битово число, въведено като низ в десетична бройна система.

Решение: Задачата се състои от 4 части: (1) въвеждане на число в десетична бройна система като низ от ASCII-цифри, (2) преобразуване на низа в двоично число, (3) преобразуване на числото в шестнадесетична бройнасистема като низ от ASCII-цифри и букви от A до F и (4) извеждане на получения низ. За въвеждане иизвеждане на низа се използват функции на ДОС съответно 0Ah и 9. Низът и в двата случая е в буфера „buf“.Първият байт на буфера указва дължината му. Във втория байт функция 0Ah записва дължината на въведенияниз. Символът „$“ е терминатор на извеждания низ от функция 9. Алгоритъмът се състои от 3 цикъла („L1“–„L3“), като „L3“ е вложен в „L2“. В първия цикъл („L1“) се получава двоичното число от входящия низ чрезпоследователно умножение по 10 на натрупваната сума и добавяне на поредната цифра, преобразувана отASCII-код. Във втория цикъл („L2“) се получава изходящият низ от числото чрез делене на 16 и запис в низа наостатъците от деленията, превърнати в ASCII-код. Низът се записва от края, т.е. се получава дясноподравняване .

Заб. 1: 5-байтовата група команди за преобразуване на 1 тетрада в ASCII-код по шестнадесетичната бройнасистема „cmp al,10 / sbb al,69h / das“ може да се замени с друга 5-байтова: „daa / add al,−10h / adc al,40h“ (защо?).

Заб. 2: По метода от тази задача може да се напише вариант на зад. 42 (т. 11.6) със запис на низа вшестнадесетична вместо в десетична бройна система (как?).

Page 38: Сборник задачи по програмиране на Асемблер

38Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of DEC2HEX.COM 2011-07-30 11:46Source: DEC2HEX.A86 Page 1

1 ; Въведи десетичен ASCII-низ, преобразувай го в шестнадесетичен и го изведи2 0100 BA 55 01 mov dx,offset buf3 0103 B4 0A mov ah,0ah4 0105 CD 21 int 21h ; входящ низ5 0107 42 inc dx ; указател към дължината на низа6 0108 89 D6 mov si,dx ; подготви указателя на източника7 010A 33 C9 xor cx,cx8 010C 8A 0C mov cl,[si] ; зареди байта на дължината на низа9 010E E3 44 jcxz l4 ; избегни 65536-те итерации, ако низът е празен10 0110 46 inc si ; укажи към началото на низа11 0111 BB 0A 00 mov bx,10 ; зареди множителя12 0114 33 C0 xor ax,ax ; нулирай младшата дума на натрупваната сума13 0116 33 D2 xor dx,dx ; и старшата дума14 0118 80 2C 30 l1: sub byte ptr [si],'0';преобразувай от ASCII в двоично-десетичен код15 011B 50 push ax ; съхрани младшата дума16 011C 92 xchg ax,dx ; прехвъри сташата дума в множимото17 011D F7 E3 mul bx ; получи 10 * старшата дума18 011F 95 xchg ax,bp ; съхрани това произведение19 0120 58 pop ax ; възстанови младшата дума20 0121 F7 E3 mul bx ; получи 10 * младшата дума21 0123 02 04 add al,[si] ; добави цифрата22 0125 80 D4 00 adc ah,023 0128 11 EA adc dx,bp ; и съхраненото произведение 10 * старшата дума24 012A 46 inc si ; укажи към следващия символ на низа25 012B E2 EB loop l1 ; повтаряй до края на низа26 012D BF 61 01 mov di,offset buf+12; укажи към края на изходящия низ27 0130 50 l2: push ax ; съхрани акумулатора28 0131 24 0F and al,1111b ; изолирай младшата тетрада29 0133 3C 0A cmp al,10 ; отличи цифрите от буквите30 0135 1C 69 sbb al,69h ; 0-9 => 96h-9Fh, 0Ah-0Fh => 0A1h-0A6h31 0137 2F das ; извади 66h от цифрите или 60h от буквите32 0138 88 05 mov [di],al ; съхрани 30h-39h ('0'-'9') или 41h-46h ('A'-'F')33 013A 4F dec di ; укажи към предходния символ (придвижи се назад)34 013B 58 pop ax ; възстанови акумулатора35 013C B9 04 00 mov cx,4 ; раздели натрупаната сума на 1636 013F D1 EA l3: shr dx,137 0141 D1 D8 rcr ax,138 0143 E2 FA loop l339 0145 8B CA mov cx,dx ; подготви проверката за нула40 0147 0B C8 or cx,ax ; частното нула ли е?41 0149 75 E5 jnz l2 ; не, продължи42 014B C6 05 0A mov byte ptr [di],10; вмъкни символ за нов ред (LF) в началото43 014E 89 FA mov dx,di ; зареди указателя към низа за ДОС44 0150 B4 09 mov ah,945 0152 CD 21 int 21h ; изведи го46 0154 C3 l4: ret ; завърши програмата47 0155 0B 20 20 20 20 buf db 11,' $'

12.2. Преобразуване от шестнадесетична в десетична бройна система

Зад. 45. Да се напише подпрограма, която да преобразува в десетична бройна система и извежда като низ 32-битово число, въведено като низ в шестнадесетична бройна система.

Решение: Задачата е обратна на предходната и има същата структура. Особеностите тук са 2. Първо, входящиятниз се проверява както за малки („a–f“), така и за главни букви („A–F“). Второ, при деленето на 10 на двоичноточисло остатъкът от предходното делене служи като старша част на делимото в следващото делене.

Заб.: По метода от тази задача може да се напише вариант на зад. 41 (т. 11.5) с четене на низа в шестнадесетичнавместо в десетична бройна система (как?).

Page 39: Сборник задачи по програмиране на Асемблер

39Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

AA386 V4.05 assembly of HEX2DEC.COM 2011-07-30 11:46Source: HEX2DEC.A86 Page 1

1 ; Въведи шестнадесетичен ASCII-низ, преобразувай го в десетичен и го изведи2 0100 BA 57 01 mov dx,offset buf3 0103 B4 0A mov ah,0ah4 0105 CD 21 int 21h ; входящ низ5 0107 BE 59 01 mov si,offset buf+2 ; указател към входящия низ6 010A 33 C9 xor cx,cx7 010C 8A 0E 58 01 mov cl,[buf+1] ; зареди байта на дължината на низа8 0110 E3 44 jcxz l5 ; избегни 65536-те итерации, ако низът е празен9 0112 33 DB xor bx,bx ; нулирай младшата дума на натрупваната сума10 0114 33 D2 xor dx,dx ; и старшата дума11 0116 AC l1: lodsb ; зареди следващия символ12 0117 A8 40 test al,1000000b ; буква?13 0119 74 02 jz l2 ; не, цифра14 011B 2C 07 sub al,'A'-('9'+1) ; да, превърни в "16-ична ASCII цифра" (3Ah-3Fh)15 011D 24 0F l2: and al,1111b ; преобразувай в двоична тетрада (16-ична цифра)16 011F B4 04 mov ah,4 ; зареди броя на изместванията17 0121 D1 E3 l3: shl bx,1 ; измести младшата дума на сбора18 0123 D1 D2 rcl dx,1 ; и старшата дума19 0125 FE CC dec ah ; готово ли е?20 0127 75 F8 jnz l3 ; не, измествай по-нататък21 0129 01 C3 add bx,ax ; да, прибави шестнадесетичната цифра22 012B 83 D2 00 adc dx,023 012E E2 E6 loop l1 ; повтаряй до края на низа24 0130 93 xchg ax,bx ; прехвърли младшата дума в AX25 0131 8B CA mov cx,dx ; и старшата в CX26 0133 BF 62 01 mov di,offset buf+11; укажи към края на изходящия низ27 0136 BB 0A 00 mov bx,10 ; зареди делителя28 0139 33 D2 l4: xor dx,dx ; нулирай ст. дума на делимото на следващия „div“29 013B 91 xchg ax,cx ; съхрани мл. дума в CX, старшата - в AX (делимо)30 013C F7 F3 div bx ; раздели старшата дума на 10 (остатъкът - в DX)31 013E 91 xchg ax,cx ; ст. дума на частното в CX, възстанови мл. в AX32 013F F7 F3 div bx ; мл. дума на частното в AX, 10-ичната цифра в DX33 0141 80 C2 30 add dl,'0' ; преобразувай тази цифра в ASCII34 0144 88 15 mov [di],dl ; съхрани в изходящия низ35 0146 4F dec di ; укажи към предходния символ (придвижи се назад)36 0147 8B D1 mov dx,cx ; подготви проверката за нула37 0149 09 C2 or dx,ax ; частното нула ли е?38 014B 75 EC jnz l4 ; не, продължи39 014D C6 05 0A mov byte ptr [di],10; вмъкни символ за нов ред (LF) в началото40 0150 89 FA mov dx,di ; зареди указателя към низа за ДОС41 0152 B4 09 mov ah,942 0154 CD 21 int 21h ; изведи го43 0156 C3 l5: ret ; завърши програмата44 0157 09 20 20 20 20 buf db 9,' $'

13. Операции върху числа с двойна и четворна разрядност

В тази група са включени задачи за извличане на квадратен корен от 32-битово число, умножение на две 32-битови числа и делене на 64-битово число на 32-битово.

13.1. Извличане на квадратен корен

Зад. 46. Да се напише подпрограма, която да връща в регистър AX цялата част на квадратния корен на цялоточисло без знак в DX:AX.

Решение: Алгоритъмът на език C е даден като коментар в листинга на програмата. Той е цикличен сразклонение в тялото на цикъла. Обърнете внимание как се правят побитови измествания на 32-битовипроменливи („a“, „rem“ и „root“), разположени в по два 16-битови регистъра. Освен това, въпреки чепроменливата „root“ е 32-битова, от нея накрая се използват само 16 бита. Тъй като използваните операции сасамо побитово изместване, събиране и изваждане, е възможно по същия алгоритъм да се напише и подпрограмаза получаване на 32-битов квадратен корен от 64-битово число (как?), макар че в този случай ще са необходимии локални променливи в паметта – вж. зад. №50 (в т. 13.5).

Заб.: Автор на алгоритъма е Jack Crenshaw – вж. статията му „Integer Square Roots“ в сп. „Embedded SystemsProgramming“, бр. 2 / 1998 г., стр. 15–32.

Page 40: Сборник задачи по програмиране на Асемблер

40Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of SQRT.COM 2011-07-30 11:46Source: SQRT.A86 Page 1

1 ; върни в AX цялата част на квадратния корен на цялото число без знак в DX:AX2 ; („Integer Square Roots“ от Jack Crenshaw, „Embedded Systems Programming“, 02/98)3 0100 33 ED xor bp,bp ; unsigned short sqrt(unsigned long a)4 0102 33 DB xor bx,bx ;\ {5 0104 33 F6 xor si,si ; \_ BP:BX unsigned long rem = 0;6 0106 33 FF xor di,di ; SI:DI unsigned long root = 0;7 0108 B9 10 00 mov cx,16 ; for (int i = 0; i < 16; i++) {8 010B D1 E7 l1: shl di,1 ; root <<= 1;9 010D D1 D6 rcl si,110 010F D1 E3 shl bx,1 ; rem = (rem << 2) + (a >> 30);11 0111 D1 D5 rcl bp,112 0113 D1 E3 shl bx,113 0115 D1 D5 rcl bp,114 0117 52 push dx15 .18616 0118 C1 EA 0E shr dx,14 ; старша дума >> (30 - 16)17 011B 03 DA add bx,dx18 011D 5A pop dx19 011E 83 D5 00 adc bp,020 0121 D1 E0 shl ax,1 ; a <<= 2;21 0123 D1 D2 rcl dx,122 0125 D1 E0 shl ax,123 0127 D1 D2 rcl dx,124 0129 83 C7 01 add di,1 ; root++;25 012C 83 D6 00 adc si,026 012F 39 EE cmp si,bp ; if (root <= rem) {27 0131 72 06 jb l228 0133 77 10 ja l329 0135 3B FB cmp di,bx30 0137 77 0C ja l331 0139 2B DF l2: sub bx,di ; rem -= root;32 013B 19 F5 sbb bp,si33 013D 83 C7 01 add di,1 ; root++;34 0140 83 D6 00 adc si,035 0143 EB 06 jmp short l4 ; } else36 0145 83 EF 01 l3: sub di,1 ; root--;37 0148 83 DE 00 sbb si,038 014B E2 BE l4: loop l1 ; }39 014D D1 EE shr si,1 ; return (unsigned short)(root >> 1);40 014F D1 DF rcr di,141 0151 97 xchg ax,di42 0152 C3 ret ; }

13.2. Беззнаково умножение на две 32-битови числа

Зад. 47. Да се напише подпрограма, която да връща в регистрите DX:CX:BX:AX 64-битовото произведение на32-битовите цели числа без знак в DX:CX и BX:AX.

Решение: Едно от най-простите възможни решения е с цикличен алгоритъм с разклонение в тялото на цикъла.Във всяка итерация се проверява по 1 бит от множителя и при лог. 1 множимото се добавя към частичнотопроизведение, което се измества с 1 бит надясно. Така постепенно множителят се измества от младшата двойнадума на произведението .

Заб.: Алгоритъмът е взаимстван от книгата на Камен Фильов, Иван Зарков и Николай Велчев „Големи МОСинтегрални схеми“, издателство „Техника “, София, 1979 г., стр. 294.

Page 41: Сборник задачи по програмиране на Асемблер

41Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of MULLUSA.COM 2011-07-30 11:46Source: MULLUSA.A86 Page 1

1 ; Умножи без знак DX:CX (Y1:Y0) на BX:AX (Z1:Z0), резултат в DX:CX:BX:AX (A:B:C:D)2 0100 33 FF xor di,di ; изчисти старшата двойна дума на резултата3 0102 33 F6 xor si,si4 0104 87 FA xchg dx,di ; DI = Y1, DX = 05 0106 87 F1 xchg cx,si ; SI = Y0, CX = 06 0108 BD 20 00 mov bp,32 ; установи брояча на цикъла7 010B D1 DB rcr bx,18 010D D1 D8 rcr ax,1 ; най-младшият бит на множителя = 1 ?9 010F 73 04 l1: jnc l2 ; не, прескочи10 0111 01 F1 add cx,si ; да, добави множимото към частичното произведение11 0113 13 D7 adc dx,di12 0115 D1 DA l2: rcr dx,1 ; измести частичното произведение13 0117 D1 D9 rcr cx,1 ; (65-бита)14 0119 D1 DB rcr bx,115 011B D1 D8 rcr ax,116 011D 4D dec bp ; 32 пъти през цикъла?17 011E 75 EF jnz l1 ; не, продължи18 0120 C3 ret ; да, изход

13.3. Умножение 32 × 32 бита с приоритет на бързодействието

Зад. 48. Да се реши предходната задача с използването на бързодействащ алгоритъм.

Решение: Решението е линеен алгоритъм с умножение на всяка от думите на множителя с всяка на множимотои събиране – всичко 4 умножения и 8 събирания на 16-битови числа.

Заб. 1: Въпреки че умножението е сравнително бавна операция, 4 умножения тук се изпълняват значително по-бързо от 130-те побитови измествания в предходната задача. Например на микропроцесор „Pentium-III“ (1,2GHz) подпрограмата се изпълнява средно за 73 машинни цикъла, а предходният вариант с изместването исъбирането – за около 700, т.е. около 9,6 пъти по-бавно!

Заб. 2: Алгоритъмът е взаимстван от книгата на Peter Norton „Advanced Assembly Language“, издателство„Brady“, Ню Йорк, 1991 г., стр. 229–230.

A386 V4.05 assembly of MULLU.COM 2011-07-30 11:46Source: MULLU.A86 Page 1

1 ; Умножи без знак DX:CX (Y1:Y0) по BX:AX (Z1:Z0), резултат в DX:CX:BX:AX (A:B:C:D)2 0100 8B F0 mov si,ax ; Z03 0102 89 D7 mov di,dx ; Y14 0104 52 push dx ; съхрани Y15 0105 F7 E1 mul cx ; Z0 * Y06 0107 96 xchg ax,si ; SI = D = мл.д.(Z0 * Y0), AX = Z07 0108 87 FA xchg dx,di ; DI = C = ст.д.(Z0 * Y0), DX = Y18 010A F7 E2 mul dx ; Z0 * Y19 010C 33 ED xor bp,bp ; A = 010 010E 01 C7 add di,ax ; C = ст.д.(Z0 * Y0) + мл.д.(Z0 * Y1)11 0110 11 EA adc dx,bp ; ст.д.(Z0 * Y1)12 0112 13 ED adc bp,bp ; A13 0114 87 CA xchg dx,cx ; CX = B = ст.д.(Z0 * Y1), DX = Y014 0116 8B C3 mov ax,bx ; Z115 0118 F7 E2 mul dx ; Z1 * Y016 011A 01 C7 add di,ax ; C = ст.д.(Z0 * Y0) + мл.д.(Z0 * Y1) + мл.д.(Z1 * Y0)17 011C 11 D1 adc cx,dx ; B = ст.д.(Z0 * Y1) + ст.д.(Z1 * Y0)18 011E 83 D5 00 adc bp,0 ; A19 0121 97 xchg ax,di ; AX = C20 0122 93 xchg ax,bx ; AX = Z1, BX = C21 0123 5A pop dx ; възстанови Y122 0124 F7 E2 mul dx ; Z1 * Y123 0126 03 C8 add cx,ax ; B = ст.д.(Z0 * Y1) + ст.д.(Z1 * Y0) + мл.д.(Z1 * Y1)24 0128 11 EA adc dx,bp ; A = ст.д.(Z1 * Y1)25 012A 96 xchg ax,si ; AX = D26 012B C3 ret

Page 42: Сборник задачи по програмиране на Асемблер

42Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

13.4. Беззнаково делене на 64-битово число на 32-битово

Зад. 49. Да се напише подпрограма, която да връща в регистри DX:AX частното от беззнаковото делене на 64-битово число (аргументи u1:u0) на 32-битово (аргумент v). Остатъкът да се запише по адрес, указан от аргументаr, ако r ≠ 0. Аргументите се предават чрез стека по конвенцията за език C.

Решение: Едно от най-простите възможни решения е с цикличен алгоритъм с разклонение в тялото на цикъла,подобно на зад. №47 (в т. 13.2), но с изместване наляво вместо надясно и изваждане на делителя вместосъбиране на множимото.

Заб. 1: В листинга (даден в т. 14, където са обяснени директивите) има 1 неизползван етикет, който е излишен иможе да се премахне (кой?).

Заб. 2: Алгоритъмът е взаимстван от книгата на Peter Norton „Advanced Assembly Language“, издателство„Brady“, Ню Йорк, 1991 г., стр. 230–237. Но там има 1 грешка, която тук е поправена (как?).

13.5. Деление 64 : 32 бита с приоритет на бързодействието

Зад. 50. Да се реши предходната задача с използването на бързодействащ алгоритъм.

Решение: Решението е сложен разклонен алгоритъм с 8 умножения и 2 деления, който е даден на език C вкоментара на листинга на програмата (вж. т. 14). Той се състои от 9 главни части: (1) проверка за препълване,(2) нормализация на делителя, (3) разполовяване на делителя, (4) нормализация на делимото, (5) разполовяванена младшата му половина , (6) изчисляване и корекция на старшата част на частното, (7) умножение на тази частпо делителя и изваждане от старшата част на делимото, (8) изчисляване и корекция на младшата част начастното и (9) денормализация на остатъка. Подробното описание на този алгоритъм е дадено в литературата,цитирана в заб. №2.

Заб. 1: Въпреки че умножението и деленето са сравнително бавни операции, 8-те умножения и двете делениятук се изпълняват значително по-бързо от 384-те побитови измествания в предходната задача. Например намикропроцесор „Pentium-III“ (1,2 GHz) подпрограмата се изпълнява средно за 460 машинни цикъла, апредходният вариант с изместването и изваждането – за около 1500, т.е. около 3,26 пъти по-бавно!

Заб. 2: Алгоритъмът е взаимстван от книгата на Henry Warren „Hacker’s Delight“, раздел 9-4. Той е версия сразгърнат цикъл като частен случай на алгоритъма на Donald Knuth за целочислено делене с произволнадължина – вж. книгата му „The Art of Computer Programming“, том 2, раздел 4.3.1, алгоритъм D.

14. Асемблери, директиви и синтаксис на командите

За съжаление аналог на стандартите на езика C като ANSI C и C-99 при езика Асемблер за x86 няма, а има „дефакто“ стандарт – „Microsoft Macro Assembler“ (MASM). Повечето от другите асемблери са съвместими с някояот версиите на MASM, но и някои от несъвместимите (напр. NASM) имат своите предимства и приложение . Втози раздел са разгледани 20 асемблера: QASM, MASM, WASM, JWASM, LASM, TASM, PASM, ALP, ASM,TASMB, TASMC, OPTASM, ASM86, A86, A386, 386|ASM, LZASM, NASM, NBASM и RASM-86. Разгледани са идирективите им, използвани в зад. №№49–50 (тт. 13.4–13.5), както и разликите в синтаксиса на командите .

Заб. 1: В горния списък асемблерите, съвместими с MASM версия 5.1 или по-нова, са дадени в получер шрифт,а несъвместимите с MASM са подчертани.

Заб. 2: В списъка са включени само асемблери, които могат да създават 16-битов обектен файл във формат„OMF“ за свързване с обектния файл на извикващата програма. Това изключва такива известни асемблери катоGAS и YASM например.

Заб. 3: За всеки от съвместимите с MASM асемблери (без WASM) в следващите подраздели (тт. 14.1 и 14.2) сададени по 2 генерирани от него листинга на един и същ (с изключение на директивата „page“) за целияподраздел изходен текст. Първият е на зад. №49 (т. 13.4), а вторият – на зад. №50 (т. 13.5). За икономия на мястоот края на листингите са премахнати списъците на имената, сегментите и т.н., които асемблерите генерират.

Page 43: Сборник задачи по програмиране на Асемблер

43Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

14.1. Съвместими с MASM версия 5.1 или по-нова

В тази версия на решенията на зад. №№49–50 (тт. 13.4–13.5) се използват следните директиви на MASM 5.1+:„.model“, „.code“, „public“, „proc“, „local“, „endp“ и „end“. Някои от тях („.model“, „proc“ и „local“) са отвисоко ниво. Те позволяват на програмиста да се съсредоточи върху същността на задачата. За подробностите(задаване параметри на сегментите, предаване на агрументите, адресация на аргументите и локалнитепроменливи , съхранение на използваните регистри и др.) се грижи асемблерът.

„.model“: задава модела на паметта, езика, на който съответстват конвенциите за именуване и извикванена подпрограми и др. В случая модел „small“ (малък) означава, че сегментният регистър CS адресираединствения кодов сегмент на програмата, а DS, ES и SS имат еднаква стойност и адресират един общданнов сегмент. А език „c“ означава, че пред „публичните “ имена автоматично се вмъква подчертаващотире („_“) и аргументите на функциите се записват в стека в обратен ред, а той не се възстановява отизвикваните функнции .„.code“: задава типа сегмент (кодов), към който да принадлежат редовете след директивата (аналогично„.data“ означава, че следващите редове са за даннов сегмент и т.н.).„public“: задава списък от „публично“ достъпните извън дадения модул имена на подпрограми илиданни, които да се обявят в обектния му файл, – в случая това е само „divlu2“.„proc“: задава името, използваните регистри, аргументите и техния тип на подпрограма (процедура). Чрезключовата дума „uses“ се задава списък на използваните от подпрограмата регистри (в случая SI и DI), закоито асемблерът автоматично генерира команди „push“ за съхранение в началото („пролога“) и „pop“ завъзстановяване в края („епилога“) на подпрограмата. По-нататък се изброяват аргументите и технитетипове (в случая 3 аргумента тип „dword“ (двойна дума) и 1 тип „word“ (дума). Асемблерът автоматичногенерира код за създаване на кадър в стека („push bp / mov bp,sp“) в началото и за премахването му („popbp“) в края на подпрограмата. Освен това той автоматично адресира всеки аргумент с базова адресация порегистър BP с отместване – например в случая „u1“ се адресира по отместване 4, „u0“ по отместване 8 ит.н.„local“: задава списък на локалните променливи – в случая в зад. №50 (т. 13.5) това са 6 аргумента тип„word“ (дума) и 2 тип „dword“ (двойна дума). Подобно на аргументите, асемблерът генерира код зазаемане на стеково пространство за локалните променливи в началото („sub sp,20“) и за освобождаванетому („mov sp,bp“) в края на подпрограмата. Също така той автоматично адресира всяка локална променливас базова адресация по регистър BP с отрицателно отместване – например в случая „un1“ се адресира сотместване −2, „un0“ с отместване −4 и т.н.„endp“: задава края на подпрограмата, чието начало е зададено с „proc“.„end“: задава края на изходния текст във файла.

Заб. 1: Три от променливите в реализацията на език C на зад. №50 (т. 13.5), всяка за определено време, серазполагат в регистри – „un10“ (DX:AX), „rhat“ (BX:CX) и „s“ (SI). Ето защо ги няма в списъка на локалнитепроменливи на директивата „local“.

Заб. 2: Директивата „page“ е само за управление на листинга (т.е. не се изисква за генериране на обектен файл)и не е включена в горния списък.

14.1.1. MicrosoftQuickAssembler (QASM)

Този малко познат асемблер се появява като допълнение към интегрираната среда „QuickC“ на фирматаMicrosoft за първи път във версията ѝ 2.01 (1989 г.). Той има същите възможности като MASM версия 5.1 (вж. т.14.1.2). Но за разлика от всички останали асемблери, той не е във вид на изпълним файл (тип „.EXE“ или„.COM“), а във вид на т.н. „overlay“ (файл тип „.OVL“). Това позволява изпълнението му както отинтегрираната среда, така и от командния ред с команда „QCL“. В дадените по-долу листинги той е извикан откомандния ред с ключове „–l –Sa –P2“, които задават съответно генерация на листинг, максимизация налистинга и 2 паса на асемблера.

Заб.: В максимизирания листинг се включват и автоматично генерираните от асемблера директиви и команди .Тук те са с главни букви, за разлика от директивите в изходния текст, които са с малки букви.

Page 44: Сборник задачи по програмиране на Асемблер

44Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Microsoft (R) QuickAssembler for QuickC Version 2.51 08/14/11 10:44:47 Page 1-1

page 255,125.model small,c.CODE

0000 _TEXT SEGMENT 'CODE'.DATA

0000 @CurSeg ENDS0000 _DATA SEGMENT 'DATA' DGROUP GROUP @CurSeg0000 @CurSeg ENDS ASSUME cs:@code,ds:@data,ss:@data

.code0000 _TEXT SEGMENT 'CODE'

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public divlu20000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word0000 55 PUSH bp0001 8B EC MOV bp,sp0003 57 PUSH DI0004 56 PUSH SI0005 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в0008 8B 56 0E mov dx,word ptr v+2000B 39 46 04 cmp word ptr u1,ax000E 8B 5E 06 mov bx,word ptr u1+20011 1B DA sbb bx,dx0013 72 12 jb l20015 8B 5E 10 mov bx,r0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност001A B8 FFFF mov ax,-1 ;ffffH001D 74 05 jz l1001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0021 89 47 02 mov [bx+2],ax0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0025 EB 40 jmp short l8 ; }0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод!002A 33 C0 xor ax,ax002C 33 D2 xor dx,dx002E D1 66 08 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво0031 D1 56 0A rcl word ptr u0+2,10034 D1 56 04 rcl word ptr u1,10037 D1 56 06 rcl word ptr u1+2,1003A D1 D0 rcl ax,1003C D1 D2 rcl dx,1003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)0040 3B 46 0C cmp ax,word ptr v ; сравни DX:AX и делителя0043 8B DA mov bx,dx0045 1B 5E 0E sbb bx,word ptr v+2 ; DX:AX < делителя ?0048 72 09 jb l5 ; да, прескочи004A 2B 46 0C l4: sub ax,word ptr v ; не, извади делителя004D 1B 56 0E sbb dx,word ptr v+20050 FF 46 08 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти0055 8B 5E 10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го.0058 0B DB or bx,bx005A 74 05 jz l7005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX005E 89 57 02 mov [bx+2],dx0061 8B 46 08 l7: mov ax,word ptr u00064 8B 56 0A mov dx,word ptr u0+20067 5E POP SI0068 5F POP DI0069 5D POP bp006A C3 l8: ret006B divlu2 endp ; }

Page 45: Сборник задачи по програмиране на Асемблер

45Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

end006B @CurSeg ENDS

Page 46: Сборник задачи по програмиране на Асемблер

46Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Microsoft (R) QuickAssembler for QuickC Version 2.51 08/14/11 10:44:49 Page 1-1

page 255,125.model small,c.CODE

0000 _TEXT SEGMENT 'CODE'.DATA

0000 @CurSeg ENDS0000 _DATA SEGMENT 'DATA' DGROUP GROUP @CurSeg0000 @CurSeg ENDS ASSUME cs:@code,ds:@data,ss:@data

.code0000 _TEXT SEGMENT 'CODE'

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public divlu20000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word

; unsigned short un1, un0, // Нормализирани младши цифри на делимото.; vn1, vn0, // Нормализирани цифри на делителя.; q1, q0; // Цифри на частното.; unsigned long un32, un21, un10, // Двойки цифри на делимото.; rhat; // Остатък.; int s; // Колко бита изместваме за нормализация.

local un1:word,un0:word,vn1:word,vn0:word,q1:word,q0:word,un32:dword,un21:dword0000 55 PUSH bp0001 8B EC MOV bp,sp0003 83 EC 14 SUB sp,20d0006 57 PUSH DI0007 56 PUSH SI0008 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в000B 8B 56 0E mov dx,word ptr v+2000E 39 46 04 cmp word ptr u1,ax0011 8B 5E 06 mov bx,word ptr u1+20014 1B DA sbb bx,dx0016 72 13 jb l20018 8B 5E 10 mov bx,r001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност001D B8 FFFF mov ax,-1 ;ffffH0020 74 05 jz l10022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0024 89 47 02 mov [bx+2],ax0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0028 E9 0179 jmp l24 ; }002B 8B F2 l2: mov si,dx ; if (v == 0)002D 0B F0 or si,ax002F BE 0020 mov si,32 ;0020H ; s = 32;0032 74 4C jz l7 ; else { // 0 <= s <= 31.0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя.0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) {0038 75 06 jne l3003A BE 0010 mov si,16 ;0010H ; s += 16;003D 92 xchg ax,dx ; v <<= 16;003E 33 C0 xor ax,ax0040 0A F6 l3: or dh,dh ; }0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) {0044 83 C6 08 add si,8 ; s += 8;0047 8A F2 mov dh,dl ; v <<= 8;0049 8A D4 mov dl,ah004B 8A E0 mov ah,al004D 2A C0 sub al,al004F 80 FE 0F l4: cmp dh,0Fh ; }0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) {0054 83 C6 04 add si,4 ; s += 4;0057 D1 E0 shl ax,1 ; v <<= 4;0059 D1 D2 rcl dx,1

Page 47: Сборник задачи по програмиране на Асемблер

47Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

005B D1 E0 shl ax,1005D D1 D2 rcl dx,1005F D1 E0 shl ax,10061 D1 D2 rcl dx,10063 D1 E0 shl ax,10065 D1 D2 rcl dx,10067 80 FE 3F l5: cmp dh,3Fh ; }006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) {006C 83 C6 02 add si,2 ; s += 2;006F D1 E0 shl ax,1 ; v <<= 2;0071 D1 D2 rcl dx,10073 D1 E0 shl ax,10075 D1 D2 rcl dx,10077 0B D2 l6: or dx,dx ; }0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) {007B 46 inc si ; s++;007C D1 E0 shl ax,1 ; v <<= 1;007E D1 D2 rcl dx,10080 89 46 0C l7: mov word ptr v,ax ; }0083 89 56 0E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри.0086 89 56 FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16);0089 89 46 F8 mov vn0,ax ; vn0 = (unsigned short)v;008C 8B CE mov cx,si ; un32 = (u1 << s) |008E 8B 5E 04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31));0091 8B 7E 06 mov di,word ptr u1+20094 E3 06 jcxz l90096 D1 E3 l8: shl bx,10098 D1 D7 rcl di,1009A E2 FA loop l8009C B1 20 l9: mov cl,32 ;0020H009E 2B CE sub cx,si00A0 8B 46 08 mov ax,word ptr u000A3 8B 56 0A mov dx,word ptr u0+200A6 74 06 jz l1100A8 D1 EA l10: shr dx,100AA D1 D8 rcr ax,100AC E2 FA loop l1000AE 0B F6 l11: or si,si00B0 74 04 jz l1200B2 0B D8 or bx,ax00B4 0B FA or di,dx00B6 89 5E F0 l12: mov word ptr un32,bx00B9 89 7E F2 mov word ptr un32+2,di00BC 8B 46 08 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво.00BF 8B 56 0A mov dx,word ptr u0+200C2 8B CE mov cx,si00C4 E3 06 jcxz l1400C6 D1 E0 l13: shl ax,100C8 D1 D2 rcl dx,100CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри.00CC 89 56 FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16);00CF 89 46 FC mov un0,ax ; un0 = (unsigned short)un10;00D2 3B 7E FA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване00D5 C7 46 F6 FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1.00DC 8B 46 F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1);00DF 8B 56 F2 mov dx,word ptr un32+200E2 F7 76 FA div vn100E5 89 46 F6 mov q1,ax00E8 F7 66 FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1;00EB 8B 4E F0 mov cx,word ptr un3200EE 8B 5E F2 mov bx,word ptr un32+200F1 2B C8 sub cx,ax00F3 1B DA sbb bx,dx00F5 8B 46 F6 again1: mov ax,q1 ; again1:00F8 F7 66 F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16)00FB 3B D1 cmp dx,cx ; + un1) {00FD 72 12 jb l1700FF 77 05 ja l160101 3B 46 FE cmp ax,un10104 76 0B jbe l170106 FF 4E F6 l16: dec q1 ; q1--;0109 03 4E FA add cx,vn1 ; rhat += vn1;010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)010F 74 E4 jz again1 ; goto again1;0111 8B 46 F6 l17: mov ax,q1 ; }0114 F7 66 0E mul word ptr v+2 ; } // Умножи и извади.

Page 48: Сборник задачи по програмиране на Асемблер

48Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;0118 8B 46 F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване011B F7 66 0C mul word ptr v; CX = мл.д.(ст.д.(v)*q1)011E 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)0120 8B 7E F0 mov di,word ptr un320123 8B 5E FE mov bx,un10126 2B D8 sub bx,ax0128 1B FA sbb di,dx012A 89 5E EC mov word ptr un21,bx012D 89 7E EE mov word ptr un21+2,di0130 3B 7E FA cmp di,vn10133 C7 46 F4 FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене.0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0.013A 8B 46 EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1);013D 8B 56 EE mov dx,word ptr un21+20140 F7 76 FA div vn10143 89 46 F4 mov q0,ax0146 F7 66 FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1;0149 8B 4E EC mov cx,word ptr un21014C 8B 5E EE mov bx,word ptr un21+2014F 2B C8 sub cx,ax0151 1B DA sbb bx,dx0153 8B 46 F4 again0: mov ax,q0 ; again0:0156 F7 66 F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16)0159 3B D1 cmp dx,cx ; + un0) {015B 72 12 jb l20015D 77 05 ja l19015F 3B 46 FC cmp ax,un00162 76 0B jbe l200164 FF 4E F4 l19: dec q0 ; q0--;0167 03 4E FA add cx,vn1 ; rhat += vn1;016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)016D 74 E4 jz again0 ; goto again0;016F 8B 5E 10 l20: mov bx,r ; }0172 0B DB or bx,bx ; }0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го.0176 8B 46 F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s;0179 F7 66 0E mul word ptr v+2017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)017D 8B 46 F4 mov ax,q00180 F7 66 0C mul word ptr v0183 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)0185 8B 4E FC mov cx,un00188 8B 7E EC mov di,word ptr un21018B 2B C8 sub cx,ax018D 1B FA sbb di,dx018F 87 CE xchg cx,si0191 E3 06 jcxz l220193 D1 EF l21: shr di,10195 D1 DE rcr si,10197 E2 FA loop l210199 89 37 l22: mov [bx],si019B 89 7F 02 mov [bx+2],di019E 8B 46 F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0;01A1 8B 56 F6 mov dx,q101A4 5E POP SI01A5 5F POP DI01A6 8B E5 MOV sp,bp01A8 5D POP bp01A9 C3 l24: ret01AA divlu2 endp ; } end01AA @CurSeg ENDS

Page 49: Сборник задачи по програмиране на Асемблер

49Сборник задачи по програмиране на Асемблер

22 март 2012 11:43:11http://x86.hit.bg/assembly.html

14.1.2. MicrosoftMacro Assembler (MASM)

Това е най-известният асемблер за x86, към съвместимост с който се стремят повечето от останалите. От версия1 (1981 г.) до версия 10 (2010 г.) са добавени множество директиви , набори от команди и др. При директивитенай-големите нововъведения са във версии 5 (опростени декларации на сегментите), 5.1 (директива „local“ иразширение на директива „proc“ – вж. т. 14) и 6 (директива „invoke“ за извикване на подпрограма). Във версия 6са добавени и управляващи конструкции от високо ниво („.if“, „.else“, „.elseif“, „.endif“, „.while“, „.endw“,„.repeat“, „.until“, „.untilcxz“, „.break“ и „.continue“). Въведени са и оператори за проверка на флаговете („carry?“,„overflow?“, „parity?“, „sign?“ и „zero?“). Поддържат се и условните оператори на езика C („==“, „!=“, „<“, „<=“,„>“, „>=“, „!“, „&&“ и „||“), както и оператор за проверка на бит („&“). С тези конструкции и оператори отвисоко ниво програмата става по-четлива и структурирана и отпада необходимостта от етикети.

До MASM версия 10 целевият микропроцесор по подразбиране е 8086, но от версия 10 вече е 286 и директивите„.8086“, „.186“ и „.286“ отпадат. Версии от 1 до 6 генерират обектен файл само във формат „OMF“. От версия6.1 нататък може да се генерира и обектен файл във формат „COFF“. Версии от 1 до 3 са написани на езика„Паскал“, а версия 4 е пренаписана на език C. Версиите от 1 до 4 са 16-битови програми за ДОС. Версии 5, 5.1 и6 съществуват в 2 варианта: за ДОС (16-битови) и за OS/2. Версии от 6.1 до 6.11d са 32-битови и използват т.н.„разширител на ДОС“ за достъп до разширената памет, а от версия 6.12 нататък асемблерът е конзолна програмаза „Win32“. От версия 9 нататък за работата му се изисква поне „Windows 2000“ (NT 5.0), от версия 10 нататък –поне „Windows XP“ (NT 5.1), а от версия 11 нататък – поне „Windows Vista“ (NT 6.0) и поддръжка на SSE2.Последните няколко версии са част от пакета „Visual C++“ – например MASM версия 10 е част от „Visual C++“2010.

Заб. 1: В листингите по-долу звездичките („*“) обозначават автоматично генерираните от MASM команди .

Заб. 2: Обърнете внимание , че при зад. №50 (т. 13.5) MASM версия 10 генерира „епилог“ с команда„leave“ (защо?), която липсва в 8086. MASM версия 9 и по-старите версии генерират вместо нея „mov sp,bp / popbp“.

Page 50: Сборник задачи по програмиране на Асемблер

50Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Microsoft (R) Macro Assembler Version 10.00.30319.01 08/29/11 17:11:54DIVLUSS.ASM Page 1 - 1

.model small,c0000 .code

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public divlu20000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word0000 55 * push bp0001 8B EC * mov bp, sp0003 57 * push di0004 56 * push si0005 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в0008 8B 56 0E mov dx,word ptr v+2000B 39 46 04 cmp word ptr u1,ax000E 8B 5E 06 mov bx,word ptr u1+20011 1B DA sbb bx,dx0013 72 12 jb l20015 8B 5E 10 mov bx,r0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност001A B8 FFFF mov ax,-1 ;ffffH001D 74 05 jz l1001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0021 89 47 02 mov [bx+2],ax0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0025 EB 40 jmp short l8 ; }0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод!002A 33 C0 xor ax,ax002C 33 D2 xor dx,dx002E D1 66 08 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво0031 D1 56 0A rcl word ptr u0+2,10034 D1 56 04 rcl word ptr u1,10037 D1 56 06 rcl word ptr u1+2,1003A D1 D0 rcl ax,1003C D1 D2 rcl dx,1003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)0040 3B 46 0C cmp ax,word ptr v ; сравни DX:AX и делителя0043 8B DA mov bx,dx0045 1B 5E 0E sbb bx,word ptr v+2 ; DX:AX < делителя ?0048 72 09 jb l5 ; да, прескочи004A 2B 46 0C l4: sub ax,word ptr v ; не, извади делителя004D 1B 56 0E sbb dx,word ptr v+20050 FF 46 08 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти0055 8B 5E 10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го.0058 0B DB or bx,bx005A 74 05 jz l7005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX005E 89 57 02 mov [bx+2],dx0061 8B 46 08 l7: mov ax,word ptr u00064 8B 56 0A mov dx,word ptr u0+20067 l8: ret0067 5E * pop si0068 5F * pop di0069 C9 * leave006A C3 * ret 00000h006B divlu2 endp ; } end

Page 51: Сборник задачи по програмиране на Асемблер

51Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Microsoft (R) Macro Assembler Version 10.00.30319.01 08/29/11 17:12:00DIVLU2.ASM Page 1 - 1

.model small,c0000 .code

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public divlu20000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word

; unsigned short un1, un0, // Нормализирани младши цифри на делимото.; vn1, vn0, // Нормализирани цифри на делителя.; q1, q0; // Цифри на частното.; unsigned long un32, un21, un10, // Двойки цифри на делимото.; rhat; // Остатък.; int s; // Колко бита изместваме за нормализация.

local un1:word,un0:word,vn1:word,vn0:word,q1:word,q0:word,un32:dword,un21:dword0000 55 * push bp0001 8B EC * mov bp, sp0003 83 C4 EC * add sp, 0FFECh0006 57 * push di0007 56 * push si0008 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в000B 8B 56 0E mov dx,word ptr v+2000E 39 46 04 cmp word ptr u1,ax0011 8B 5E 06 mov bx,word ptr u1+20014 1B DA sbb bx,dx0016 72 13 jb l20018 8B 5E 10 mov bx,r001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност001D B8 FFFF mov ax,-1 ;ffffH0020 74 05 jz l10022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0024 89 47 02 mov [bx+2],ax0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0028 E9 0179 jmp l24 ; }002B 8B F2 l2: mov si,dx ; if (v == 0)002D 0B F0 or si,ax002F BE 0020 mov si,32 ;0020H ; s = 32;0032 74 4C jz l7 ; else { // 0 <= s <= 31.0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя.0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) {0038 75 06 jne l3003A BE 0010 mov si,16 ;0010H ; s += 16;003D 92 xchg ax,dx ; v <<= 16;003E 33 C0 xor ax,ax0040 0A F6 l3: or dh,dh ; }0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) {0044 83 C6 08 add si,8 ; s += 8;0047 8A F2 mov dh,dl ; v <<= 8;0049 8A D4 mov dl,ah004B 8A E0 mov ah,al004D 2A C0 sub al,al004F 80 FE 0F l4: cmp dh,0Fh ; }0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) {0054 83 C6 04 add si,4 ; s += 4;0057 D1 E0 shl ax,1 ; v <<= 4;0059 D1 D2 rcl dx,1005B D1 E0 shl ax,1005D D1 D2 rcl dx,1005F D1 E0 shl ax,10061 D1 D2 rcl dx,10063 D1 E0 shl ax,10065 D1 D2 rcl dx,10067 80 FE 3F l5: cmp dh,3Fh ; }006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) {006C 83 C6 02 add si,2 ; s += 2;006F D1 E0 shl ax,1 ; v <<= 2;

Page 52: Сборник задачи по програмиране на Асемблер

52Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0071 D1 D2 rcl dx,10073 D1 E0 shl ax,10075 D1 D2 rcl dx,10077 0B D2 l6: or dx,dx ; }0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) {007B 46 inc si ; s++;007C D1 E0 shl ax,1 ; v <<= 1;007E D1 D2 rcl dx,10080 89 46 0C l7: mov word ptr v,ax ; }0083 89 56 0E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри.0086 89 56 FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16);0089 89 46 F8 mov vn0,ax ; vn0 = (unsigned short)v;008C 8B CE mov cx,si ; un32 = (u1 << s) |008E 8B 5E 04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31));0091 8B 7E 06 mov di,word ptr u1+20094 E3 06 jcxz l90096 D1 E3 l8: shl bx,10098 D1 D7 rcl di,1009A E2 FA loop l8009C B1 20 l9: mov cl,32 ;0020H009E 2B CE sub cx,si00A0 8B 46 08 mov ax,word ptr u000A3 8B 56 0A mov dx,word ptr u0+200A6 74 06 jz l1100A8 D1 EA l10: shr dx,100AA D1 D8 rcr ax,100AC E2 FA loop l1000AE 0B F6 l11: or si,si00B0 74 04 jz l1200B2 0B D8 or bx,ax00B4 0B FA or di,dx00B6 89 5E F0 l12: mov word ptr un32,bx00B9 89 7E F2 mov word ptr un32+2,di00BC 8B 46 08 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво.00BF 8B 56 0A mov dx,word ptr u0+200C2 8B CE mov cx,si00C4 E3 06 jcxz l1400C6 D1 E0 l13: shl ax,100C8 D1 D2 rcl dx,100CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри.00CC 89 56 FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16);00CF 89 46 FC mov un0,ax ; un0 = (unsigned short)un10;00D2 3B 7E FA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване00D5 C7 46 F6 FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1.00DC 8B 46 F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1);00DF 8B 56 F2 mov dx,word ptr un32+200E2 F7 76 FA div vn100E5 89 46 F6 mov q1,ax00E8 F7 66 FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1;00EB 8B 4E F0 mov cx,word ptr un3200EE 8B 5E F2 mov bx,word ptr un32+200F1 2B C8 sub cx,ax00F3 1B DA sbb bx,dx00F5 8B 46 F6 again1: mov ax,q1 ; again1:00F8 F7 66 F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16)00FB 3B D1 cmp dx,cx ; + un1) {00FD 72 12 jb l1700FF 77 05 ja l160101 3B 46 FE cmp ax,un10104 76 0B jbe l170106 FF 4E F6 l16: dec q1 ; q1--;0109 03 4E FA add cx,vn1 ; rhat += vn1;010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)010F 74 E4 jz again1 ; goto again1;0111 8B 46 F6 l17: mov ax,q1 ; }0114 F7 66 0E mul word ptr v+2 ; } // Умножи и извади.0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;0118 8B 46 F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване011B F7 66 0C mul word ptr v; CX = мл.д.(ст.д.(v)*q1)011E 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)0120 8B 7E F0 mov di,word ptr un320123 8B 5E FE mov bx,un10126 2B D8 sub bx,ax0128 1B FA sbb di,dx012A 89 5E EC mov word ptr un21,bx012D 89 7E EE mov word ptr un21+2,di

Page 53: Сборник задачи по програмиране на Асемблер

53Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0130 3B 7E FA cmp di,vn10133 C7 46 F4 FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене.0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0.013A 8B 46 EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1);013D 8B 56 EE mov dx,word ptr un21+20140 F7 76 FA div vn10143 89 46 F4 mov q0,ax0146 F7 66 FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1;0149 8B 4E EC mov cx,word ptr un21014C 8B 5E EE mov bx,word ptr un21+2014F 2B C8 sub cx,ax0151 1B DA sbb bx,dx0153 8B 46 F4 again0: mov ax,q0 ; again0:0156 F7 66 F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16)0159 3B D1 cmp dx,cx ; + un0) {015B 72 12 jb l20015D 77 05 ja l19015F 3B 46 FC cmp ax,un00162 76 0B jbe l200164 FF 4E F4 l19: dec q0 ; q0--;0167 03 4E FA add cx,vn1 ; rhat += vn1;016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)016D 74 E4 jz again0 ; goto again0;016F 8B 5E 10 l20: mov bx,r ; }0172 0B DB or bx,bx ; }0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го.0176 8B 46 F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s;0179 F7 66 0E mul word ptr v+2017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)017D 8B 46 F4 mov ax,q00180 F7 66 0C mul word ptr v0183 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)0185 8B 4E FC mov cx,un00188 8B 7E EC mov di,word ptr un21018B 2B C8 sub cx,ax018D 1B FA sbb di,dx018F 87 CE xchg cx,si0191 E3 06 jcxz l220193 D1 EF l21: shr di,10195 D1 DE rcr si,10197 E2 FA loop l210199 89 37 l22: mov [bx],si019B 89 7F 02 mov [bx+2],di019E 8B 46 F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0;01A1 8B 56 F6 mov dx,q101A4 l24: ret01A4 5E * pop si01A5 5F * pop di01A6 C9 * leave01A7 C3 * ret 00000h01A8 divlu2 endp ; } end

14.1.3. Watcom Macro Assembler (WASM)

Този асемблер е част от пакета „Watcom C++“ (сега „Open Watcom“ с открит изходен текст) и е донякъдесъвместим с MASM версия 6 (вж. т. 14.1.2). За съжаление той е много лошо документиран, а снедокументирания си ключ „–fl“ за създаване на листинг дава само списък на сегментите, групите, процедуритеи имената. Затова WASM е единственият от разглежданите тук асемблери, за който липсва листинг на зад. №№49–50 (тт. 13.4–13.5), макар че той успешно асемблира файла с изходния текст и създава верен обектен код.

Page 54: Сборник задачи по програмиране на Асемблер

54Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

14.1.4. Japheth’s Watcom Macro Assembler (JWASM)

Това е сравнително нов, но бързо развиващ се и многообещаващ проект. JWASM е развитие на WASM отAndreas Grech („Яфет“) с цел да създаде асемблер, почти напълно съвместим с MASM версия 6, но без неговитегрешки. Това до голяма степен вече е постигнато . Нещо повече, JWASM от версия 2.06 нататък е почти напълносъвместим с MASM версия 8. При това той генерира обектни файлове в повече формати (освен „OMF“ и„COFF“, също и „ELF“, но не и „Mach-O“), двоични файлове и изпълними файлове тип „.EXE“ („MZ“) за ДОС.Тъй като JWASM като предшественика си WASM е написан 100% на език C, той е мобилен, т.е. може да секомпилира за произволна операционна система с достатъчно мощен компилатор на C. Все пак явно по JWASMима още работа, като се има предвид например това, което той прави с коментарите на първия и последния редкоманди на процедурите в листингите по-долу...

Page 55: Сборник задачи по програмиране на Асемблер

55Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

JWasm v2.06e, Jul 22 2011DIVLUSS.ASM

.model small,c00000000 * _TEXT segment WORD PUBLIC 'CODE' * _TEXT ends00000000 * _DATA segment WORD PUBLIC 'DATA' * _DATA ends

* DGROUP group _DATA* assume cs:_TEXT,ds:DGROUP,ss:DGROUP

.code00000000 * _TEXT segment

* assume cs:_TEXT; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public divlu200000000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word00000000 55 * push bp00000001 8BEC * mov bp, sp00000003 57 * push di00000004 56 * push si00000005 8B460C mov ax,word ptr v00000008 8B560E mov dx,word ptr v+20000000B 394604 cmp word ptr u1,ax0000000E 8B5E06 mov bx,word ptr u1+200000011 1BDA sbb bx,dx00000013 7212 jb l200000015 8B5E10 mov bx,r00000018 0BDB or bx,bx ; if (r) // остатъка невъзможна стойност0000001A B8FFFF mov ax,-1 ;ffffH0000001D 7405 jz l10000001F 8907 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото00000021 894702 mov [bx+2],ax00000024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.00000025 EB40 jmp short l8 ; }00000027 B94000 l2: mov cx,64 ; благодарности на Peter Norton за този метод!0000002A 33C0 xor ax,ax0000002C 33D2 xor dx,dx0000002E D16608 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво00000031 D1560A rcl word ptr u0+2,100000034 D15604 rcl word ptr u1,100000037 D15606 rcl word ptr u1+2,10000003A D1D0 rcl ax,10000003C D1D2 rcl dx,10000003E 720A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)00000040 3B460C cmp ax,word ptr v ; сравни DX:AX и делителя00000043 8BDA mov bx,dx00000045 1B5E0E sbb bx,word ptr v+2 ; DX:AX < делителя ?00000048 7209 jb l5 ; да, прескочи0000004A 2B460C l4: sub ax,word ptr v ; не, извади делителя0000004D 1B560E sbb dx,word ptr v+200000050 FF4608 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път00000053 E2D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти00000055 8B5E10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го.00000058 0BDB or bx,bx0000005A 7405 jz l70000005C 8907 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX0000005E 895702 mov [bx+2],dx00000061 8B4608 l7: mov ax,word ptr u000000064 8B560A mov dx,word ptr u0+200000067 l8: ret00000067 5E * pop si00000068 5F * pop di00000069 5D * pop bp0000006A C3 * retn0000006B divlu2 endp ; 0000006B * _TEXT ends end

Page 56: Сборник задачи по програмиране на Асемблер

56Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

JWasm v2.06e, Jul 22 2011DIVLU2.ASM

.model small,c00000000 * _TEXT segment WORD PUBLIC 'CODE' * _TEXT ends00000000 * _DATA segment WORD PUBLIC 'DATA' * _DATA ends

* DGROUP group _DATA* assume cs:_TEXT,ds:DGROUP,ss:DGROUP

.code00000000 * _TEXT segment

* assume cs:_TEXT; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В слуачите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public divlu200000000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word

; unsigned short un1, un0, // Нормализирани младши цифри на делимото.; vn1, vn0, // Нормализирани цифри на делителя.; q1, q0; // Цифри на частното.; unsigned long un32, un21, un10, // Двойки цифри на делимото.; rhat; // Остатък.; int s; // Колко бита изместваме за нормализация.

local un1:word,un0:word,vn1:word,vn0:word,q1:word,q0:word,un32:dword,un21:dword00000000 55 * push bp00000001 8BEC * mov bp, sp00000003 83EC14 * sub sp, 2000000006 57 * push di00000007 56 * push si00000008 8B460C mov ax,word ptr v0000000B 8B560E mov dx,word ptr v+20000000E 394604 cmp word ptr u1,ax00000011 8B5E06 mov bx,word ptr u1+200000014 1BDA sbb bx,dx00000016 7213 jb l200000018 8B5E10 mov bx,r0000001B 0BDB or bx,bx ; if (r) // остатъка невъзможна стойност0000001D B8FFFF mov ax,-1 ;ffffH00000020 7405 jz l100000022 8907 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото00000024 894702 mov [bx+2],ax00000027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.00000028 E97901 jmp l24 ; }0000002B 8BF2 l2: mov si,dx ; if (v == 0)0000002D 0BF0 or si,ax0000002F BE2000 mov si,32 ;0020H ; s = 32;00000032 744C jz l7 ; else { // 0 <= s <= 31.00000034 33F6 xor si,si ; s = 0; // Нормализирай делителя.00000036 3BD6 cmp dx,si ; if (v <= 0x0000FFFFL) {00000038 7506 jne l30000003A BE1000 mov si,16 ;0010H ; s += 16;0000003D 92 xchg ax,dx ; v <<= 16;0000003E 33C0 xor ax,ax00000040 0AF6 l3: or dh,dh ; }00000042 750B jnz l4 ; if (v <= 0x00FFFFFFL) {00000044 83C608 add si,8 ; s += 8;00000047 8AF2 mov dh,dl ; v <<= 8;00000049 8AD4 mov dl,ah0000004B 8AE0 mov ah,al0000004D 2AC0 sub al,al0000004F 80FE0F l4: cmp dh,0Fh ; }00000052 7713 ja l5 ; if (v <= 0x0FFFFFFFL) {00000054 83C604 add si,4 ; s += 4;00000057 D1E0 shl ax,1 ; v <<= 4;00000059 D1D2 rcl dx,10000005B D1E0 shl ax,10000005D D1D2 rcl dx,10000005F D1E0 shl ax,100000061 D1D2 rcl dx,1

Page 57: Сборник задачи по програмиране на Асемблер

57Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

00000063 D1E0 shl ax,100000065 D1D2 rcl dx,100000067 80FE3F l5: cmp dh,3Fh ; }0000006A 770B ja l6 ; if (v <= 0x3FFFFFFFL) {0000006C 83C602 add si,2 ; s += 2;0000006F D1E0 shl ax,1 ; v <<= 2;00000071 D1D2 rcl dx,100000073 D1E0 shl ax,100000075 D1D2 rcl dx,100000077 0BD2 l6: or dx,dx ; }00000079 7805 js l7 ; if (v <= 0x7FFFFFFFL) {0000007B 46 inc si ; s++;0000007C D1E0 shl ax,1 ; v <<= 1;0000007E D1D2 rcl dx,100000080 89460C l7: mov word ptr v,ax ; }00000083 89560E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри.00000086 8956FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16);00000089 8946F8 mov vn0,ax ; vn0 = (unsigned short)v;0000008C 8BCE mov cx,si ; un32 = (u1 << s) |0000008E 8B5E04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31));00000091 8B7E06 mov di,word ptr u1+200000094 E306 jcxz l900000096 D1E3 l8: shl bx,100000098 D1D7 rcl di,10000009A E2FA loop l80000009C B120 l9: mov cl,32 ;0020H0000009E 2BCE sub cx,si000000A0 8B4608 mov ax,word ptr u0000000A3 8B560A mov dx,word ptr u0+2000000A6 7406 jz l11000000A8 D1EA l10: shr dx,1000000AA D1D8 rcr ax,1000000AC E2FA loop l10000000AE 0BF6 l11: or si,si000000B0 7404 jz l12000000B2 0BD8 or bx,ax000000B4 0BFA or di,dx000000B6 895EF0 l12: mov word ptr un32,bx000000B9 897EF2 mov word ptr un32+2,di000000BC 8B4608 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво.000000BF 8B560A mov dx,word ptr u0+2000000C2 8BCE mov cx,si000000C4 E306 jcxz l14000000C6 D1E0 l13: shl ax,1000000C8 D1D2 rcl dx,1000000CA E2FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри.000000CC 8956FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16);000000CF 8946FC mov un0,ax ; un0 = (unsigned short)un10;000000D2 3B7EFA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване000000D5 C746F6FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.000000DA 7335 jae l17 ; else {// Изчисли първата цифра на частното, q1.000000DC 8B46F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1);000000DF 8B56F2 mov dx,word ptr un32+2000000E2 F776FA div vn1000000E5 8946F6 mov q1,ax000000E8 F766FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1;000000EB 8B4EF0 mov cx,word ptr un32000000EE 8B5EF2 mov bx,word ptr un32+2000000F1 2BC8 sub cx,ax000000F3 1BDA sbb bx,dx000000F5 8B46F6 again1: mov ax,q1 ; again1:000000F8 F766F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16)000000FB 3BD1 cmp dx,cx ; + un1) {000000FD 7212 jb l17000000FF 7705 ja l1600000101 3B46FE cmp ax,un100000104 760B jbe l1700000106 FF4EF6 l16: dec q1 ; q1--;00000109 034EFA add cx,vn1 ; rhat += vn1;0000010C 83D300 adc bx,0 ; if ((rhat >> 16) == 0)0000010F 74E4 jz again1 ; goto again1;00000111 8B46F6 l17: mov ax,q1 ; }00000114 F7660E mul word ptr v+2 ; } // Умножи и извади.00000117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;00000118 8B46F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване0000011B F7660C mul word ptr v; CX = мл.д.(ст.д.(v)*q1)0000011E 03D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)

Page 58: Сборник задачи по програмиране на Асемблер

58Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

00000120 8B7EF0 mov di,word ptr un3200000123 8B5EFE mov bx,un100000126 2BD8 sub bx,ax00000128 1BFA sbb di,dx0000012A 895EEC mov word ptr un21,bx0000012D 897EEE mov word ptr un21+2,di00000130 3B7EFA cmp di,vn100000133 C746F4FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене.00000138 7335 jae l20 ; else {// Изчисли втората цифра на частното, q0.0000013A 8B46EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1);0000013D 8B56EE mov dx,word ptr un21+200000140 F776FA div vn100000143 8946F4 mov q0,ax00000146 F766FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1;00000149 8B4EEC mov cx,word ptr un210000014C 8B5EEE mov bx,word ptr un21+20000014F 2BC8 sub cx,ax00000151 1BDA sbb bx,dx00000153 8B46F4 again0: mov ax,q0 ; again0:00000156 F766F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16)00000159 3BD1 cmp dx,cx ; + un0) {0000015B 7212 jb l200000015D 7705 ja l190000015F 3B46FC cmp ax,un000000162 760B jbe l2000000164 FF4EF4 l19: dec q0 ; q0--;00000167 034EFA add cx,vn1 ; rhat += vn1;0000016A 83D300 adc bx,0 ; if ((rhat >> 16) == 0)0000016D 74E4 jz again0 ; goto again0;0000016F 8B5E10 l20: mov bx,r ; }00000172 0BDB or bx,bx ; }00000174 7428 jz l23 ; if (r) // Ако се иска остатъкът, върни го.00000176 8B46F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s;00000179 F7660E mul word ptr v+20000017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)0000017D 8B46F4 mov ax,q000000180 F7660C mul word ptr v00000183 03D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)00000185 8B4EFC mov cx,un000000188 8B7EEC mov di,word ptr un210000018B 2BC8 sub cx,ax0000018D 1BFA sbb di,dx0000018F 87CE xchg cx,si00000191 E306 jcxz l2200000193 D1EF l21: shr di,100000195 D1DE rcr si,100000197 E2FA loop l2100000199 8937 l22: mov [bx],si0000019B 897F02 mov [bx+2],di0000019E 8B46F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0;000001A1 8B56F6 mov dx,q1000001A4 l24: ret000001A4 5E * pop si000001A5 5F * pop di000001A6 8BE5 * mov sp, bp000001A8 5D * pop bp000001A9 C3 * retn000001AA divlu2 endp ; 000001AA * _TEXT ends end

14.1.5. Light Macro Assembler (LASM)

Този асемблер на фирмата „Tama Software Ltd.“ (автор – Шоичиро Соеджима) е съвместим с MASM версия 6. Занего се изисква регистрационен файл, без който има ограничение до 150 реда на асемблирания изходен текст.Както се вижда по-долу, LASM вмъква една излишна команда („mov sp,bp“) в автоматично генерирания„епилог“ на зад. №49 (т. 13.4). Освен това генерираният от него листинг е доста по-различен от листингите наостаналите асемблери...

Page 59: Сборник задачи по програмиране на Асемблер

59Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

; Light Macro Assembler for x86 Version 2.34A 2011-08-28 12:12:15; source file : DIVLUSS.ASM

.MODEL small,C?_DUMMY SEGMENT BYTE PRIVATE?_DUMMY ENDS ;=0000DGROUP GROUP ?_DUMMY ASSUME ds:DGROUP,es:DGROUP_TEXT SEGMENT WORD PUBLIC 'CODE' ASSUME cs:_TEXT PUBLIC divlu2divlu2 PROC NEAR push bp ;-0000 55 mov bp,sp ;-0001 8B EC push si ;-0003 56 push di ;-0004 57 mov ax,WORD PTR [bp+0Ch] ; 0005 8B 46 0C (v) mov dx,WORD PTR [bp+0Eh] ; 0008 8B 56 0E (v) cmp WORD PTR [bp+4h],ax ; 000B 39 46 04 (u1) mov bx,WORD PTR [bp+6h] ; 000E 8B 5E 06 (u1) sbb bx,dx ; 0011 1B DA jb SHORT _TEXT:l2 ; 0013 72 12 mov bx,WORD PTR [bp+10h] ; 0015 8B 5E 10 (r) or bx,bx ; 0018 0B DB mov ax,-1h ; 001A B8 FF FF je SHORT _TEXT:l1 ; 001D 74 05 mov WORD PTR [bx],ax ; 001F 89 07 mov WORD PTR [bx+2h],ax ; 0021 89 47 02l1: ; 0024 cwd ; 0024 99 jmp SHORT _TEXT:l8 ; 0025 EB 40l2: ; 0027 mov cx,40h ; 0027 B9 40 00 xor ax,ax ; 002A 33 C0 xor dx,dx ; 002C 33 D2l3: ; 002E shl WORD PTR [bp+8h],1h ; 002E D1 66 08 (u0) rcl WORD PTR [bp+0Ah],1h ; 0031 D1 56 0A (u0) rcl WORD PTR [bp+4h],1h ; 0034 D1 56 04 (u1) rcl WORD PTR [bp+6h],1h ; 0037 D1 56 06 (u1) rcl ax,1h ; 003A D1 D0 rcl dx,1h ; 003C D1 D2 jc SHORT _TEXT:l4 ; 003E 72 0A cmp ax,WORD PTR [bp+0Ch] ; 0040 3B 46 0C (v) mov bx,dx ; 0043 8B DA sbb bx,WORD PTR [bp+0Eh] ; 0045 1B 5E 0E (v) jb SHORT _TEXT:l5 ; 0048 72 09l4: ; 004A sub ax,WORD PTR [bp+0Ch] ; 004A 2B 46 0C (v) sbb dx,WORD PTR [bp+0Eh] ; 004D 1B 56 0E (v) inc WORD PTR [bp+8h] ; 0050 FF 46 08 (u0)l5: ; 0053 loop SHORT _TEXT:l3 ; 0053 E2 D9 mov bx,WORD PTR [bp+10h] ; 0055 8B 5E 10 (r) or bx,bx ; 0058 0B DB je SHORT _TEXT:l7 ; 005A 74 05l6: ; 005C mov WORD PTR [bx],ax ; 005C 89 07 mov WORD PTR [bx+2h],dx ; 005E 89 57 02l7: ; 0061 mov ax,WORD PTR [bp+8h] ; 0061 8B 46 08 (u0) mov dx,WORD PTR [bp+0Ah] ; 0064 8B 56 0A (u0)l8: ; 0067 pop di ;-0067 5F pop si ;-0068 5E mov sp,bp ;-0069 8B E5 pop bp ;-006B 5D retn ; 006C C3divlu2 ENDP_TEXT ENDS ; 006D END

Page 60: Сборник задачи по програмиране на Асемблер

60Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

; Light Macro Assembler for x86 Version 2.34A 2011-08-28 12:12:17; source file : DIVLU2.ASM

.MODEL small,C?_DUMMY SEGMENT BYTE PRIVATE?_DUMMY ENDS ;=0000DGROUP GROUP ?_DUMMY ASSUME ds:DGROUP,es:DGROUP_TEXT SEGMENT WORD PUBLIC 'CODE' ASSUME cs:_TEXT PUBLIC divlu2divlu2 PROC NEAR push bp ;-0000 55 mov bp,sp ;-0001 8B EC add sp,-14h ;-0003 83 C4 EC push si ;-0006 56 push di ;-0007 57 mov ax,WORD PTR [bp+0Ch] ; 0008 8B 46 0C (v) mov dx,WORD PTR [bp+0Eh] ; 000B 8B 56 0E (v) cmp WORD PTR [bp+4h],ax ; 000E 39 46 04 (u1) mov bx,WORD PTR [bp+6h] ; 0011 8B 5E 06 (u1) sbb bx,dx ; 0014 1B DA jb SHORT _TEXT:l2 ; 0016 72 13 mov bx,WORD PTR [bp+10h] ; 0018 8B 5E 10 (r) or bx,bx ; 001B 0B DB mov ax,-1h ; 001D B8 FF FF je SHORT _TEXT:l1 ; 0020 74 05 mov WORD PTR [bx],ax ; 0022 89 07 mov WORD PTR [bx+2h],ax ; 0024 89 47 02l1: ; 0027 cwd ; 0027 99 jmp NEAR PTR _TEXT:l24 ; 0028 E9 79 01l2: ; 002B mov si,dx ; 002B 8B F2 or si,ax ; 002D 0B F0 mov si,20h ; 002F BE 20 00 je SHORT _TEXT:l7 ; 0032 74 4C xor si,si ; 0034 33 F6 cmp dx,si ; 0036 3B D6 jne SHORT _TEXT:l3 ; 0038 75 06 mov si,10h ; 003A BE 10 00 xchg ax,dx ; 003D 92 xor ax,ax ; 003E 33 C0l3: ; 0040 or dh,dh ; 0040 0A F6 jne SHORT _TEXT:l4 ; 0042 75 0B add si,8h ; 0044 83 C6 08 mov dh,dl ; 0047 8A F2 mov dl,ah ; 0049 8A D4 mov ah,al ; 004B 8A E0 sub al,al ; 004D 2A C0l4: ; 004F cmp dh,0Fh ; 004F 80 FE 0F ja SHORT _TEXT:l5 ; 0052 77 13 add si,4h ; 0054 83 C6 04 shl ax,1h ; 0057 D1 E0 rcl dx,1h ; 0059 D1 D2 shl ax,1h ; 005B D1 E0 rcl dx,1h ; 005D D1 D2 shl ax,1h ; 005F D1 E0 rcl dx,1h ; 0061 D1 D2 shl ax,1h ; 0063 D1 E0 rcl dx,1h ; 0065 D1 D2l5: ; 0067 cmp dh,3Fh ; 0067 80 FE 3F ja SHORT _TEXT:l6 ; 006A 77 0B add si,2h ; 006C 83 C6 02 shl ax,1h ; 006F D1 E0 rcl dx,1h ; 0071 D1 D2 shl ax,1h ; 0073 D1 E0 rcl dx,1h ; 0075 D1 D2l6: ; 0077 or dx,dx ; 0077 0B D2 js SHORT _TEXT:l7 ; 0079 78 05 inc si ; 007B 46 shl ax,1h ; 007C D1 E0 rcl dx,1h ; 007E D1 D2

Page 61: Сборник задачи по програмиране на Асемблер

61Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

l7: ; 0080 mov WORD PTR [bp+0Ch],ax ; 0080 89 46 0C (v) mov WORD PTR [bp+0Eh],dx ; 0083 89 56 0E (v) mov WORD PTR [bp-6h],dx ; 0086 89 56 FA (vn1) mov WORD PTR [bp-8h],ax ; 0089 89 46 F8 (vn0) mov cx,si ; 008C 8B CE mov bx,WORD PTR [bp+4h] ; 008E 8B 5E 04 (u1) mov di,WORD PTR [bp+6h] ; 0091 8B 7E 06 (u1) jcxz SHORT _TEXT:l9 ; 0094 E3 06l8: ; 0096 shl bx,1h ; 0096 D1 E3 rcl di,1h ; 0098 D1 D7 loop SHORT _TEXT:l8 ; 009A E2 FAl9: ; 009C mov cl,20h ; 009C B1 20 sub cx,si ; 009E 2B CE mov ax,WORD PTR [bp+8h] ; 00A0 8B 46 08 (u0) mov dx,WORD PTR [bp+0Ah] ; 00A3 8B 56 0A (u0) je SHORT _TEXT:l11 ; 00A6 74 06l10: ; 00A8 shr dx,1h ; 00A8 D1 EA rcr ax,1h ; 00AA D1 D8 loop SHORT _TEXT:l10 ; 00AC E2 FAl11: ; 00AE or si,si ; 00AE 0B F6 je SHORT _TEXT:l12 ; 00B0 74 04 or bx,ax ; 00B2 0B D8 or di,dx ; 00B4 0B FAl12: ; 00B6 mov WORD PTR [bp-10h],bx ; 00B6 89 5E F0 (un32) mov WORD PTR [bp-0Eh],di ; 00B9 89 7E F2 (un32) mov ax,WORD PTR [bp+8h] ; 00BC 8B 46 08 (u0) mov dx,WORD PTR [bp+0Ah] ; 00BF 8B 56 0A (u0) mov cx,si ; 00C2 8B CE jcxz SHORT _TEXT:l14 ; 00C4 E3 06l13: ; 00C6 shl ax,1h ; 00C6 D1 E0 rcl dx,1h ; 00C8 D1 D2 loop SHORT _TEXT:l13 ; 00CA E2 FAl14: ; 00CC mov WORD PTR [bp-2h],dx ; 00CC 89 56 FE (un1) mov WORD PTR [bp-4h],ax ; 00CF 89 46 FC (un0) cmp di,WORD PTR [bp-6h] ; 00D2 3B 7E FA (vn1) mov WORD PTR [bp-0Ah],-1h ; 00D5 C7 46 F6 FF FF (q1) JAE SHORT _TEXT:l17 ; 00DA 73 35 mov ax,WORD PTR [bp-10h] ; 00DC 8B 46 F0 (un32) mov dx,WORD PTR [bp-0Eh] ; 00DF 8B 56 F2 (un32) div WORD PTR [bp-6h] ; 00E2 F7 76 FA (vn1) mov WORD PTR [bp-0Ah],ax ; 00E5 89 46 F6 (q1) mul WORD PTR [bp-6h] ; 00E8 F7 66 FA (vn1) mov cx,WORD PTR [bp-10h] ; 00EB 8B 4E F0 (un32) mov bx,WORD PTR [bp-0Eh] ; 00EE 8B 5E F2 (un32) sub cx,ax ; 00F1 2B C8 sbb bx,dx ; 00F3 1B DAagain1: ; 00F5 mov ax,WORD PTR [bp-0Ah] ; 00F5 8B 46 F6 (q1) mul WORD PTR [bp-8h] ; 00F8 F7 66 F8 (vn0) cmp dx,cx ; 00FB 3B D1 jb SHORT _TEXT:l17 ; 00FD 72 12 ja SHORT _TEXT:l16 ; 00FF 77 05 cmp ax,WORD PTR [bp-2h] ; 0101 3B 46 FE (un1) jbe SHORT _TEXT:l17 ; 0104 76 0Bl16: ; 0106 dec WORD PTR [bp-0Ah] ; 0106 FF 4E F6 (q1) add cx,WORD PTR [bp-6h] ; 0109 03 4E FA (vn1) adc bx,0h ; 010C 83 D3 00 je SHORT _TEXT:again1 ; 010F 74 E4l17: ; 0111 mov ax,WORD PTR [bp-0Ah] ; 0111 8B 46 F6 (q1) mul WORD PTR [bp+0Eh] ; 0114 F7 66 0E (v) xchg ax,cx ; 0117 91 mov ax,WORD PTR [bp-0Ah] ; 0118 8B 46 F6 (q1) mul WORD PTR [bp+0Ch] ; 011B F7 66 0C (v) add dx,cx ; 011E 03 D1 mov di,WORD PTR [bp-10h] ; 0120 8B 7E F0 (un32) mov bx,WORD PTR [bp-2h] ; 0123 8B 5E FE (un1) sub bx,ax ; 0126 2B D8

Page 62: Сборник задачи по програмиране на Асемблер

62Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

sbb di,dx ; 0128 1B FA mov WORD PTR [bp-14h],bx ; 012A 89 5E EC (un21) mov WORD PTR [bp-12h],di ; 012D 89 7E EE (un21) cmp di,WORD PTR [bp-6h] ; 0130 3B 7E FA (vn1) mov WORD PTR [bp-0Ch],-1h ; 0133 C7 46 F4 FF FF (q0) JAE SHORT _TEXT:l20 ; 0138 73 35 mov ax,WORD PTR [bp-14h] ; 013A 8B 46 EC (un21) mov dx,WORD PTR [bp-12h] ; 013D 8B 56 EE (un21) div WORD PTR [bp-6h] ; 0140 F7 76 FA (vn1) mov WORD PTR [bp-0Ch],ax ; 0143 89 46 F4 (q0) mul WORD PTR [bp-6h] ; 0146 F7 66 FA (vn1) mov cx,WORD PTR [bp-14h] ; 0149 8B 4E EC (un21) mov bx,WORD PTR [bp-12h] ; 014C 8B 5E EE (un21) sub cx,ax ; 014F 2B C8 sbb bx,dx ; 0151 1B DAagain0: ; 0153 mov ax,WORD PTR [bp-0Ch] ; 0153 8B 46 F4 (q0) mul WORD PTR [bp-8h] ; 0156 F7 66 F8 (vn0) cmp dx,cx ; 0159 3B D1 jb SHORT _TEXT:l20 ; 015B 72 12 ja SHORT _TEXT:l19 ; 015D 77 05 cmp ax,WORD PTR [bp-4h] ; 015F 3B 46 FC (un0) jbe SHORT _TEXT:l20 ; 0162 76 0Bl19: ; 0164 dec WORD PTR [bp-0Ch] ; 0164 FF 4E F4 (q0) add cx,WORD PTR [bp-6h] ; 0167 03 4E FA (vn1) adc bx,0h ; 016A 83 D3 00 je SHORT _TEXT:again0 ; 016D 74 E4l20: ; 016F mov bx,WORD PTR [bp+10h] ; 016F 8B 5E 10 (r) or bx,bx ; 0172 0B DB je SHORT _TEXT:l23 ; 0174 74 28 mov ax,WORD PTR [bp-0Ch] ; 0176 8B 46 F4 (q0) mul WORD PTR [bp+0Eh] ; 0179 F7 66 0E (v) xchg ax,cx ; 017C 91 mov ax,WORD PTR [bp-0Ch] ; 017D 8B 46 F4 (q0) mul WORD PTR [bp+0Ch] ; 0180 F7 66 0C (v) add dx,cx ; 0183 03 D1 mov cx,WORD PTR [bp-4h] ; 0185 8B 4E FC (un0) mov di,WORD PTR [bp-14h] ; 0188 8B 7E EC (un21) sub cx,ax ; 018B 2B C8 sbb di,dx ; 018D 1B FA xchg cx,si ; 018F 87 CE jcxz SHORT _TEXT:l22 ; 0191 E3 06l21: ; 0193 shr di,1h ; 0193 D1 EF rcr si,1h ; 0195 D1 DE loop SHORT _TEXT:l21 ; 0197 E2 FAl22: ; 0199 mov WORD PTR [bx],si ; 0199 89 37 mov WORD PTR [bx+2h],di ; 019B 89 7F 02l23: ; 019E mov ax,WORD PTR [bp-0Ch] ; 019E 8B 46 F4 (q0) mov dx,WORD PTR [bp-0Ah] ; 01A1 8B 56 F6 (q1)l24: ; 01A4 pop di ;-01A4 5F pop si ;-01A5 5E mov sp,bp ;-01A6 8B E5 pop bp ;-01A8 5D retn ; 01A9 C3divlu2 ENDP_TEXT ENDS ; 01AA END

Page 63: Сборник задачи по програмиране на Асемблер

63Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

14.1.6. Turbo Assembler (TASM)

Този асемблер на фирмата „Borland“ е най-известният след MASM. Още версия 1 (1988 г.), излязла само 7 месецаслед излизането на MASM версия 5.1, е напълно съвместима с него. А TASM32 версия 5 (1996 г.) е съвместим сMASM версия 6. В TASM има и т.н. „идеален режим“. В него за сметка на съвместимостта с MASM е подобренаобработката на изразите, прави се строга проверка на типовете в тях, позволяват се вместващи се една в другаструктури и обединения, променен е синтаксисът на операндите с адресация на паметта и др. За съжаление следверсия 5.3 (2000 г.), в която е добавена поддръжка на набора команди „MMX“, TASM практически не еразвиван. Версия 5.4 (2010 г.) е всъщност 5.3 с еднакъв размер на изпълнимия файл и незначителни променипредимно по името на новия собственик (от 2008 г.) на продукта „C++Builder“, част от който сега е TASM –„Embarcadero Technologies“.

Заб.: В листингите по-долу единиците („1“) обозначават автоматично генерираните в пас №1 на TASMкоманди .

Page 64: Сборник задачи по програмиране на Асемблер

64Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Turbo Assembler Version 5.4 08-25-11 16:23:22 Page 1DIVLUSS.ASM

1 0000 .model small,c2 0000 .code3 ; /* Дълго делене, беззнаково (64/32 ==> 32).4 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово5 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.6 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя7 ; връща остатък 0xFFFFFFFF (невъзможна стойност).8 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.9 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */

10 ; 11 ; unsigned long 12 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 13 ; { 14 public divlu2 15 0000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word1 16 0000 55 PUSH BP1 17 0001 8B EC MOV BP,SP1 18 0003 57 PUSH DI1 19 0004 56 PUSH SI1 20 0005 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в 21 0008 8B 56 0E mov dx,word ptr v+2 22 000B 39 46 04 cmp word ptr u1,ax 23 000E 8B 5E 06 mov bx,word ptr u1+2 24 0011 1B DA sbb bx,dx 25 0013 72 12 jb l2 26 0015 8B 5E 10 mov bx,r 27 0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност 28 001A B8 FFFF mov ax,-1 ;ffffH 29 001D 74 05 jz l1 30 001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 31 0021 89 47 02 mov [bx+2],ax 32 0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 33 0025 EB 40 jmp short l8 ; } 34 0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод! 35 002A 33 C0 xor ax,ax 36 002C 33 D2 xor dx,dx 37 002E D1 66 08 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво 38 0031 D1 56 0A rcl word ptr u0+2,1 39 0034 D1 56 04 rcl word ptr u1,1 40 0037 D1 56 06 rcl word ptr u1+2,1 41 003A D1 D0 rcl ax,1 42 003C D1 D2 rcl dx,1 43 003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2) 44 0040 3B 46 0C cmp ax,word ptr v ; сравни DX:AX и делителя 45 0043 8B DA mov bx,dx 46 0045 1B 5E 0E sbb bx,word ptr v+2 ; DX:AX < делителя ? 47 0048 72 09 jb l5 ; да, прескочи 48 004A 2B 46 0C l4: sub ax,word ptr v ; не, извади делителя 49 004D 1B 56 0E sbb dx,word ptr v+2 50 0050 FF 46 08 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път 51 0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти 52 0055 8B 5E 10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го. 53 0058 0B DB or bx,bx 54 005A 74 05 jz l7 55 005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX 56 005E 89 57 02 mov [bx+2],dx 57 0061 8B 46 08 l7: mov ax,word ptr u0 58 0064 8B 56 0A mov dx,word ptr u0+21 59 0067 5E POP SI1 60 0068 5F POP DI1 61 0069 5D POP BP1 62 006A C3 RET 00000h 63 006B divlu2 endp ; } 64 end

Page 65: Сборник задачи по програмиране на Асемблер

65Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Turbo Assembler Version 5.4 08-25-11 16:23:24 Page 1DIVLU2.ASM

1 0000 .model small,c2 0000 .code3 ; /* Дълго делене, беззнаково (64/32 ==> 32).4 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово5 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.6 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя7 ; връща остатък 0xFFFFFFFF (невъзможна стойност).8 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.9 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */

10 ; 11 ; unsigned long 12 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 13 ; { 14 public divlu2 15 0000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word 16 ; unsigned short un1, un0, // Нормализирани младши цифри на делимото. 17 ; vn1, vn0, // Нормализирани цифри на делителя. 18 ; q1, q0; // Цифри на частното. 19 ; unsigned long un32, un21, un10, // Двойки цифри на делимото. 20 ; rhat; // Остатък. 21 ; int s; // Колко бита изместваме за нормализация. 22 local un1:word,un0:word,vn1:word,vn0:word,q1:word,q0:word,un32:dword,un21:dword1 23 0000 55 PUSH BP1 24 0001 8B EC MOV BP,SP1 25 0003 83 EC 14 SUB SP,00014h1 26 0006 57 PUSH DI1 27 0007 56 PUSH SI1 28 0008 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в 29 000B 8B 56 0E mov dx,word ptr v+2 30 000E 39 46 04 cmp word ptr u1,ax 31 0011 8B 5E 06 mov bx,word ptr u1+2 32 0014 1B DA sbb bx,dx 33 0016 72 13 jb l2 34 0018 8B 5E 10 mov bx,r 35 001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност 36 001D B8 FFFF mov ax,-1 ;ffffH 37 0020 74 05 jz l1 38 0022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 39 0024 89 47 02 mov [bx+2],ax 40 0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 41 0028 E9 0179 jmp l24 ; } 42 002B 8B F2 l2: mov si,dx ; if (v == 0) 43 002D 0B F0 or si,ax 44 002F BE 0020 mov si,32 ;0020H ; s = 32; 45 0032 74 4C jz l7 ; else { // 0 <= s <= 31. 46 0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя. 47 0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) { 48 0038 75 06 jne l3 49 003A BE 0010 mov si,16 ;0010H ; s += 16; 50 003D 92 xchg ax,dx ; v <<= 16; 51 003E 33 C0 xor ax,ax 52 0040 0A F6 l3: or dh,dh ; } 53 0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) { 54 0044 83 C6 08 add si,8 ; s += 8; 55 0047 8A F2 mov dh,dl ; v <<= 8; 56 0049 8A D4 mov dl,ah 57 004B 8A E0 mov ah,al 58 004D 2A C0 sub al,al 59 004F 80 FE 0F l4: cmp dh,0Fh ; } 60 0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) { 61 0054 83 C6 04 add si,4 ; s += 4; 62 0057 D1 E0 shl ax,1 ; v <<= 4; 63 0059 D1 D2 rcl dx,1 64 005B D1 E0 shl ax,1 65 005D D1 D2 rcl dx,1 66 005F D1 E0 shl ax,1 67 0061 D1 D2 rcl dx,1 68 0063 D1 E0 shl ax,1 69 0065 D1 D2 rcl dx,1 70 0067 80 FE 3F l5: cmp dh,3Fh ; } 71 006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) { 72 006C 83 C6 02 add si,2 ; s += 2;

Page 66: Сборник задачи по програмиране на Асемблер

66Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

73 006F D1 E0 shl ax,1 ; v <<= 2; 74 0071 D1 D2 rcl dx,1 75 0073 D1 E0 shl ax,1 76 0075 D1 D2 rcl dx,1 77 0077 0B D2 l6: or dx,dx ; } 78 0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) { 79 007B 46 inc si ; s++; 80 007C D1 E0 shl ax,1 ; v <<= 1; 81 007E D1 D2 rcl dx,1 82 0080 89 46 0C l7: mov word ptr v,ax ; } 83 0083 89 56 0E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри. 84 0086 89 56 FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16); 85 0089 89 46 F8 mov vn0,ax ; vn0 = (unsigned short)v; 86 008C 8B CE mov cx,si ; un32 = (u1 << s) | 87 008E 8B 5E 04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31)); 88 0091 8B 7E 06 mov di,word ptr u1+2 89 0094 E3 06 jcxz l9 90 0096 D1 E3 l8: shl bx,1 91 0098 D1 D7 rcl di,1 92 009A E2 FA loop l8 93 009C B1 20 l9: mov cl,32 ;0020H 94 009E 2B CE sub cx,si 95 00A0 8B 46 08 mov ax,word ptr u0 96 00A3 8B 56 0A mov dx,word ptr u0+2 97 00A6 74 06 jz l11 98 00A8 D1 EA l10: shr dx,1 99 00AA D1 D8 rcr ax,1 100 00AC E2 FA loop l10 101 00AE 0B F6 l11: or si,si 102 00B0 74 04 jz l12 103 00B2 0B D8 or bx,ax 104 00B4 0B FA or di,dx 105 00B6 89 5E F0 l12: mov word ptr un32,bx 106 00B9 89 7E F2 mov word ptr un32+2,di 107 00BC 8B 46 08 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво. 108 00BF 8B 56 0A mov dx,word ptr u0+2 109 00C2 8B CE mov cx,si 110 00C4 E3 06 jcxz l14 111 00C6 D1 E0 l13: shl ax,1 112 00C8 D1 D2 rcl dx,1 113 00CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри. 114 00CC 89 56 FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16); 115 00CF 89 46 FC mov un0,ax ; un0 = (unsigned short)un10; 116 00D2 3B 7E FA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване 117 00D5 C7 46 F6 FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене. 118 00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1. 119 00DC 8B 46 F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1); 120 00DF 8B 56 F2 mov dx,word ptr un32+2 121 00E2 F7 76 FA div vn1 122 00E5 89 46 F6 mov q1,ax 123 00E8 F7 66 FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1; 124 00EB 8B 4E F0 mov cx,word ptr un32 125 00EE 8B 5E F2 mov bx,word ptr un32+2 126 00F1 2B C8 sub cx,ax 127 00F3 1B DA sbb bx,dx 128 00F5 8B 46 F6 again1: mov ax,q1 ; again1: 129 00F8 F7 66 F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16) 130 00FB 3B D1 cmp dx,cx ; + un1) { 131 00FD 72 12 jb l17 132 00FF 77 05 ja l16 133 0101 3B 46 FE cmp ax,un1 134 0104 76 0B jbe l17 135 0106 FF 4E F6 l16: dec q1 ; q1--; 136 0109 03 4E FA add cx,vn1 ; rhat += vn1; 137 010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 138 010F 74 E4 jz again1 ; goto again1; 139 0111 8B 46 F6 l17: mov ax,q1 ; } 140 0114 F7 66 0E mul word ptr v+2 ; } // Умножи и извади. 141 0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v; 142 0118 8B 46 F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване

143 011B F7 66 0C mul word ptr v; CX = мл.д.(ст.д.(v)*q1) 144 011E 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1) 145 0120 8B 7E F0 mov di,word ptr un32 146 0123 8B 5E FE mov bx,un1 147 0126 2B D8 sub bx,ax 148 0128 1B FA sbb di,dx 149 012A 89 5E EC mov word ptr un21,bx

Page 67: Сборник задачи по програмиране на Асемблер

67Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

150 012D 89 7E EE mov word ptr un21+2,di 151 0130 3B 7E FA cmp di,vn1 152 0133 C7 46 F4 FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене. 153 0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0. 154 013A 8B 46 EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1); 155 013D 8B 56 EE mov dx,word ptr un21+2 156 0140 F7 76 FA div vn1 157 0143 89 46 F4 mov q0,ax 158 0146 F7 66 FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1; 159 0149 8B 4E EC mov cx,word ptr un21 160 014C 8B 5E EE mov bx,word ptr un21+2 161 014F 2B C8 sub cx,ax 162 0151 1B DA sbb bx,dx 163 0153 8B 46 F4 again0: mov ax,q0 ; again0: 164 0156 F7 66 F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16) 165 0159 3B D1 cmp dx,cx ; + un0) { 166 015B 72 12 jb l20 167 015D 77 05 ja l19 168 015F 3B 46 FC cmp ax,un0 169 0162 76 0B jbe l20 170 0164 FF 4E F4 l19: dec q0 ; q0--; 171 0167 03 4E FA add cx,vn1 ; rhat += vn1; 172 016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 173 016D 74 E4 jz again0 ; goto again0; 174 016F 8B 5E 10 l20: mov bx,r ; } 175 0172 0B DB or bx,bx ; } 176 0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го. 177 0176 8B 46 F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s; 178 0179 F7 66 0E mul word ptr v+2 179 017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0) 180 017D 8B 46 F4 mov ax,q0 181 0180 F7 66 0C mul word ptr v 182 0183 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0) 183 0185 8B 4E FC mov cx,un0 184 0188 8B 7E EC mov di,word ptr un21 185 018B 2B C8 sub cx,ax 186 018D 1B FA sbb di,dx 187 018F 87 CE xchg cx,si 188 0191 E3 06 jcxz l22 189 0193 D1 EF l21: shr di,1 190 0195 D1 DE rcr si,1 191 0197 E2 FA loop l21 192 0199 89 37 l22: mov [bx],si 193 019B 89 7F 02 mov [bx+2],di 194 019E 8B 46 F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0; 195 01A1 8B 56 F6 mov dx,q11 196 01A4 5E POP SI1 197 01A5 5F POP DI1 198 01A6 8B E5 MOV SP,BP1 199 01A8 5D POP BP1 200 01A9 C3 RET 00000h 201 01AA divlu2 endp ; } 202 end

14.1.7. Paradigm Assembler (PASM)

Това е всъщност вариант на TASM версия 5, чиито изходни текстове фирмата „Borland“ е продала на „ParadigmSystems“. Ето защо и PASM32 като TASM32 версия 5 е съвместим с MASM версия 6, а PASM като TASM – сMASM версия 5.1. Но PASM е единственият асемблер, който поддържа т.н. „разширен“ или „усърършенстван“режим – вариант на реалния с адресация на 16 MB памет (вж. т. 1). За съжаление в „Paradigm Systems“ са сепрестарали със защитата от копиране на своя продукт „Paradigm C++“, част от който е PASM. При всякоизвикване , изпълнимият файл („.EXE“) на асемблера се записва на диска като файл тип „.DLL“ от съдържаща гозащитна обвивка, извиква се и после се изтрива. Това предизвиква забавяне и дори износване в случай, че сеизползва полупроводников („SSD“) вместо твърд диск. Затова е по-добре файлът тип „.DLL“ да се възстановислед изтриване, да се преименува в „.EXE“ и да се изпълнява вместо гореспоменатата обвивка. Това се отнасякакто до PASM, така и до PASM32.

Page 68: Сборник задачи по програмиране на Асемблер

68Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Paradigm C++ Assembler 5.0 10/08/11 11:05:46 Page 1DIVLUSS.ASM

1 0000 .model small,c2 0000 .code3 ; /* Дълго делене, беззнаково (64/32 ==> 32).4 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово5 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.6 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя7 ; връща остатък 0xFFFFFFFF (невъзможна стойност).8 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.9 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */

10 ; 11 ; unsigned long 12 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 13 ; { 14 public divlu2 15 0000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word1 16 0000 55 PUSH BP1 17 0001 8B EC MOV BP,SP1 18 0003 57 PUSH DI1 19 0004 56 PUSH SI1 20 0005 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в 21 0008 8B 56 0E mov dx,word ptr v+2 22 000B 39 46 04 cmp word ptr u1,ax 23 000E 8B 5E 06 mov bx,word ptr u1+2 24 0011 1B DA sbb bx,dx 25 0013 72 12 jb l2 26 0015 8B 5E 10 mov bx,r 27 0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност 28 001A B8 FFFF mov ax,-1 ;ffffH 29 001D 74 05 jz l1 30 001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 31 0021 89 47 02 mov [bx+2],ax 32 0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 33 0025 EB 40 jmp short l8 ; } 34 0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод! 35 002A 33 C0 xor ax,ax 36 002C 33 D2 xor dx,dx 37 002E D1 66 08 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво 38 0031 D1 56 0A rcl word ptr u0+2,1 39 0034 D1 56 04 rcl word ptr u1,1 40 0037 D1 56 06 rcl word ptr u1+2,1 41 003A D1 D0 rcl ax,1 42 003C D1 D2 rcl dx,1 43 003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2) 44 0040 3B 46 0C cmp ax,word ptr v ; сравни DX:AX и делителя 45 0043 8B DA mov bx,dx 46 0045 1B 5E 0E sbb bx,word ptr v+2 ; DX:AX < делителя ? 47 0048 72 09 jb l5 ; да, прескочи 48 004A 2B 46 0C l4: sub ax,word ptr v ; не, извади делителя 49 004D 1B 56 0E sbb dx,word ptr v+2 50 0050 FF 46 08 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път 51 0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти 52 0055 8B 5E 10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го. 53 0058 0B DB or bx,bx 54 005A 74 05 jz l7 55 005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX 56 005E 89 57 02 mov [bx+2],dx 57 0061 8B 46 08 l7: mov ax,word ptr u0 58 0064 8B 56 0A mov dx,word ptr u0+21 59 0067 5E POP SI1 60 0068 5F POP DI1 61 0069 5D POP BP1 62 006A C3 RET 00000h 63 006B divlu2 endp ; } 64 end

Page 69: Сборник задачи по програмиране на Асемблер

69Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Paradigm C++ Assembler 5.0 10/08/11 11:05:52 Page 1DIVLU2.ASM

1 0000 .model small,c2 0000 .code3 ; /* Дълго делене, беззнаково (64/32 ==> 32).4 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово5 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.6 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя7 ; връща остатък 0xFFFFFFFF (невъзможна стойност).8 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.9 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */

10 ; 11 ; unsigned long 12 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 13 ; { 14 public divlu2 15 0000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word 16 ; unsigned short un1, un0, // Нормализирани младши цифри на делимото. 17 ; vn1, vn0, // Нормализирани цифри на делителя. 18 ; q1, q0; // Цифри на частното. 19 ; unsigned long un32, un21, un10, // Двойки цифри на делимото. 20 ; rhat; // Остатък. 21 ; int s; // Колко бита изместваме за нормализация. 22 local un1:word,un0:word,vn1:word,vn0:word,q1:word,q0:word,un32:dword,un21:dword1 23 0000 55 PUSH BP1 24 0001 8B EC MOV BP,SP1 25 0003 83 EC 14 SUB SP,00014h1 26 0006 57 PUSH DI1 27 0007 56 PUSH SI1 28 0008 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в 29 000B 8B 56 0E mov dx,word ptr v+2 30 000E 39 46 04 cmp word ptr u1,ax 31 0011 8B 5E 06 mov bx,word ptr u1+2 32 0014 1B DA sbb bx,dx 33 0016 72 13 jb l2 34 0018 8B 5E 10 mov bx,r 35 001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност 36 001D B8 FFFF mov ax,-1 ;ffffH 37 0020 74 05 jz l1 38 0022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 39 0024 89 47 02 mov [bx+2],ax 40 0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 41 0028 E9 0179 jmp l24 ; } 42 002B 8B F2 l2: mov si,dx ; if (v == 0) 43 002D 0B F0 or si,ax 44 002F BE 0020 mov si,32 ;0020H ; s = 32; 45 0032 74 4C jz l7 ; else { // 0 <= s <= 31. 46 0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя. 47 0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) { 48 0038 75 06 jne l3 49 003A BE 0010 mov si,16 ;0010H ; s += 16; 50 003D 92 xchg ax,dx ; v <<= 16; 51 003E 33 C0 xor ax,ax 52 0040 0A F6 l3: or dh,dh ; } 53 0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) { 54 0044 83 C6 08 add si,8 ; s += 8; 55 0047 8A F2 mov dh,dl ; v <<= 8; 56 0049 8A D4 mov dl,ah 57 004B 8A E0 mov ah,al 58 004D 2A C0 sub al,al 59 004F 80 FE 0F l4: cmp dh,0Fh ; } 60 0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) { 61 0054 83 C6 04 add si,4 ; s += 4; 62 0057 D1 E0 shl ax,1 ; v <<= 4; 63 0059 D1 D2 rcl dx,1 64 005B D1 E0 shl ax,1 65 005D D1 D2 rcl dx,1 66 005F D1 E0 shl ax,1 67 0061 D1 D2 rcl dx,1 68 0063 D1 E0 shl ax,1 69 0065 D1 D2 rcl dx,1 70 0067 80 FE 3F l5: cmp dh,3Fh ; } 71 006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) { 72 006C 83 C6 02 add si,2 ; s += 2;

Page 70: Сборник задачи по програмиране на Асемблер

70Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

73 006F D1 E0 shl ax,1 ; v <<= 2; 74 0071 D1 D2 rcl dx,1 75 0073 D1 E0 shl ax,1 76 0075 D1 D2 rcl dx,1 77 0077 0B D2 l6: or dx,dx ; } 78 0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) { 79 007B 46 inc si ; s++; 80 007C D1 E0 shl ax,1 ; v <<= 1; 81 007E D1 D2 rcl dx,1 82 0080 89 46 0C l7: mov word ptr v,ax ; } 83 0083 89 56 0E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри. 84 0086 89 56 FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16); 85 0089 89 46 F8 mov vn0,ax ; vn0 = (unsigned short)v; 86 008C 8B CE mov cx,si ; un32 = (u1 << s) | 87 008E 8B 5E 04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31)); 88 0091 8B 7E 06 mov di,word ptr u1+2 89 0094 E3 06 jcxz l9 90 0096 D1 E3 l8: shl bx,1 91 0098 D1 D7 rcl di,1 92 009A E2 FA loop l8 93 009C B1 20 l9: mov cl,32 ;0020H 94 009E 2B CE sub cx,si 95 00A0 8B 46 08 mov ax,word ptr u0 96 00A3 8B 56 0A mov dx,word ptr u0+2 97 00A6 74 06 jz l11 98 00A8 D1 EA l10: shr dx,1 99 00AA D1 D8 rcr ax,1 100 00AC E2 FA loop l10 101 00AE 0B F6 l11: or si,si 102 00B0 74 04 jz l12 103 00B2 0B D8 or bx,ax 104 00B4 0B FA or di,dx 105 00B6 89 5E F0 l12: mov word ptr un32,bx 106 00B9 89 7E F2 mov word ptr un32+2,di 107 00BC 8B 46 08 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво. 108 00BF 8B 56 0A mov dx,word ptr u0+2 109 00C2 8B CE mov cx,si 110 00C4 E3 06 jcxz l14 111 00C6 D1 E0 l13: shl ax,1 112 00C8 D1 D2 rcl dx,1 113 00CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри. 114 00CC 89 56 FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16); 115 00CF 89 46 FC mov un0,ax ; un0 = (unsigned short)un10; 116 00D2 3B 7E FA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване 117 00D5 C7 46 F6 FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене. 118 00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1. 119 00DC 8B 46 F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1); 120 00DF 8B 56 F2 mov dx,word ptr un32+2 121 00E2 F7 76 FA div vn1 122 00E5 89 46 F6 mov q1,ax 123 00E8 F7 66 FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1; 124 00EB 8B 4E F0 mov cx,word ptr un32 125 00EE 8B 5E F2 mov bx,word ptr un32+2 126 00F1 2B C8 sub cx,ax 127 00F3 1B DA sbb bx,dx 128 00F5 8B 46 F6 again1: mov ax,q1 ; again1: 129 00F8 F7 66 F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16) 130 00FB 3B D1 cmp dx,cx ; + un1) { 131 00FD 72 12 jb l17 132 00FF 77 05 ja l16 133 0101 3B 46 FE cmp ax,un1 134 0104 76 0B jbe l17 135 0106 FF 4E F6 l16: dec q1 ; q1--; 136 0109 03 4E FA add cx,vn1 ; rhat += vn1; 137 010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 138 010F 74 E4 jz again1 ; goto again1; 139 0111 8B 46 F6 l17: mov ax,q1 ; } 140 0114 F7 66 0E mul word ptr v+2 ; } // Умножи и извади. 141 0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v; 142 0118 8B 46 F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване

143 011B F7 66 0C mul word ptr v; CX = мл.д.(ст.д.(v)*q1) 144 011E 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1) 145 0120 8B 7E F0 mov di,word ptr un32 146 0123 8B 5E FE mov bx,un1 147 0126 2B D8 sub bx,ax 148 0128 1B FA sbb di,dx 149 012A 89 5E EC mov word ptr un21,bx

Page 71: Сборник задачи по програмиране на Асемблер

71Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

150 012D 89 7E EE mov word ptr un21+2,di 151 0130 3B 7E FA cmp di,vn1 152 0133 C7 46 F4 FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене. 153 0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0. 154 013A 8B 46 EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1); 155 013D 8B 56 EE mov dx,word ptr un21+2 156 0140 F7 76 FA div vn1 157 0143 89 46 F4 mov q0,ax 158 0146 F7 66 FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1; 159 0149 8B 4E EC mov cx,word ptr un21 160 014C 8B 5E EE mov bx,word ptr un21+2 161 014F 2B C8 sub cx,ax 162 0151 1B DA sbb bx,dx 163 0153 8B 46 F4 again0: mov ax,q0 ; again0: 164 0156 F7 66 F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16) 165 0159 3B D1 cmp dx,cx ; + un0) { 166 015B 72 12 jb l20 167 015D 77 05 ja l19 168 015F 3B 46 FC cmp ax,un0 169 0162 76 0B jbe l20 170 0164 FF 4E F4 l19: dec q0 ; q0--; 171 0167 03 4E FA add cx,vn1 ; rhat += vn1; 172 016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 173 016D 74 E4 jz again0 ; goto again0; 174 016F 8B 5E 10 l20: mov bx,r ; } 175 0172 0B DB or bx,bx ; } 176 0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го. 177 0176 8B 46 F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s; 178 0179 F7 66 0E mul word ptr v+2 179 017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0) 180 017D 8B 46 F4 mov ax,q0 181 0180 F7 66 0C mul word ptr v 182 0183 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0) 183 0185 8B 4E FC mov cx,un0 184 0188 8B 7E EC mov di,word ptr un21 185 018B 2B C8 sub cx,ax 186 018D 1B FA sbb di,dx 187 018F 87 CE xchg cx,si 188 0191 E3 06 jcxz l22 189 0193 D1 EF l21: shr di,1 190 0195 D1 DE rcr si,1 191 0197 E2 FA loop l21 192 0199 89 37 l22: mov [bx],si 193 019B 89 7F 02 mov [bx+2],di 194 019E 8B 46 F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0; 195 01A1 8B 56 F6 mov dx,q11 196 01A4 5E POP SI1 197 01A5 5F POP DI1 198 01A6 8B E5 MOV SP,BP1 199 01A8 5D POP BP1 200 01A9 C3 RET 00000h 201 01AA divlu2 endp ; } 202 end

14.1.8. IBM Assembly Language Processor (ALP)

Този асемблер е част от пакета „VusualAge C++“ на фирмата IBM за тяхната операционна система „OS/2“. Той есъвместим с MASM версия 5.1 и частично с 6. Има много богат набор от ключове за практически пълен контролвърху работата му (особено на листинга), много по-голям, отколкото при всички други асемблери, разглежданитук.

Page 72: Сборник задачи по програмиране на Асемблер

72Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

File: divluss.asm Pass: 2 ALP: Assembly Language Processor Ver 4.00.005 Page: 1

yyy-oooo-ggggggggggggggg-z-m-ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

1 0000 .model small,c2 .code3 ; /* Дълго делене, беззнаково (64/32 ==> 32).4 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово5 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.6 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя7 ; връща остатък 0xFFFFFFFF (невъзможна стойност).8 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.9 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */10 ;11 ; unsigned long12 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)13 ; {14 public divlu215 0000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word16 0000 55 1 1> push bp16 0001 8B EC 2 1> mov bp,sp16 0003 57 3 1> push DI16 0004 56 4 1> push SI16 0005 8B 46 [0C] mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в17 0008 8B 56 [0E] mov dx,word ptr v+218 000B 39 46 [04] cmp word ptr u1,ax19 000E 8B 5E [06] mov bx,word ptr u1+220 0011 1B DA sbb bx,dx21 0013 72 12 jb l222 0015 8B 5E [10] mov bx,r23 0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност24 001A B8 FFFF mov ax,-1 ;ffffH25 001D 74 05 jz l126 001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото27 0021 89 47 [02] mov [bx+2],ax28 0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.29 0025 EB 40 jmp short l8 ; }30 0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод!31 002A 33 C0 xor ax,ax32 002C 33 D2 xor dx,dx33 002E D1 66 [08] l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво34 0031 D1 56 [0A] rcl word ptr u0+2,135 0034 D1 56 [04] rcl word ptr u1,136 0037 D1 56 [06] rcl word ptr u1+2,137 003A D1 D0 rcl ax,138 003C D1 D2 rcl dx,139 003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)40 0040 3B 46 [0C] cmp ax,word ptr v ; сравни DX:AX и делителя41 0043 8B DA mov bx,dx42 0045 1B 5E [0E] sbb bx,word ptr v+2 ; DX:AX < делителя ?43 0048 72 09 jb l5 да, прескочи44 004A 2B 46 [0C] l4: sub ax,word ptr v не, извади делителя45 004D 1B 56 [0E] sbb dx,word ptr v+246 0050 FF 46 [08] inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път47 0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти48 0055 8B 5E [10] mov bx,r ; if (r) // Ако се иска остатъкът, върни го.49 0058 0B DB or bx,bx50 005A 74 05 jz l751 005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX52 005E 89 57 [02] mov [bx+2],dx53 0061 8B 46 [08] l7: mov ax,word ptr u054 0064 8B 56 [0A] mov dx,word ptr u0+255 0067 l8: ret55 0067 5E 1 1> pop SI55 0068 5F 2 1> pop DI55 0069 5D 3 1> pop bp55 006A C3 4 1> retn 00000h56 006B divlu2 endp ; }57 end

Page 73: Сборник задачи по програмиране на Асемблер

73Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

File: divlu2.asm Pass: 2 ALP: Assembly Language Processor Ver 4.00.005 Page: 1

yyy-oooo-ggggggggggggggg-z-m-ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss

1 0000 .model small,c2 .code3 ; /* Дълго делене, беззнаково (64/32 ==> 32).4 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово5 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.6 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя7 ; връща остатък 0xFFFFFFFF (невъзможна стойност).8 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.9 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */10 ;11 ; unsigned long12 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)13 ; {14 public divlu215 0000 divlu2 proc uses di si, u1:dword, u0:dword, v:dword, r:word16 ; unsigned short un1, un0, // Нормализирани младши цифри на делимото.17 ; vn1, vn0, // Нормализирани цифри на делителя.18 ; q1, q0; // Цифри на частното.19 ; unsigned long un32, un21, un10, // Двойки цифри на делимото.20 ; rhat; // Остатък.21 ; int s; // Колко бита изместваме за нормализация.22 local un1:word,un0:word,vn1:word,vn0:word,q1:word,q0:word,un32:dword,un21:dword23 0000 55 1 1> push bp23 0001 8B EC 2 1> mov bp,sp23 0003 83 C4 EC 3 1> add sp,0FFECh23 0006 57 4 1> push DI23 0007 56 5 1> push SI23 0008 8B 46 [0C] mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в24 000B 8B 56 [0E] mov dx,word ptr v+225 000E 39 46 [04] cmp word ptr u1,ax26 0011 8B 5E [06] mov bx,word ptr u1+227 0014 1B DA sbb bx,dx28 0016 72 13 jb l229 0018 8B 5E [10] mov bx,r30 001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност31 001D B8 FFFF mov ax,-1 ;ffffH32 0020 74 05 jz l133 0022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото34 0024 89 47 [02] mov [bx+2],ax35 0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.36 0028 E9 01A4:R jmp l24 ; }37 002B 8B F2 l2: mov si,dx ; if (v == 0)38 002D 0B F0 or si,ax39 002F BE 0020 mov si,32 ;0020H ; s = 32;40 0032 74 4C jz l7 ; else { // 0 <= s <= 31.41 0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя.42 0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) {43 0038 75 06 jne l344 003A BE 0010 mov si,16 ;0010H ; s += 16;45 003D 92 xchg ax,dx ; v <<= 16;46 003E 33 C0 xor ax,ax47 0040 0A F6 l3: or dh,dh ; }48 0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) {49 0044 83 C6 08 add si,8 ; s += 8;50 0047 8A F2 mov dh,dl ; v <<= 8;51 0049 8A D4 mov dl,ah52 004B 8A E0 mov ah,al53 004D 2A C0 sub al,al54 004F 80 FE 0F l4: cmp dh,0Fh ; }55 0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) {56 0054 83 C6 04 add si,4 ; s += 4;57 0057 D1 E0 shl ax,1 ; v <<= 4;58 0059 D1 D2 rcl dx,159 005B D1 E0 shl ax,160 005D D1 D2 rcl dx,161 005F D1 E0 shl ax,162 0061 D1 D2 rcl dx,163 0063 D1 E0 shl ax,164 0065 D1 D2 rcl dx,165 0067 80 FE 3F l5: cmp dh,3Fh ; }66 006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) {67 006C 83 C6 02 add si,2 ; s += 2;

Page 74: Сборник задачи по програмиране на Асемблер

74Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

68 006F D1 E0 shl ax,1 ; v <<= 2;69 0071 D1 D2 rcl dx,170 0073 D1 E0 shl ax,171 0075 D1 D2 rcl dx,172 0077 0B D2 l6: or dx,dx ; }73 0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) {74 007B 46 inc si ; s++;75 007C D1 E0 shl ax,1 ; v <<= 1;76 007E D1 D2 rcl dx,177 0080 89 46 [0C] l7: mov word ptr v,ax ; }78 0083 89 56 [0E] mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри.79 0086 89 56 [FA] mov vn1,dx ; vn1 = (unsigned short)(v >> 16);80 0089 89 46 [F8] mov vn0,ax ; vn0 = (unsigned short)v;81 008C 8B CE mov cx,si ; un32 = (u1 << s) |82 008E 8B 5E [04] mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31));83 0091 8B 7E [06] mov di,word ptr u1+284 0094 E3 06 jcxz l985 0096 D1 E3 l8: shl bx,186 0098 D1 D7 rcl di,187 009A E2 FA loop l888 009C B1 20 l9: mov cl,32 ;0020H89 009E 2B CE sub cx,si90 00A0 8B 46 [08] mov ax,word ptr u091 00A3 8B 56 [0A] mov dx,word ptr u0+292 00A6 74 06 jz l1193 00A8 D1 EA l10: shr dx,194 00AA D1 D8 rcr ax,195 00AC E2 FA loop l1096 00AE 0B F6 l11: or si,si97 00B0 74 04 jz l1298 00B2 0B D8 or bx,ax99 00B4 0B FA or di,dx100 00B6 89 5E [F0] l12: mov word ptr un32,bx101 00B9 89 7E [F2] mov word ptr un32+2,di102 00BC 8B 46 [08] mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво.103 00BF 8B 56 [0A] mov dx,word ptr u0+2104 00C2 8B CE mov cx,si105 00C4 E3 06 jcxz l14106 00C6 D1 E0 l13: shl ax,1107 00C8 D1 D2 rcl dx,1108 00CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри.109 00CC 89 56 [FE] l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16);110 00CF 89 46 [FC] mov un0,ax ; un0 = (unsigned short)un10;111 00D2 3B 7E [FA] cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване112 00D5 C7 46 [F6] FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.113 00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1.114 00DC 8B 46 [F0] mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1);115 00DF 8B 56 [F2] mov dx,word ptr un32+2116 00E2 F7 76 [FA] div vn1117 00E5 89 46 [F6] mov q1,ax118 00E8 F7 66 [FA] mul vn1 ; rhat = un32 - (unsigned long)q1*vn1;119 00EB 8B 4E [F0] mov cx,word ptr un32120 00EE 8B 5E [F2] mov bx,word ptr un32+2121 00F1 2B C8 sub cx,ax122 00F3 1B DA sbb bx,dx123 00F5 8B 46 [F6] again1: mov ax,q1 ; again1:124 00F8 F7 66 [F8] mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16)125 00FB 3B D1 cmp dx,cx ; + un1) {126 00FD 72 12 jb l17127 00FF 77 05 ja l16128 0101 3B 46 [FE] cmp ax,un1129 0104 76 0B jbe l17130 0106 FF 4E [F6] l16: dec q1 ; q1--;131 0109 03 4E [FA] add cx,vn1 ; rhat += vn1;132 010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)133 010F 74 E4 jz again1 ; goto again1;134 0111 8B 46 [F6] l17: mov ax,q1 ; }135 0114 F7 66 [0E] mul word ptr v+2 ; } // Умножи и извади.136 0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;137 0118 8B 46 [F6] mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване138 011B F7 66 [0C] mul word ptr v; CX = мл.д.(ст.д.(v)*q1)139 011E 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)140 0120 8B 7E [F0] mov di,word ptr un32141 0123 8B 5E [FE] mov bx,un1142 0126 2B D8 sub bx,ax143 0128 1B FA sbb di,dx144 012A 89 5E [EC] mov word ptr un21,bx

Page 75: Сборник задачи по програмиране на Асемблер

75Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

145 012D 89 7E [EE] mov word ptr un21+2,di146 0130 3B 7E [FA] cmp di,vn1147 0133 C7 46 [F4] FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене.148 0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0.149 013A 8B 46 [EC] mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1);150 013D 8B 56 [EE] mov dx,word ptr un21+2151 0140 F7 76 [FA] div vn1152 0143 89 46 [F4] mov q0,ax153 0146 F7 66 [FA] mul vn1 ; rhat = un21 - (unsigned long)q0*vn1;154 0149 8B 4E [EC] mov cx,word ptr un21155 014C 8B 5E [EE] mov bx,word ptr un21+2156 014F 2B C8 sub cx,ax157 0151 1B DA sbb bx,dx158 0153 8B 46 [F4] again0: mov ax,q0 ; again0:159 0156 F7 66 [F8] mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16)160 0159 3B D1 cmp dx,cx ; + un0) {161 015B 72 12 jb l20162 015D 77 05 ja l19163 015F 3B 46 [FC] cmp ax,un0164 0162 76 0B jbe l20165 0164 FF 4E [F4] l19: dec q0 ; q0--;166 0167 03 4E [FA] add cx,vn1 ; rhat += vn1;167 016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)168 016D 74 E4 jz again0 ; goto again0;169 016F 8B 5E [10] l20: mov bx,r ; }170 0172 0B DB or bx,bx ; }171 0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го.172 0176 8B 46 [F4] mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s;173 0179 F7 66 [0E] mul word ptr v+2174 017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)175 017D 8B 46 [F4] mov ax,q0176 0180 F7 66 [0C] mul word ptr v177 0183 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)178 0185 8B 4E [FC] mov cx,un0179 0188 8B 7E [EC] mov di,word ptr un21180 018B 2B C8 sub cx,ax181 018D 1B FA sbb di,dx182 018F 87 CE xchg cx,si183 0191 E3 06 jcxz l22184 0193 D1 EF l21: shr di,1185 0195 D1 DE rcr si,1186 0197 E2 FA loop l21187 0199 89 37 l22: mov [bx],si188 019B 89 7F [02] mov [bx+2],di189 019E 8B 46 [F4] l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0;190 01A1 8B 56 [F6] mov dx,q1191 01A4 l24: ret191 01A4 5E 1 1> pop SI191 01A5 5F 2 1> pop DI191 01A6 8B E5 3 1> mov sp,bp191 01A8 5D 4 1> pop bp191 01A9 C3 5 1> retn 00000h192 01AA divlu2 endp ; }193 end

14.2. Съвместими с по-стари версии на MASM

В тази версия на решенията на зад. №№49–50 (тт. 13.4–13.5) се използват следните директиви на по-старите от5.1 версии на MASM, които не са разгледани в т. 14.1: „name“, „segment“, „assume“ и „ends“. (Директивата„equ“ е използвана в зад. №№20, 31 и 32 (тт. 6.4, 8.4 и 8.5), а директивите „page“, „public“ и „end“ са разгледани вт. 14.1.) Всички тези директиви са от ниско ниво (директивите от високо ниво започват от MASM версия 5.1).Но те се поддържат и в най-новите версии на асемблерите. Ето защо тази версия на решенията се асемблирауспешно и от всички асемблери от т. 14.1.

„name“: задава името на модула на обектния файл.„segment“: задава името и атрибутите на сегмента. В конкретния случай „_TEXT segment public 'CODE'“означава, че (1) сегментът с име „_TEXT“ е общ („public“), т.е. дори да е разпръснат по изходния текст,тези негови части се обединяват 1 сегмент и (2) класът на сегмента е „CODE“ (за даннов сегмент се задава„DATA “). Има и много други незадължителни атрибути на сегмента.„assume“: задава съответствието между сегментен регистър и име на сегмент или група сегменти. Могатда се задават и повече от 1 съответствия, като се разделят със запетая.

Page 76: Сборник задачи по програмиране на Асемблер

76Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

„ends“: маркира края на сегмент, структура или обединение.

Заб.: Обърнете внимание , че тялото на процедурите в тази и предходната (в т. 14.1.) версии на решенията на зад.№№49–50 (тт. 13.4–13.5) е едно и също. Разликата е само в директивите преди и след него и в това, чеавтоматично генерираните при предходната версия от асемблера „пролог“ и „епилог“ тук са „ръчно“ добавени визходните текстове.

14.2.1. IBM Personal Computer Assembler (ASM)

Към версии 1 (1981 г.) и 2 (1984 г.) на MASM се прилага и „ASM“ – по-малък асемблер без поддръжка намакроси за компютри с малко памет (напр. първият „IBM PC“ има само 64 KB RAM!). Представеният по-долулистинг е генериран от ASM версия 1 (1981 г.). Това е най-старата версия асемблер измежду разглежданите, нои той успешно асемблира тази версия на решенията на зад. №№49–50 (тт. 13.4–13.5).

Заб.: Символът на цифрата на десетиците на годината в заглавния ред, който би трябвало да бъде „1“, е... точкаи запетая „;“ (защо?).

Page 77: Сборник задачи по програмиране на Асемблер

77Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

The IBM Personal Computer Assembler 08-28-;1 PAGE 1-1

page 255,125 name divlu20000 _TEXT segment public 'CODE' assume cs:_TEXT

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public _divlu20000 _divlu2: = u1 equ [bp+4] = u0 equ [bp+8] = v equ [bp+12] = r equ [bp+16]0000 55 push bp0001 8B EC mov bp,sp0003 57 push di0004 56 push si0005 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в0008 8B 56 0E mov dx,word ptr v+2000B 39 46 04 cmp word ptr u1,ax000E 8B 5E 06 mov bx,word ptr u1+20011 1B DA sbb bx,dx0013 72 12 jb l20015 8B 5E 10 mov bx,r0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност001A B8 FFFF mov ax,-1 ;ffffH001D 74 05 jz l1001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0021 89 47 02 mov [bx+2],ax0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0025 EB 40 jmp short l8 ; }0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод!002A 33 C0 xor ax,ax002C 33 D2 xor dx,dx002E D1 66 08 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво0031 D1 56 0A rcl word ptr u0+2,10034 D1 56 04 rcl word ptr u1,10037 D1 56 06 rcl word ptr u1+2,1003A D1 D0 rcl ax,1003C D1 D2 rcl dx,1003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)0040 3B 46 0C cmp ax,word ptr v ; сравни DX:AX и делителя0043 8B DA mov bx,dx0045 1B 5E 0E sbb bx,word ptr v+2 ; DX:AX < делителя ?0048 72 09 jb l5 ; да, прескочи004A 2B 46 0C l4: sub ax,word ptr v ; не, извади делителя004D 1B 56 0E sbb dx,word ptr v+20050 FF 46 08 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти0055 8B 5E 10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го.0058 0B DB or bx,bx005A 74 05 jz l7005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX005E 89 57 02 mov [bx+2],dx0061 8B 46 08 l7: mov ax,word ptr u00064 8B 56 0A mov dx,word ptr u0+20067 5E l8: pop si0068 5F pop di0069 5D pop bp006A C3 ret ; }006B _TEXT ends end

Page 78: Сборник задачи по програмиране на Асемблер

78Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

The IBM Personal Computer Assembler 08-28-;1 PAGE 1-1

page 255,125 name divlu20000 _TEXT segment public 'CODE' assume cs:_TEXT

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public _divlu20000 _divlu2: = u1 equ [bp+4] = u0 equ [bp+8] = v equ [bp+12] = r equ [bp+16]

; unsigned short un1, un0, // Нормализирани младши цифри на делимото.; vn1, vn0, // Нормализирани цифри на делителя.; q1, q0; // Цифри на частното.; unsigned long un32, un21, un10, // Двойки цифри на делимото.; rhat; // Остатък.; int s; // Колко бита изместваме за нормализация.

= un1 equ word ptr [bp-2] = un0 equ word ptr [bp-4] = vn1 equ word ptr [bp-6] = vn0 equ word ptr [bp-8] = q1 equ word ptr [bp-10] = q0 equ word ptr [bp-12] = un32 equ [bp-16] = un21 equ [bp-20]0000 55 push bp0001 8B EC mov bp,sp0003 83 C4 EC add sp,-200006 57 push di0007 56 push si0008 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в000B 8B 56 0E mov dx,word ptr v+2000E 39 46 04 cmp word ptr u1,ax0011 8B 5E 06 mov bx,word ptr u1+20014 1B DA sbb bx,dx0016 72 13 jb l20018 8B 5E 10 mov bx,r001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност001D B8 FFFF mov ax,-1 ;ffffH0020 74 05 jz l10022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0024 89 47 02 mov [bx+2],ax0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0028 E9 01A4 R jmp l24 ; }002B 8B F2 l2: mov si,dx ; if (v == 0)002D 0B F0 or si,ax002F BE 0020 mov si,32 ;0020H ; s = 32;0032 74 4C jz l7 ; else { // 0 <= s <= 31.0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя.0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) {0038 75 06 jne l3003A BE 0010 mov si,16 ;0010H ; s += 16;003D 92 xchg ax,dx ; v <<= 16;003E 33 C0 xor ax,ax0040 0A F6 l3: or dh,dh ; }0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) {0044 83 C6 08 add si,8 ; s += 8;0047 8A F2 mov dh,dl ; v <<= 8;0049 8A D4 mov dl,ah004B 8A E0 mov ah,al004D 2A C0 sub al,al004F 80 FE 0F l4: cmp dh,0Fh ; }0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) {

Page 79: Сборник задачи по програмиране на Асемблер

79Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0054 83 C6 04 add si,4 ; s += 4;0057 D1 E0 shl ax,1 ; v <<= 4;0059 D1 D2 rcl dx,1005B D1 E0 shl ax,1005D D1 D2 rcl dx,1005F D1 E0 shl ax,10061 D1 D2 rcl dx,10063 D1 E0 shl ax,10065 D1 D2 rcl dx,10067 80 FE 3F l5: cmp dh,3Fh ; }006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) {006C 83 C6 02 add si,2 ; s += 2;006F D1 E0 shl ax,1 ; v <<= 2;0071 D1 D2 rcl dx,10073 D1 E0 shl ax,10075 D1 D2 rcl dx,10077 0B D2 l6: or dx,dx ; }0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) {007B 46 inc si ; s++;007C D1 E0 shl ax,1 ; v <<= 1;007E D1 D2 rcl dx,10080 89 46 0C l7: mov word ptr v,ax ; }0083 89 56 0E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри.0086 89 56 FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16);0089 89 46 F8 mov vn0,ax ; vn0 = (unsigned short)v;008C 8B CE mov cx,si ; un32 = (u1 << s) |008E 8B 5E 04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31));0091 8B 7E 06 mov di,word ptr u1+20094 E3 06 jcxz l90096 D1 E3 l8: shl bx,10098 D1 D7 rcl di,1009A E2 FA loop l8009C B1 20 l9: mov cl,32 ;0020H009E 2B CE sub cx,si00A0 8B 46 08 mov ax,word ptr u000A3 8B 56 0A mov dx,word ptr u0+200A6 74 06 jz l1100A8 D1 EA l10: shr dx,100AA D1 D8 rcr ax,100AC E2 FA loop l1000AE 0B F6 l11: or si,si00B0 74 04 jz l1200B2 0B D8 or bx,ax00B4 0B FA or di,dx00B6 89 5E F0 l12: mov word ptr un32,bx00B9 89 7E F2 mov word ptr un32+2,di00BC 8B 46 08 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво.00BF 8B 56 0A mov dx,word ptr u0+200C2 8B CE mov cx,si00C4 E3 06 jcxz l1400C6 D1 E0 l13: shl ax,100C8 D1 D2 rcl dx,100CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри.00CC 89 56 FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16);00CF 89 46 FC mov un0,ax ; un0 = (unsigned short)un10;00D2 3B 7E FA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване00D5 C7 46 F6 FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1.00DC 8B 46 F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1);00DF 8B 56 F2 mov dx,word ptr un32+200E2 F7 76 FA div vn100E5 89 46 F6 mov q1,ax00E8 F7 66 FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1;00EB 8B 4E F0 mov cx,word ptr un3200EE 8B 5E F2 mov bx,word ptr un32+200F1 2B C8 sub cx,ax00F3 1B DA sbb bx,dx00F5 8B 46 F6 again1: mov ax,q1 ; again1:00F8 F7 66 F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16)00FB 3B D1 cmp dx,cx ; + un1) {00FD 72 12 jb l1700FF 77 05 ja l160101 3B 46 FE cmp ax,un10104 76 0B jbe l170106 FF 4E F6 l16: dec q1 ; q1--;0109 03 4E FA add cx,vn1 ; rhat += vn1;010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)

Page 80: Сборник задачи по програмиране на Асемблер

80Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

010F 74 E4 jz again1 ; goto again1;0111 8B 46 F6 l17: mov ax,q1 ; }0114 F7 66 0E mul word ptr v+2 ; } // Умножи и извади.0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;0118 8B 46 F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване011B F7 66 0C mul word ptr v; CX = мл.д.(ст.д.(v)*q1)011E 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)0120 8B 7E F0 mov di,word ptr un320123 8B 5E FE mov bx,un10126 2B D8 sub bx,ax0128 1B FA sbb di,dx012A 89 5E EC mov word ptr un21,bx012D 89 7E EE mov word ptr un21+2,di0130 3B 7E FA cmp di,vn10133 C7 46 F4 FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене.0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0.013A 8B 46 EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1);013D 8B 56 EE mov dx,word ptr un21+20140 F7 76 FA div vn10143 89 46 F4 mov q0,ax0146 F7 66 FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1;0149 8B 4E EC mov cx,word ptr un21014C 8B 5E EE mov bx,word ptr un21+2014F 2B C8 sub cx,ax0151 1B DA sbb bx,dx0153 8B 46 F4 again0: mov ax,q0 ; again0:0156 F7 66 F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16)0159 3B D1 cmp dx,cx ; + un0) {015B 72 12 jb l20015D 77 05 ja l19015F 3B 46 FC cmp ax,un00162 76 0B jbe l200164 FF 4E F4 l19: dec q0 ; q0--;0167 03 4E FA add cx,vn1 ; rhat += vn1;016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)016D 74 E4 jz again0 ; goto again0;016F 8B 5E 10 l20: mov bx,r ; }0172 0B DB or bx,bx ; }0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го.0176 8B 46 F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s;0179 F7 66 0E mul word ptr v+2017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)017D 8B 46 F4 mov ax,q00180 F7 66 0C mul word ptr v0183 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)0185 8B 4E FC mov cx,un00188 8B 7E EC mov di,word ptr un21018B 2B C8 sub cx,ax018D 1B FA sbb di,dx018F 87 CE xchg cx,si0191 E3 06 jcxz l220193 D1 EF l21: shr di,10195 D1 DE rcr si,10197 E2 FA loop l210199 89 37 l22: mov [bx],si019B 89 7F 02 mov [bx+2],di019E 8B 46 F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0;01A1 8B 56 F6 mov dx,q101A4 5E l24: pop si01A5 5F pop di01A6 8B E5 mov sp,bp01A8 5D pop bp01A9 C3 ret ; }01AA _TEXT ends end

14.2.2. Turbo Editasm (TASMB)

От 1984 г. до 1987 г. фирмата „Speedware“ разработва продукта „Turbo Editasm“ (автор – Uriah Barnett). Това еинтегрирана среда за разработка на програми на Асемблер. Името на изпълнимия ѝ файл е „TASMB “. Тявключва вграден редактор, асемблер и възможност за изобразяване на съдържанието на регистрите на екранапри програмно прекъсване 20h за завършване на програмата. Може да генерира обектни, символни , листинговии изпълними файлове тип „.COM“. Интересна е възможността за генериране и изпълнение на машинния коднаправо в оперативната памет, без да се минава през обектен и изпълним файл.

Page 81: Сборник задачи по програмиране на Асемблер

81Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

14.2.3. Turbo EditasmMacro Assembler (TASMC)

Това е асемблер с име на изпълнимия файл „TASMC “ в пакета „Turbo Editasm“, който може да генерираобектни, листингови и изпълними файлове тип „.COM“. Листингите на TASMB и TASMC се различават самопо това, че TASMC неизвестно защо преброява в края 1 ред изходен текст в повече. Затова тук е даден самолистингът, генериран от TASMB .

Заб.: TASMB и TASMC поддържат командите на микропроцесорите до 286 включително , но за съжалениегенерират 1 байт по-малко за командата „enter“. В края на 1987 г. или началото на 1988 г. са купени от фирмата„Borland“ и служат за основа на техния TASM, чиято първа версия излиза през 1988 г. (вж. т. 14.1.6).

Page 82: Сборник задачи по програмиране на Асемблер

82Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

TURBO ASSEMBLER July 27,2011 03:28:01 p.m. Page 1

1 name divlu22 0000 _TEXT segment public 'CODE'3 assume cs:_TEXT4 ; /* Дълго делене, беззнаково (64/32 ==> 32).5 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово6 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.7 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя8 ; връща остатък 0xFFFFFFFF (невъзможна стойност).9 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.

10 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */ 11 ; 12 ; unsigned long 13 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 14 ; { 15 public _divlu2 16 0000 _divlu2: 17 = u1 equ [bp+4] 18 = u0 equ [bp+8] 19 = v equ [bp+12] 20 = r equ [bp+16] 21 0000 55 push bp 22 0001 8B EC mov bp,sp 23 0003 57 push di 24 0004 56 push si 25 0005 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в 26 0008 8B 56 0E mov dx,word ptr v+2 27 000B 39 46 04 cmp word ptr u1,ax 28 000E 8B 5E 06 mov bx,word ptr u1+2 29 0011 19 D3 sbb bx,dx 30 0013 72 12 jb l2 31 0015 8B 5E 10 mov bx,r 32 0018 09 DB or bx,bx ; if (r) // остатъка невъзможна стойност 33 001A B8 FFFF mov ax,-1 ;ffffH 34 001D 74 05 jz l1 35 001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 36 0021 89 47 02 mov [bx+2],ax 37 0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 38 0025 EB 40 jmp short l8 ; } 39 0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод! 40 002A 31 C0 xor ax,ax 41 002C 31 D2 xor dx,dx 42 002E D1 66 08 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво 43 0031 D1 56 0A rcl word ptr u0+2,1 44 0034 D1 56 04 rcl word ptr u1,1 45 0037 D1 56 06 rcl word ptr u1+2,1 46 003A D1 D0 rcl ax,1 47 003C D1 D2 rcl dx,1 48 003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2) 49 0040 3B 46 0C cmp ax,word ptr v ; сравни DX:AX и делителя 50 0043 8B DA mov bx,dx 51 0045 1B 5E 0E sbb bx,word ptr v+2 ; DX:AX < делителя ? 52 0048 72 09 jb l5 ; да, прескочи 53 004A 2B 46 0C l4: sub ax,word ptr v ; не, извади делителя 54 004D 1B 56 0E sbb dx,word ptr v+2 55 0050 FF 46 08 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път 56 0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти 57 0055 8B 5E 10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го. 58 0058 09 DB or bx,bx 59 005A 74 05 jz l7 60 005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX 61 005E 89 57 02 mov [bx+2],dx 62 0061 8B 46 08 l7: mov ax,word ptr u0 63 0064 8B 56 0A mov dx,word ptr u0+2 64 0067 5E l8: pop si 65 0068 5F pop di 66 0069 5D pop bp 67 006A C3 ret ; } 68 006B _TEXT ends 69 end

Page 83: Сборник задачи по програмиране на Асемблер

83Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

TURBO ASSEMBLER July 27,2011 03:28:12 p.m. Page 1

1 name divlu22 0000 _TEXT segment public 'CODE'3 assume cs:_TEXT4 ; /* Дълго делене, беззнаково (64/32 ==> 32).5 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово6 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.7 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя8 ; връща остатък 0xFFFFFFFF (невъзможна стойност).9 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.

10 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */ 11 ; 12 ; unsigned long 13 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 14 ; { 15 public _divlu2 16 0000 _divlu2: 17 = u1 equ [bp+4] 18 = u0 equ [bp+8] 19 = v equ [bp+12] 20 = r equ [bp+16] 21 ; unsigned short un1, un0, // Нормализирани младши цифри на делимото. 22 ; vn1, vn0, // Нормализирани цифри на делителя. 23 ; q1, q0; // Цифри на частното. 24 ; unsigned long un32, un21, un10, // Двойки цифри на делимото. 25 ; rhat; // Остатък. 26 ; int s; // Колко бита изместваме за нормализация. 27 = un1 equ word ptr [bp-2] 28 = un0 equ word ptr [bp-4] 29 = vn1 equ word ptr [bp-6] 30 = vn0 equ word ptr [bp-8] 31 = q1 equ word ptr [bp-10] 32 = q0 equ word ptr [bp-12] 33 = un32 equ [bp-16] 34 = un21 equ [bp-20] 35 0000 55 push bp 36 0001 8B EC mov bp,sp 37 0003 83 C4 EC add sp,-20 38 0006 57 push di 39 0007 56 push si 40 0008 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в 41 000B 8B 56 0E mov dx,word ptr v+2 42 000E 39 46 04 cmp word ptr u1,ax 43 0011 8B 5E 06 mov bx,word ptr u1+2 44 0014 19 D3 sbb bx,dx 45 0016 72 13 jb l2 46 0018 8B 5E 10 mov bx,r 47 001B 09 DB or bx,bx ; if (r) // остатъка невъзможна стойност 48 001D B8 FFFF mov ax,-1 ;ffffH 49 0020 74 05 jz l1 50 0022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 51 0024 89 47 02 mov [bx+2],ax 52 0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 53 0028 E9 01A4 R jmp l24 ; } 54 002B 8B F2 l2: mov si,dx ; if (v == 0) 55 002D 09 C6 or si,ax 56 002F BE 0020 mov si,32 ;0020H ; s = 32; 57 0032 74 4C jz l7 ; else { // 0 <= s <= 31. 58 0034 31 F6 xor si,si ; s = 0; // Нормализирай делителя. 59 0036 39 F2 cmp dx,si ; if (v <= 0x0000FFFFL) { 60 0038 75 06 jne l3 61 003A BE 0010 mov si,16 ;0010H ; s += 16; 62 003D 92 xchg ax,dx ; v <<= 16; 63 003E 31 C0 xor ax,ax 64 0040 08 F6 l3: or dh,dh ; } 65 0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) { 66 0044 83 C6 08 add si,8 ; s += 8; 67 0047 8A F2 mov dh,dl ; v <<= 8; 68 0049 8A D4 mov dl,ah 69 004B 8A E0 mov ah,al 70 004D 28 C0 sub al,al 71 004F 80 FE 0F l4: cmp dh,0Fh ; } 72 0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) { 73 0054 83 C6 04 add si,4 ; s += 4;

Page 84: Сборник задачи по програмиране на Асемблер

84Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

74 0057 D1 E0 shl ax,1 ; v <<= 4; 75 0059 D1 D2 rcl dx,1 76 005B D1 E0 shl ax,1 77 005D D1 D2 rcl dx,1 78 005F D1 E0 shl ax,1 79 0061 D1 D2 rcl dx,1 80 0063 D1 E0 shl ax,1 81 0065 D1 D2 rcl dx,1 82 0067 80 FE 3F l5: cmp dh,3Fh ; } 83 006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) { 84 006C 83 C6 02 add si,2 ; s += 2; 85 006F D1 E0 shl ax,1 ; v <<= 2; 86 0071 D1 D2 rcl dx,1 87 0073 D1 E0 shl ax,1 88 0075 D1 D2 rcl dx,1 89 0077 09 D2 l6: or dx,dx ; } 90 0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) { 91 007B 46 inc si ; s++; 92 007C D1 E0 shl ax,1 ; v <<= 1; 93 007E D1 D2 rcl dx,1 94 0080 89 46 0C l7: mov word ptr v,ax ; } 95 0083 89 56 0E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри. 96 0086 89 56 FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16); 97 0089 89 46 F8 mov vn0,ax ; vn0 = (unsigned short)v; 98 008C 8B CE mov cx,si ; un32 = (u1 << s) | 99 008E 8B 5E 04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31)); 100 0091 8B 7E 06 mov di,word ptr u1+2 101 0094 E3 06 jcxz l9 102 0096 D1 E3 l8: shl bx,1 103 0098 D1 D7 rcl di,1 104 009A E2 FA loop l8 105 009C B1 20 l9: mov cl,32 ;0020H 106 009E 29 F1 sub cx,si 107 00A0 8B 46 08 mov ax,word ptr u0 108 00A3 8B 56 0A mov dx,word ptr u0+2 109 00A6 74 06 jz l11 110 00A8 D1 EA l10: shr dx,1 111 00AA D1 D8 rcr ax,1 112 00AC E2 FA loop l10 113 00AE 09 F6 l11: or si,si 114 00B0 74 04 jz l12 115 00B2 09 C3 or bx,ax 116 00B4 09 D7 or di,dx 117 00B6 89 5E F0 l12: mov word ptr un32,bx 118 00B9 89 7E F2 mov word ptr un32+2,di 119 00BC 8B 46 08 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво. 120 00BF 8B 56 0A mov dx,word ptr u0+2 121 00C2 8B CE mov cx,si 122 00C4 E3 06 jcxz l14 123 00C6 D1 E0 l13: shl ax,1 124 00C8 D1 D2 rcl dx,1 125 00CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри. 126 00CC 89 56 FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16); 127 00CF 89 46 FC mov un0,ax ; un0 = (unsigned short)un10; 128 00D2 3B 7E FA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване129 00D5 C7 46 F6 FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.

130 00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1. 131 00DC 8B 46 F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1); 132 00DF 8B 56 F2 mov dx,word ptr un32+2 133 00E2 F7 76 FA div vn1 134 00E5 89 46 F6 mov q1,ax 135 00E8 F7 66 FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1; 136 00EB 8B 4E F0 mov cx,word ptr un32 137 00EE 8B 5E F2 mov bx,word ptr un32+2 138 00F1 29 C1 sub cx,ax 139 00F3 19 D3 sbb bx,dx 140 00F5 8B 46 F6 again1: mov ax,q1 ; again1: 141 00F8 F7 66 F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16) 142 00FB 39 CA cmp dx,cx ; + un1) { 143 00FD 72 12 jb l17 144 00FF 77 05 ja l16 145 0101 3B 46 FE cmp ax,un1 146 0104 76 0B jbe l17 147 0106 FF 4E F6 l16: dec q1 ; q1--; 148 0109 03 4E FA add cx,vn1 ; rhat += vn1; 149 010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 150 010F 74 E4 jz again1 ; goto again1;

Page 85: Сборник задачи по програмиране на Асемблер

85Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

151 0111 8B 46 F6 l17: mov ax,q1 ; } 152 0114 F7 66 0E mul word ptr v+2 ; } // Умножи и извади. 153 0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v; 154 0118 8B 46 F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване 155 011B F7 66 0C mul word ptr v; CX = мл.д.(ст.д.(v)*q1) 156 011E 01 CA add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1) 157 0120 8B 7E F0 mov di,word ptr un32 158 0123 8B 5E FE mov bx,un1 159 0126 29 C3 sub bx,ax 160 0128 19 D7 sbb di,dx 161 012A 89 5E EC mov word ptr un21,bx 162 012D 89 7E EE mov word ptr un21+2,di 163 0130 3B 7E FA cmp di,vn1 164 0133 C7 46 F4 FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене. 165 0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0. 166 013A 8B 46 EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1); 167 013D 8B 56 EE mov dx,word ptr un21+2 168 0140 F7 76 FA div vn1 169 0143 89 46 F4 mov q0,ax 170 0146 F7 66 FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1; 171 0149 8B 4E EC mov cx,word ptr un21 172 014C 8B 5E EE mov bx,word ptr un21+2 173 014F 29 C1 sub cx,ax 174 0151 19 D3 sbb bx,dx 175 0153 8B 46 F4 again0: mov ax,q0 ; again0: 176 0156 F7 66 F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16) 177 0159 39 CA cmp dx,cx ; + un0) { 178 015B 72 12 jb l20 179 015D 77 05 ja l19 180 015F 3B 46 FC cmp ax,un0 181 0162 76 0B jbe l20 182 0164 FF 4E F4 l19: dec q0 ; q0--; 183 0167 03 4E FA add cx,vn1 ; rhat += vn1; 184 016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 185 016D 74 E4 jz again0 ; goto again0; 186 016F 8B 5E 10 l20: mov bx,r ; } 187 0172 09 DB or bx,bx ; } 188 0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го. 189 0176 8B 46 F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s; 190 0179 F7 66 0E mul word ptr v+2 191 017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0) 192 017D 8B 46 F4 mov ax,q0 193 0180 F7 66 0C mul word ptr v 194 0183 01 CA add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0) 195 0185 8B 4E FC mov cx,un0 196 0188 8B 7E EC mov di,word ptr un21 197 018B 29 C1 sub cx,ax 198 018D 19 D7 sbb di,dx 199 018F 87 F1 xchg cx,si 200 0191 E3 06 jcxz l22 201 0193 D1 EF l21: shr di,1 202 0195 D1 DE rcr si,1 203 0197 E2 FA loop l21 204 0199 89 37 l22: mov [bx],si 205 019B 89 7F 02 mov [bx+2],di 206 019E 8B 46 F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0; 207 01A1 8B 56 F6 mov dx,q1 208 01A4 5E l24: pop si 209 01A5 5F pop di 210 01A6 8B E5 mov sp,bp 211 01A8 5D pop bp 212 01A9 C3 ret ; } 213 01AA _TEXT ends 214 end

14.2.4. OptimizingAssembler (OPTASM)

Този асемблер е разработван от 1988 г. до 1990 г. от фирмата „SLR Systems“ (автор – Steve Russell). Тойподдържа командите на микропроцесорите до 286 включително и е съвместим с MASM версия 5 (но не 5.1).Фирмата „SLR Systems“ е купена през 1994 г. от „Symantec“.

Page 86: Сборник задачи по програмиране на Асемблер

86Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

SLR Systems SuperFast Optimizing Assembler Mon, 08 Aug 11 18:09:06 OPTASM 1.65 Page 1-1

0000 _TEXT segment public 'CODE' assume cs:_TEXT

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public _divlu2 0000 _divlu2: = [bp+4] u1 equ [bp+4] = [bp+8] u0 equ [bp+8] = [bp+12] v equ [bp+12] = [bp+16] r equ [bp+16] 0000 55 push bp 0001 8B EC mov bp,sp 0003 57 push di 0004 56 push si 0005 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в 0008 8B 56 0E mov dx,word ptr v+2 000B 39 46 04 cmp word ptr u1,ax 000E 8B 5E 06 mov bx,word ptr u1+2 0011 1B DA sbb bx,dx 0013 72 12 (0027) jb l2 0015 8B 5E 10 mov bx,r 0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност 001A B8 FFFF mov ax,-1 ;ffffH 001D 74 05 (0024) jz l1 001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 0021 89 47 02 mov [bx+2],ax 0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 0025 EB 40 (0067) jmp short l8 ; } 0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод! 002A 33 C0 xor ax,ax 002C 33 D2 xor dx,dx 002E D1 66 08 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво 0031 D1 56 0A rcl word ptr u0+2,1 0034 D1 56 04 rcl word ptr u1,1 0037 D1 56 06 rcl word ptr u1+2,1 003A D1 D0 rcl ax,1 003C D1 D2 rcl dx,1 003E 72 0A (004A) jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2) 0040 3B 46 0C cmp ax,word ptr v ; сравни DX:AX и делителя 0043 8B DA mov bx,dx 0045 1B 5E 0E sbb bx,word ptr v+2 ; DX:AX < делителя ? 0048 72 09 (0053) jb l5 ; да, прескочи 004A 2B 46 0C l4: sub ax,word ptr v ; не, извади делителя 004D 1B 56 0E sbb dx,word ptr v+2 0050 FF 46 08 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път 0053 E2 D9 (002E) l5: loop l3 ; продължи да преминаваш всичките 64 пъти 0055 8B 5E 10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го. 0058 0B DB or bx,bx 005A 74 05 (0061) jz l7 005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX 005E 89 57 02 mov [bx+2],dx 0061 8B 46 08 l7: mov ax,word ptr u0 0064 8B 56 0A mov dx,word ptr u0+2 0067 5E l8: pop si 0068 5F pop di 0069 5D pop bp 006A C3 ret ; } 006B _TEXT ends end

Page 87: Сборник задачи по програмиране на Асемблер

87Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

SLR Systems SuperFast Optimizing Assembler Mon, 08 Aug 11 18:09:09 OPTASM 1.65 Page 1-1

0000 _TEXT segment public 'CODE' assume cs:_TEXT

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public _divlu2 0000 _divlu2: = [bp+4] u1 equ [bp+4] = [bp+8] u0 equ [bp+8] = [bp+12] v equ [bp+12] = [bp+16] r equ [bp+16]

; unsigned short un1, un0, // Нормализирани младши цифри на делимото.; vn1, vn0, // Нормализирани цифри на делителя.; q1, q0; // Цифри на частното.; unsigned long un32, un21, un10, // Двойки цифри на делимото.; rhat; // Остатък.; int s; // Колко бита изместваме за нормализация.

= word ptr [bp-2] un1 equ word ptr [bp-2]= word ptr [bp-4] un0 equ word ptr [bp-4]= word ptr [bp-6] vn1 equ word ptr [bp-6]= word ptr [bp-8] vn0 equ word ptr [bp-8]= word ptr [bp-10] q1 equ word ptr [bp-10]= word ptr [bp-12] q0 equ word ptr [bp-12]

= [bp-16] un32 equ [bp-16] = [bp-20] un21 equ [bp-20] 0000 55 push bp 0001 8B EC mov bp,sp 0003 83 C4 EC add sp,-20 0006 57 push di 0007 56 push si 0008 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в 000B 8B 56 0E mov dx,word ptr v+2 000E 39 46 04 cmp word ptr u1,ax 0011 8B 5E 06 mov bx,word ptr u1+2 0014 1B DA sbb bx,dx 0016 72 13 (002B) jb l2 0018 8B 5E 10 mov bx,r 001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност 001D B8 FFFF mov ax,-1 ;ffffH 0020 74 05 (0027) jz l1 0022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 0024 89 47 02 mov [bx+2],ax 0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 0028 E9 01A4 R jmp l24 ; } 002B 8B F2 l2: mov si,dx ; if (v == 0) 002D 0B F0 or si,ax 002F BE 0020 mov si,32 ;0020H ; s = 32; 0032 74 4C (0080) jz l7 ; else { // 0 <= s <= 31. 0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя. 0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) { 0038 75 06 (0040) jne l3 003A BE 0010 mov si,16 ;0010H ; s += 16; 003D 92 xchg ax,dx ; v <<= 16; 003E 33 C0 xor ax,ax 0040 0A F6 l3: or dh,dh ; } 0042 75 0B (004F) jnz l4 ; if (v <= 0x00FFFFFFL) { 0044 83 C6 08 add si,8 ; s += 8; 0047 8A F2 mov dh,dl ; v <<= 8; 0049 8A D4 mov dl,ah 004B 8A E0 mov ah,al 004D 2A C0 sub al,al 004F 80 FE 0F l4: cmp dh,0Fh ; } 0052 77 13 (0067) ja l5 ; if (v <= 0x0FFFFFFFL) { 0054 83 C6 04 add si,4 ; s += 4; 0057 D1 E0 shl ax,1 ; v <<= 4; 0059 D1 D2 rcl dx,1

Page 88: Сборник задачи по програмиране на Асемблер

88Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

005B D1 E0 shl ax,1 005D D1 D2 rcl dx,1 005F D1 E0 shl ax,1 0061 D1 D2 rcl dx,1 0063 D1 E0 shl ax,1 0065 D1 D2 rcl dx,1 0067 80 FE 3F l5: cmp dh,3Fh ; } 006A 77 0B (0077) ja l6 ; if (v <= 0x3FFFFFFFL) { 006C 83 C6 02 add si,2 ; s += 2; 006F D1 E0 shl ax,1 ; v <<= 2; 0071 D1 D2 rcl dx,1 0073 D1 E0 shl ax,1 0075 D1 D2 rcl dx,1 0077 0B D2 l6: or dx,dx ; } 0079 78 05 (0080) js l7 ; if (v <= 0x7FFFFFFFL) { 007B 46 inc si ; s++; 007C D1 E0 shl ax,1 ; v <<= 1; 007E D1 D2 rcl dx,1 0080 89 46 0C l7: mov word ptr v,ax ; } 0083 89 56 0E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри. 0086 89 56 FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16); 0089 89 46 F8 mov vn0,ax ; vn0 = (unsigned short)v; 008C 8B CE mov cx,si ; un32 = (u1 << s) | 008E 8B 5E 04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31)); 0091 8B 7E 06 mov di,word ptr u1+2 0094 E3 06 (009C) jcxz l9 0096 D1 E3 l8: shl bx,1 0098 D1 D7 rcl di,1 009A E2 FA (0096) loop l8 009C B1 20 l9: mov cl,32 ;0020H 009E 2B CE sub cx,si 00A0 8B 46 08 mov ax,word ptr u0 00A3 8B 56 0A mov dx,word ptr u0+2 00A6 74 06 (00AE) jz l11 00A8 D1 EA l10: shr dx,1 00AA D1 D8 rcr ax,1 00AC E2 FA (00A8) loop l10 00AE 0B F6 l11: or si,si 00B0 74 04 (00B6) jz l12 00B2 0B D8 or bx,ax 00B4 0B FA or di,dx 00B6 89 5E F0 l12: mov word ptr un32,bx 00B9 89 7E F2 mov word ptr un32+2,di 00BC 8B 46 08 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво. 00BF 8B 56 0A mov dx,word ptr u0+2 00C2 8B CE mov cx,si 00C4 E3 06 (00CC) jcxz l14 00C6 D1 E0 l13: shl ax,1 00C8 D1 D2 rcl dx,1 00CA E2 FA (00C6) loop l13 ; // Раздели мл. половина на делимото на 2 цифри. 00CC 89 56 FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16); 00CF 89 46 FC mov un0,ax ; un0 = (unsigned short)un10; 00D2 3B 7E FA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване 00D5 C7 46 F6 FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене. 00DA 73 35 (0111) jae l17 ; else {// Изчисли първата цифра на частното, q1. 00DC 8B 46 F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1); 00DF 8B 56 F2 mov dx,word ptr un32+2 00E2 F7 76 FA div vn1 00E5 89 46 F6 mov q1,ax 00E8 F7 66 FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1; 00EB 8B 4E F0 mov cx,word ptr un32 00EE 8B 5E F2 mov bx,word ptr un32+2 00F1 2B C8 sub cx,ax 00F3 1B DA sbb bx,dx 00F5 8B 46 F6 again1: mov ax,q1 ; again1: 00F8 F7 66 F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16) 00FB 3B D1 cmp dx,cx ; + un1) { 00FD 72 12 (0111) jb l17 00FF 77 05 (0106) ja l16 0101 3B 46 FE cmp ax,un1 0104 76 0B (0111) jbe l17 0106 FF 4E F6 l16: dec q1 ; q1--; 0109 03 4E FA add cx,vn1 ; rhat += vn1; 010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 010F 74 E4 (00F5) jz again1 ; goto again1; 0111 8B 46 F6 l17: mov ax,q1 ; } 0114 F7 66 0E mul word ptr v+2 ; } // Умножи и извади.

Page 89: Сборник задачи по програмиране на Асемблер

89Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v; 0118 8B 46 F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване 011B F7 66 0C mul word ptr v; CX = мл.д.(ст.д.(v)*q1) 011E 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1) 0120 8B 7E F0 mov di,word ptr un32 0123 8B 5E FE mov bx,un1 0126 2B D8 sub bx,ax 0128 1B FA sbb di,dx 012A 89 5E EC mov word ptr un21,bx 012D 89 7E EE mov word ptr un21+2,di 0130 3B 7E FA cmp di,vn1 0133 C7 46 F4 FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене. 0138 73 35 (016F) jae l20 ; else {// Изчисли втората цифра на частното, q0. 013A 8B 46 EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1); 013D 8B 56 EE mov dx,word ptr un21+2 0140 F7 76 FA div vn1 0143 89 46 F4 mov q0,ax 0146 F7 66 FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1; 0149 8B 4E EC mov cx,word ptr un21 014C 8B 5E EE mov bx,word ptr un21+2 014F 2B C8 sub cx,ax 0151 1B DA sbb bx,dx 0153 8B 46 F4 again0: mov ax,q0 ; again0: 0156 F7 66 F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16) 0159 3B D1 cmp dx,cx ; + un0) { 015B 72 12 (016F) jb l20 015D 77 05 (0164) ja l19 015F 3B 46 FC cmp ax,un0 0162 76 0B (016F) jbe l20 0164 FF 4E F4 l19: dec q0 ; q0--; 0167 03 4E FA add cx,vn1 ; rhat += vn1; 016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 016D 74 E4 (0153) jz again0 ; goto again0; 016F 8B 5E 10 l20: mov bx,r ; } 0172 0B DB or bx,bx ; } 0174 74 28 (019E) jz l23 ; if (r) // Ако се иска остатъкът, върни го. 0176 8B 46 F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s; 0179 F7 66 0E mul word ptr v+2 017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0) 017D 8B 46 F4 mov ax,q0 0180 F7 66 0C mul word ptr v 0183 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0) 0185 8B 4E FC mov cx,un0 0188 8B 7E EC mov di,word ptr un21 018B 2B C8 sub cx,ax 018D 1B FA sbb di,dx 018F 87 CE xchg cx,si 0191 E3 06 (0199) jcxz l22 0193 D1 EF l21: shr di,1 0195 D1 DE rcr si,1 0197 E2 FA (0193) loop l21 0199 89 37 l22: mov [bx],si 019B 89 7F 02 mov [bx+2],di 019E 8B 46 F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0; 01A1 8B 56 F6 mov dx,q1 01A4 5E l24: pop si 01A5 5F pop di 01A6 8B E5 mov sp,bp 01A8 5D pop bp 01A9 C3 ret ; } 01AA _TEXT ends end

14.2.5. ASM86 Macro Assembler

Това е най-старият асемблер за x86. Създава го фирмата „Intel“ за операционната си система „iRMX 86“ още през1980 г., когато още няма нито IBM PC, нито ДОС. Именно синтаксисът на ASM86 е взаимстван от MASM (т.14.1.2), чиято версия 1 излиза през 1981 г. По-късно ASM86 е пренесен на ДОС и е добавена поддръжка накомандите на микропроцесорите до 186 включително . Част е от компилатора на език C „C-86“ на Intel.

Page 90: Сборник задачи по програмиране на Асемблер

90Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Заб.: Има и асемблери, наследници на ASM86 – ASM286, ASM386 и ASM486. Асемберът ASM286 създаваобектни файлове във формат „OMF286“, a ASM386 и ASM486 – в „OMF386“. Тези формати са различни от„OMF“. При ASM386 и ASM486 директивата „_TEXT segment public 'CODE'“ трябва да се промени на „_TEXTsegment ER use16 public“. Затова тези асемблери не се разглеждат тук.

Page 91: Сборник задачи по програмиране на Асемблер

91Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

DOS 7.10 (038-N) 8086/87/88/186 MACRO ASSEMBLER V3.1 ASSEMBLY OF MODULE DIVLU2OBJECT MODULE PLACED IN DIVLUSS.OBJASSEMBLER INVOKED BY: C:\AS\BIN\ASM86.EXE DIVLUSS.AS8 PRINT NOPAGING PAGEWIDTH(125)

LOC OBJ LINE SOURCE

1 name divlu2---- 2 _TEXT segment public 'CODE'

3 assume cs:_TEXT4 ; /* Дълго делене, беззнаково (64/32 ==> 32).5 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово6 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.7 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя8 ; връща остатък 0xFFFFFFFF (невъзможна стойност).9 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.

10 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */ 11 ; 12 ; unsigned long 13 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 14 ; { 15 public _divlu20000 16 _divlu2: 0004[] 17 u1 equ [bp+4] 0008[] 18 u0 equ [bp+8] 000C[] 19 v equ [bp+12] 0010[] 20 r equ [bp+16]0000 55 21 push bp0001 8BEC 22 mov bp,sp0003 57 23 push di0004 56 24 push si0005 8B460C 25 mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в0008 8B560E 26 mov dx,word ptr v+2000B 394604 27 cmp word ptr u1,ax000E 8B5E06 28 mov bx,word ptr u1+20011 1BDA 29 sbb bx,dx0013 7212 30 jb l20015 8B5E10 31 mov bx,r0018 0BDB 32 or bx,bx ; if (r) // остатъка невъзможна стойност001A B8FFFF 33 mov ax,-1 ;ffffH001D 7405 34 jz l1001F 8907 35 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0021 894702 36 mov [bx+2],ax0024 99 37 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0025 EB40 38 jmp short l8 ; }0027 B94000 39 l2: mov cx,64 ; благодарности на Peter Norton за този метод!002A 33C0 40 xor ax,ax002C 33D2 41 xor dx,dx002E D16608 42 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво0031 D1560A 43 rcl word ptr u0+2,10034 D15604 44 rcl word ptr u1,10037 D15606 45 rcl word ptr u1+2,1003A D1D0 46 rcl ax,1003C D1D2 47 rcl dx,1003E 720A 48 jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)0040 3B460C 49 cmp ax,word ptr v ; сравни DX:AX и делителя0043 8BDA 50 mov bx,dx0045 1B5E0E 51 sbb bx,word ptr v+2 ; DX:AX < делителя ?0048 7209 52 jb l5 ; да, прескочи004A 2B460C 53 l4: sub ax,word ptr v ; не, извади делителя004D 1B560E 54 sbb dx,word ptr v+20050 FF4608 55 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път0053 E2D9 56 l5: loop l3 ; продължи да преминаваш всичките 64 пъти0055 8B5E10 57 mov bx,r ; if (r) // Ако се иска остатъкът, върни го.0058 0BDB 58 or bx,bx005A 7405 59 jz l7005C 8907 60 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX005E 895702 61 mov [bx+2],dx0061 8B4608 62 l7: mov ax,word ptr u00064 8B560A 63 mov dx,word ptr u0+20067 5E 64 l8: pop si0068 5F 65 pop di0069 5D 66 pop bp006A C3 67 ret ; }---- 68 _TEXT ends 69 end

Page 92: Сборник задачи по програмиране на Асемблер

92Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Page 93: Сборник задачи по програмиране на Асемблер

93Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

DOS 7.10 (038-N) 8086/87/88/186 MACRO ASSEMBLER V3.1 ASSEMBLY OF MODULE DIVLU2OBJECT MODULE PLACED IN DIVLU2.OBJASSEMBLER INVOKED BY: C:\AS\BIN\ASM86.EXE DIVLU2.AS8 PRINT NOPAGING PAGEWIDTH(125)

LOC OBJ LINE SOURCE

1 name divlu2---- 2 _TEXT segment public 'CODE'

3 assume cs:_TEXT4 ; /* Дълго делене, беззнаково (64/32 ==> 32).5 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово6 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.7 ; В слуачите на препълване (делене на 0 или частното надхвърля 32 бита), тя8 ; връща остатък 0xFFFFFFFF (невъзможна стойност).9 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.

10 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */ 11 ; 12 ; unsigned long 13 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 14 ; { 15 public _divlu20000 16 _divlu2: 0004[] 17 u1 equ [bp+4] 0008[] 18 u0 equ [bp+8] 000C[] 19 v equ [bp+12] 0010[] 20 r equ [bp+16] 21 ; unsigned short un1, un0, // Нормализирани младши цифри на делимото. 22 ; vn1, vn0, // Нормализирани цифри на делителя. 23 ; q1, q0; // Цифри на частното. 24 ; unsigned long un32, un21, un10, // Двойки цифри на делимото. 25 ; rhat; // Остатък. 26 ; int s; // Колко бита изместваме за нормализация. -0002[] 27 un1 equ word ptr [bp-2] -0004[] 28 un0 equ word ptr [bp-4] -0006[] 29 vn1 equ word ptr [bp-6] -0008[] 30 vn0 equ word ptr [bp-8] -000A[] 31 q1 equ word ptr [bp-10] -000C[] 32 q0 equ word ptr [bp-12] -0010[] 33 un32 equ [bp-16] -0014[] 34 un21 equ [bp-20]0000 55 35 push bp0001 8BEC 36 mov bp,sp0003 83C4EC 37 add sp,-200006 57 38 push di0007 56 39 push si0008 8B460C 40 mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в000B 8B560E 41 mov dx,word ptr v+2000E 394604 42 cmp word ptr u1,ax0011 8B5E06 43 mov bx,word ptr u1+20014 1BDA 44 sbb bx,dx0016 7213 45 jb l20018 8B5E10 46 mov bx,r001B 0BDB 47 or bx,bx ; if (r) // остатъка невъзможна стойност001D B8FFFF 48 mov ax,-1 ;ffffH0020 7405 49 jz l10022 8907 50 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0024 894702 51 mov [bx+2],ax0027 99 52 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0028 E97901 53 jmp l24 ; }002B 8BF2 54 l2: mov si,dx ; if (v == 0)002D 0BF0 55 or si,ax002F BE2000 56 mov si,32 ;0020H ; s = 32;0032 744C 57 jz l7 ; else { // 0 <= s <= 31.0034 33F6 58 xor si,si ; s = 0; // Нормализирай делителя.0036 3BD6 59 cmp dx,si ; if (v <= 0x0000FFFFL) {0038 7506 60 jne l3003A BE1000 61 mov si,16 ;0010H ; s += 16;003D 92 62 xchg ax,dx ; v <<= 16;003E 33C0 63 xor ax,ax0040 0AF6 64 l3: or dh,dh ; }0042 750B 65 jnz l4 ; if (v <= 0x00FFFFFFL) {0044 83C608 66 add si,8 ; s += 8;0047 8AF2 67 mov dh,dl ; v <<= 8;0049 8AD4 68 mov dl,ah004B 8AE0 69 mov ah,al004D 2AC0 70 sub al,al

Page 94: Сборник задачи по програмиране на Асемблер

94Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

004F 80FE0F 71 l4: cmp dh,0Fh ; }0052 7713 72 ja l5 ; if (v <= 0x0FFFFFFFL) {0054 83C604 73 add si,4 ; s += 4;0057 D1E0 74 shl ax,1 ; v <<= 4;0059 D1D2 75 rcl dx,1005B D1E0 76 shl ax,1005D D1D2 77 rcl dx,1005F D1E0 78 shl ax,10061 D1D2 79 rcl dx,10063 D1E0 80 shl ax,10065 D1D2 81 rcl dx,10067 80FE3F 82 l5: cmp dh,3Fh ; }006A 770B 83 ja l6 ; if (v <= 0x3FFFFFFFL) {006C 83C602 84 add si,2 ; s += 2;006F D1E0 85 shl ax,1 ; v <<= 2;0071 D1D2 86 rcl dx,10073 D1E0 87 shl ax,10075 D1D2 88 rcl dx,10077 0BD2 89 l6: or dx,dx ; }0079 7805 90 js l7 ; if (v <= 0x7FFFFFFFL) {007B 46 91 inc si ; s++;007C D1E0 92 shl ax,1 ; v <<= 1;007E D1D2 93 rcl dx,10080 89460C 94 l7: mov word ptr v,ax ; }0083 89560E 95 mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри.0086 8956FA 96 mov vn1,dx ; vn1 = (unsigned short)(v >> 16);0089 8946F8 97 mov vn0,ax ; vn0 = (unsigned short)v;008C 8BCE 98 mov cx,si ; un32 = (u1 << s) |008E 8B5E04 99 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31));0091 8B7E06 100 mov di,word ptr u1+20094 E306 101 jcxz l90096 D1E3 102 l8: shl bx,10098 D1D7 103 rcl di,1009A E2FA 104 loop l8009C B120 105 l9: mov cl,32 ;0020H009E 2BCE 106 sub cx,si00A0 8B4608 107 mov ax,word ptr u000A3 8B560A 108 mov dx,word ptr u0+200A6 7406 109 jz l1100A8 D1EA 110 l10: shr dx,100AA D1D8 111 rcr ax,100AC E2FA 112 loop l1000AE 0BF6 113 l11: or si,si00B0 7404 114 jz l1200B2 0BD8 115 or bx,ax00B4 0BFA 116 or di,dx00B6 895EF0 117 l12: mov word ptr un32,bx00B9 897EF2 118 mov word ptr un32+2,di00BC 8B4608 119 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво.00BF 8B560A 120 mov dx,word ptr u0+200C2 8BCE 121 mov cx,si00C4 E306 122 jcxz l1400C6 D1E0 123 l13: shl ax,100C8 D1D2 124 rcl dx,100CA E2FA 125 loop l13 ; // Раздели мл. половина на делимото на 2 цифри.00CC 8956FE 126 l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16);00CF 8946FC 127 mov un0,ax ; un0 = (unsigned short)un10;00D2 3B7EFA 128 cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване00D5 C746F6FFFF 129 mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.00DA 7335 130 jae l17 ; else {// Изчисли първата цифра на частното, q1.00DC 8B46F0 131 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1);00DF 8B56F2 132 mov dx,word ptr un32+200E2 F776FA 133 div vn100E5 8946F6 134 mov q1,ax00E8 F766FA 135 mul vn1 ; rhat = un32 - (unsigned long)q1*vn1;00EB 8B4EF0 136 mov cx,word ptr un3200EE 8B5EF2 137 mov bx,word ptr un32+200F1 2BC8 138 sub cx,ax00F3 1BDA 139 sbb bx,dx00F5 8B46F6 140 again1: mov ax,q1 ; again1:00F8 F766F8 141 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16)00FB 3BD1 142 cmp dx,cx ; + un1) {00FD 7212 143 jb l1700FF 7705 144 ja l160101 3B46FE 145 cmp ax,un10104 760B 146 jbe l170106 FF4EF6 147 l16: dec q1 ; q1--;

Page 95: Сборник задачи по програмиране на Асемблер

95Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0109 034EFA 148 add cx,vn1 ; rhat += vn1;010C 83D300 149 adc bx,0 ; if ((rhat >> 16) == 0)010F 74E4 150 jz again1 ; goto again1;0111 8B46F6 151 l17: mov ax,q1 ; }0114 F7660E 152 mul word ptr v+2 ; } // Умножи и извади.0117 91 153 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;0118 8B46F6 154 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване011B F7660C 155 mul word ptr v; CX = мл.д.(ст.д.(v)*q1)011E 03D1 156 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)0120 8B7EF0 157 mov di,word ptr un320123 8B5EFE 158 mov bx,un10126 2BD8 159 sub bx,ax0128 1BFA 160 sbb di,dx012A 895EEC 161 mov word ptr un21,bx012D 897EEE 162 mov word ptr un21+2,di0130 3B7EFA 163 cmp di,vn10133 C746F4FFFF 164 mov q0,-1 ; q0 = 0xFFFF; // при следващото делене.0138 7335 165 jae l20 ; else {// Изчисли втората цифра на частното, q0.013A 8B46EC 166 mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1);013D 8B56EE 167 mov dx,word ptr un21+20140 F776FA 168 div vn10143 8946F4 169 mov q0,ax0146 F766FA 170 mul vn1 ; rhat = un21 - (unsigned long)q0*vn1;0149 8B4EEC 171 mov cx,word ptr un21014C 8B5EEE 172 mov bx,word ptr un21+2014F 2BC8 173 sub cx,ax0151 1BDA 174 sbb bx,dx0153 8B46F4 175 again0: mov ax,q0 ; again0:0156 F766F8 176 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16)0159 3BD1 177 cmp dx,cx ; + un0) {015B 7212 178 jb l20015D 7705 179 ja l19015F 3B46FC 180 cmp ax,un00162 760B 181 jbe l200164 FF4EF4 182 l19: dec q0 ; q0--;0167 034EFA 183 add cx,vn1 ; rhat += vn1;016A 83D300 184 adc bx,0 ; if ((rhat >> 16) == 0)016D 74E4 185 jz again0 ; goto again0;016F 8B5E10 186 l20: mov bx,r ; }0172 0BDB 187 or bx,bx ; }0174 7428 188 jz l23 ; if (r) // Ако се иска остатъкът, върни го.0176 8B46F4 189 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s;0179 F7660E 190 mul word ptr v+2017C 91 191 xchg ax,cx ; мл.д.(ст.д.(v)*q0)017D 8B46F4 192 mov ax,q00180 F7660C 193 mul word ptr v0183 03D1 194 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)0185 8B4EFC 195 mov cx,un00188 8B7EEC 196 mov di,word ptr un21018B 2BC8 197 sub cx,ax018D 1BFA 198 sbb di,dx018F 87CE 199 xchg cx,si0191 E306 200 jcxz l220193 D1EF 201 l21: shr di,10195 D1DE 202 rcr si,10197 E2FA 203 loop l210199 8937 204 l22: mov [bx],si019B 897F02 205 mov [bx+2],di019E 8B46F4 206 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0;01A1 8B56F6 207 mov dx,q101A4 5E 208 l24: pop si01A5 5F 209 pop di01A6 8BE5 210 mov sp,bp01A8 5D 211 pop bp01A9 C3 212 ret ; }---- 213 _TEXT ends 214 end

14.2.6. A86 Assembler

Този асемблер е вариант на асемблера „A386“ (вж. по-долу). Той е свободно достъпен и поддържа командитесамо на микропроцесорите до 286 включително . Всичко останало, казано за „A386“, се отнася и за „A86“.Листингът, генериран от A86, е същият като на A386, затова не е даден тук отделно.

Page 96: Сборник задачи по програмиране на Асемблер

96Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Заб.: За разлика от A386, A86 не може да асемблира решенията на зад. №№31–32 (тт. 8.4–8.5) порадипрепълване в 32-битовите константи „tenyrs“ и „_120yrs“.

14.2.7. A386 Assembler

В отличие от A86, A386 поддържа наборите команди до „3DNow!“ и „SSE“ включително . А изпълнимият муфайл е само 37 KB (най-малък от всички разглеждани тук асемблери!) и е шифрован. Това е единствениятасемблер, който маркира генерирания машинен код с нарочен „воден знак“ – уникална комбинация от кодоветена командите , които имат 2 регистрови операнда и могат да се кодират по 2 начина. Тези разлики междуасемблерите са нормални и могат да се видят и в листингите , генерирани от разглежданите тук асемблери. Но вникой друг асемблер тези разлики не са нарочно използвани като „воден знак“, по който да може да се познае,че даден файл е бил генериран именно от дадения асемблер. Авторът предявява специални права над „водниязнак“ (или както той го нарича, „отпечатък“), твърдейки, че е твърде сложен, за да може да бъде случайновъзпроизведен от друг асемблер. Следователно ако отпечатъкът е открит в даден код, то го е генерирал неговиятасемблер. А ако някой друг асемблер дублира отпечатъка, той ще съди лицата, които го продават илиразпространяват! Авторът на A386 Eric Isaacson е съавтор на асемблера „ASM86“ (т. 14.2.5) на фирмата Intel. Заразлика от ASM86, A386 не изисква декларация на сегмента, директива „end“ в края на файла и др.„канцеларщина“, както я нарича авторът му. При генериране на изпълним файл тип „.COM“ например не сеизисква нито една директива.

Заб. 1: Че фалшификацията на асемблер е възможна, показва примерът с т.н. „Arrowsoft Assembler“, продаванпрез 80-те години на XX век от американската фирма „Arrowsoft Systems“ (Kaplan Morovitz & Stan Goldwyn-Benton), чиито 2 версии след четвърт век са разобличени от автора на настоящия сборник като замаскираниверсии 3 и 4 на MASM! Втората версия даже е компресирана, а идентификаторът на компресиращата програма епремахнат, за да не може след декомпресия да се разкрие идентичността с MASM!

Заб. 2: Листингите на всички други задачи в настоящия сборник освен зад. №№49–50 (тт. 13.4–13.5) сагенерирани именно от A386.

Page 97: Сборник задачи по програмиране на Асемблер

97Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of DIVLUSS.OBJ 2011-08-28 16:27Source: DIVLUSS.AS8 Page 1

1 name divlu22 _TEXT segment public 'CODE'3 assume cs:_TEXT4 ; /* Дълго делене, беззнаково (64/32 ==> 32).5 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово6 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.7 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя8 ; връща остатък 0xFFFFFFFF (невъзможна стойност).9 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.10 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */11 ;12 ; unsigned long13 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)14 ; {15 public _divlu216 _divlu2:17 = + 0004 u1 equ [bp+4]18 = + 0008 u0 equ [bp+8]19 = + 000C v equ [bp+12]20 = + 0010 r equ [bp+16]21 0000 55 push bp22 0001 8B EC mov bp,sp23 0003 57 push di24 0004 56 push si25 0005 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в26 0008 8B 56 0E mov dx,word ptr v+227 000B 39 46 04 cmp word ptr u1,ax28 000E 8B 5E 06 mov bx,word ptr u1+229 0011 19 D3 sbb bx,dx30 0013 72 12 jb l231 0015 8B 5E 10 mov bx,r32 0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност33 001A B8 FF FF mov ax,-1 ;ffffH34 001D 74 05 jz l135 001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото36 0021 89 47 02 mov [bx+2],ax37 0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.38 0025 EB 40 jmp short l8 ; }39 0027 B9 40 00 l2: mov cx,64 ; благодарности на Peter Norton за този метод!40 002A 33 C0 xor ax,ax41 002C 33 D2 xor dx,dx42 002E D1 66 08 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво43 0031 D1 56 0A rcl word ptr u0+2,144 0034 D1 56 04 rcl word ptr u1,145 0037 D1 56 06 rcl word ptr u1+2,146 003A D1 D0 rcl ax,147 003C D1 D2 rcl dx,148 003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)49 0040 3B 46 0C cmp ax,word ptr v ; сравни DX:AX и делителя50 0043 8B DA mov bx,dx51 0045 1B 5E 0E sbb bx,word ptr v+2 ; DX:AX < делителя ?52 0048 72 09 jb l5 ; да, прескочи53 004A 2B 46 0C l4: sub ax,word ptr v ; не, извади делителя54 004D 1B 56 0E sbb dx,word ptr v+255 0050 FF 46 08 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път56 0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти57 0055 8B 5E 10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го.58 0058 0B DB or bx,bx59 005A 74 05 jz l760 005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в u161 005E 89 57 02 mov [bx+2],dx62 0061 8B 46 08 l7: mov ax,word ptr u063 0064 8B 56 0A mov dx,word ptr u0+264 0067 5E l8: pop si65 0068 5F pop di66 0069 5D pop bp67 006A C3 ret ; }68 _TEXT ends69 end

Page 98: Сборник задачи по програмиране на Асемблер

98Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

A386 V4.05 assembly of DIVLU2.OBJ 2011-08-28 16:27Source: DIVLU2.AS8 Page 1

1 name divlu22 _TEXT segment public 'CODE'3 assume cs:_TEXT4 ; /* Дълго делене, беззнаково (64/32 ==> 32).5 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово6 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.7 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя8 ; връща остатък 0xFFFFFFFF (невъзможна стойност).9 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.10 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */11 ;12 ; unsigned long13 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)14 ; {15 public _divlu216 _divlu2:17 = + 0004 u1 equ [bp+4]18 = + 0008 u0 equ [bp+8]19 = + 000C v equ [bp+12]20 = + 0010 r equ [bp+16]21 ; unsigned short un1, un0, // Нормализирани младши цифри на делимото.22 ; vn1, vn0, // Нормализирани цифри на делителя.23 ; q1, q0; // Цифри на частното.24 ; unsigned long un32, un21, un10, // Двойки цифри на делимото.25 ; rhat; // Остатък.26 ; int s; // Колко бита изместваме за нормализация.27 =+w FFFF_FFFE un1 equ word ptr [bp-2]28 =+w FFFF_FFFC un0 equ word ptr [bp-4]29 =+w FFFF_FFFA vn1 equ word ptr [bp-6]30 =+w FFFF_FFF8 vn0 equ word ptr [bp-8]31 =+w FFFF_FFF6 q1 equ word ptr [bp-10]32 =+w FFFF_FFF4 q0 equ word ptr [bp-12]33 = + FFFF_FFF0 un32 equ [bp-16]34 = + FFFF_FFEC un21 equ [bp-20]35 0000 55 push bp36 0001 8B EC mov bp,sp37 0003 83 C4 EC add sp,-2038 0006 57 push di39 0007 56 push si40 0008 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в41 000B 8B 56 0E mov dx,word ptr v+242 000E 39 46 04 cmp word ptr u1,ax43 0011 8B 5E 06 mov bx,word ptr u1+244 0014 19 D3 sbb bx,dx45 0016 72 13 jb l246 0018 8B 5E 10 mov bx,r47 001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност48 001D B8 FF FF mov ax,-1 ;ffffH49 0020 74 05 jz l150 0022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото51 0024 89 47 02 mov [bx+2],ax52 0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.53 0028 E9 79 01 jmp l24 ; }54 002B 89 D6 l2: mov si,dx ; if (v == 0)55 002D 0B F0 or si,ax56 002F BE 20 00 mov si,32 ;0020H ; s = 32;57 0032 74 4C jz l7 ; else { // 0 <= s <= 31.58 0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя.59 0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) {60 0038 75 06 jne l361 003A BE 10 00 mov si,16 ;0010H ; s += 16;62 003D 92 xchg ax,dx ; v <<= 16;63 003E 33 C0 xor ax,ax64 0040 0A F6 l3: or dh,dh ; }65 0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) {66 0044 83 C6 08 add si,8 ; s += 8;67 0047 88 D6 mov dh,dl ; v <<= 8;68 0049 88 E2 mov dl,ah69 004B 88 C4 mov ah,al70 004D 2A C0 sub al,al71 004F 80 FE 0F l4: cmp dh,0Fh ; }72 0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) {73 0054 83 C6 04 add si,4 ; s += 4;74 0057 D1 E0 shl ax,1 ; v <<= 4;

Page 99: Сборник задачи по програмиране на Асемблер

99Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

75 0059 D1 D2 rcl dx,176 005B D1 E0 shl ax,177 005D D1 D2 rcl dx,178 005F D1 E0 shl ax,179 0061 D1 D2 rcl dx,180 0063 D1 E0 shl ax,181 0065 D1 D2 rcl dx,182 0067 80 FE 3F l5: cmp dh,3Fh ; }83 006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) {84 006C 83 C6 02 add si,2 ; s += 2;85 006F D1 E0 shl ax,1 ; v <<= 2;86 0071 D1 D2 rcl dx,187 0073 D1 E0 shl ax,188 0075 D1 D2 rcl dx,189 0077 0B D2 l6: or dx,dx ; }90 0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) {91 007B 46 inc si ; s++;92 007C D1 E0 shl ax,1 ; v <<= 1;93 007E D1 D2 rcl dx,194 0080 89 46 0C l7: mov word ptr v,ax ; }95 0083 89 56 0E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри.96 0086 89 56 FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16);97 0089 89 46 F8 mov vn0,ax ; vn0 = (unsigned short)v;98 008C 8B CE mov cx,si ; un32 = (u1 << s) |99 008E 8B 5E 04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31));100 0091 8B 7E 06 mov di,word ptr u1+2101 0094 E3 06 jcxz l9102 0096 D1 E3 l8: shl bx,1103 0098 D1 D7 rcl di,1104 009A E2 FA loop l8105 009C B1 20 l9: mov cl,32 ;0020H106 009E 29 F1 sub cx,si107 00A0 8B 46 08 mov ax,word ptr u0108 00A3 8B 56 0A mov dx,word ptr u0+2109 00A6 74 06 jz l11110 00A8 D1 EA l10: shr dx,1111 00AA D1 D8 rcr ax,1112 00AC E2 FA loop l10113 00AE 0B F6 l11: or si,si114 00B0 74 04 jz l12115 00B2 09 C3 or bx,ax116 00B4 09 D7 or di,dx117 00B6 89 5E F0 l12: mov word ptr un32,bx118 00B9 89 7E F2 mov word ptr un32+2,di119 00BC 8B 46 08 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво.120 00BF 8B 56 0A mov dx,word ptr u0+2121 00C2 8B CE mov cx,si122 00C4 E3 06 jcxz l14123 00C6 D1 E0 l13: shl ax,1124 00C8 D1 D2 rcl dx,1125 00CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри.126 00CC 89 56 FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16);127 00CF 89 46 FC mov un0,ax ; un0 = (unsigned short)un10;128 00D2 3B 7E FA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване129 00D5 C7 46 F6 FF FF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.130 00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1.131 00DC 8B 46 F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1);132 00DF 8B 56 F2 mov dx,word ptr un32+2133 00E2 F7 76 FA div vn1134 00E5 89 46 F6 mov q1,ax135 00E8 F7 66 FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1;136 00EB 8B 4E F0 mov cx,word ptr un32137 00EE 8B 5E F2 mov bx,word ptr un32+2138 00F1 29 C1 sub cx,ax139 00F3 19 D3 sbb bx,dx140 00F5 8B 46 F6 again1: mov ax,q1 ; again1:141 00F8 F7 66 F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16)142 00FB 39 CA cmp dx,cx ; + un1) {143 00FD 72 12 jb l17144 00FF 77 05 ja l16145 0101 3B 46 FE cmp ax,un1146 0104 76 0B jbe l17147 0106 FF 4E F6 l16: dec q1 ; q1--;148 0109 03 4E FA add cx,vn1 ; rhat += vn1;149 010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)150 010F 74 E4 jz again1 ; goto again1;151 0111 8B 46 F6 l17: mov ax,q1 ; }

Page 100: Сборник задачи по програмиране на Асемблер

100Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

152 0114 F7 66 0E mul word ptr v+2 ; } // Умножи и извади.153 0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;154 0118 8B 46 F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване155 011B F7 66 0C mul word ptr v; CX = мл.д.(ст.д.(v)*q1)156 011E 01 CA add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)157 0120 8B 7E F0 mov di,word ptr un32158 0123 8B 5E FE mov bx,un1159 0126 29 C3 sub bx,ax160 0128 19 D7 sbb di,dx161 012A 89 5E EC mov word ptr un21,bx162 012D 89 7E EE mov word ptr un21+2,di163 0130 3B 7E FA cmp di,vn1164 0133 C7 46 F4 FF FF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене.165 0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0.166 013A 8B 46 EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1);167 013D 8B 56 EE mov dx,word ptr un21+2168 0140 F7 76 FA div vn1169 0143 89 46 F4 mov q0,ax170 0146 F7 66 FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1;171 0149 8B 4E EC mov cx,word ptr un21172 014C 8B 5E EE mov bx,word ptr un21+2173 014F 29 C1 sub cx,ax174 0151 19 D3 sbb bx,dx175 0153 8B 46 F4 again0: mov ax,q0 ; again0:176 0156 F7 66 F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16)177 0159 39 CA cmp dx,cx ; + un0) {178 015B 72 12 jb l20179 015D 77 05 ja l19180 015F 3B 46 FC cmp ax,un0181 0162 76 0B jbe l20182 0164 FF 4E F4 l19: dec q0 ; q0--;183 0167 03 4E FA add cx,vn1 ; rhat += vn1;184 016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)185 016D 74 E4 jz again0 ; goto again0;186 016F 8B 5E 10 l20: mov bx,r ; }187 0172 0B DB or bx,bx ; }188 0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го.189 0176 8B 46 F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s;190 0179 F7 66 0E mul word ptr v+2191 017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)192 017D 8B 46 F4 mov ax,q0193 0180 F7 66 0C mul word ptr v194 0183 01 CA add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)195 0185 8B 4E FC mov cx,un0196 0188 8B 7E EC mov di,word ptr un21197 018B 29 C1 sub cx,ax198 018D 19 D7 sbb di,dx199 018F 87 F1 xchg cx,si200 0191 E3 06 jcxz l22201 0193 D1 EF l21: shr di,1202 0195 D1 DE rcr si,1203 0197 E2 FA loop l21204 0199 89 37 l22: mov [bx],si205 019B 89 7F 02 mov [bx+2],di206 019E 8B 46 F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0;207 01A1 8B 56 F6 mov dx,q1208 01A4 5E l24: pop si209 01A5 5F pop di210 01A6 8B E5 mov sp,bp211 01A8 5D pop bp212 01A9 C3 ret ; }213 _TEXT ends214 end

14.2.8. Phar Lap Macro Assembler (386|ASM)

Асемблерът „386|ASM“ заедно със свързващия редактор „386|LINK“ и библиотекаря „386|LIB“ е част отразширителя на ДОС „TNT“ на фирмата „Phar Lap Software“, разработван до 1996 г. включително . През 2000 г.фирмата „Phar Lap Software“ преминава към „Ardence“ (бивша „VentureCom“), през 2007 г. – към „Citrix“, а от2008 г. е към „Interval Zero“.

Page 101: Сборник задачи по програмиране на Асемблер

101Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Phar Lap Macro Assembler Version 8.0 Fri Sep 02 17:42:53 2011

Page 1-1

page 255,125 name divlu20000 _TEXT segment public 'CODE' assume cs:_TEXT

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public _divlu20000 _divlu2: = u1 equ [bp+4] = u0 equ [bp+8] = v equ [bp+12] = r equ [bp+16]0000 55 push bp0001 8B EC mov bp,sp0003 57 push di0004 56 push si0005 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в0008 8B 56 0E mov dx,word ptr v+2000B 39 46 04 cmp word ptr u1,ax000E 8B 5E 06 mov bx,word ptr u1+20011 1B DA sbb bx,dx0013 72 12 jb l20015 8B 5E 10 mov bx,r0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност001A B8 FFFF mov ax,-1 ;ffffH001D 74 05 jz l1001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0021 89 47 02 mov [bx+2],ax0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0025 EB 40 jmp short l8 ; }0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод!002A 33 C0 xor ax,ax002C 33 D2 xor dx,dx002E D1 66 08 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво0031 D1 56 0A rcl word ptr u0+2,10034 D1 56 04 rcl word ptr u1,10037 D1 56 06 rcl word ptr u1+2,1003A D1 D0 rcl ax,1003C D1 D2 rcl dx,1003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)0040 3B 46 0C cmp ax,word ptr v ; сравни DX:AX и делителя0043 8B DA mov bx,dx0045 1B 5E 0E sbb bx,word ptr v+2 ; DX:AX < делителя ?0048 72 09 jb l5 ; да, прескочи004A 2B 46 0C l4: sub ax,word ptr v ; не, извади делителя004D 1B 56 0E sbb dx,word ptr v+20050 FF 46 08 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти0055 8B 5E 10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го.0058 0B DB or bx,bx005A 74 05 jz l7005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в u1005E 89 57 02 mov [bx+2],dx0061 8B 46 08 l7: mov ax,word ptr u00064 8B 56 0A mov dx,word ptr u0+20067 5E l8: pop si0068 5F pop di0069 5D pop bp006A C3 ret ; }006B _TEXT ends end

Page 102: Сборник задачи по програмиране на Асемблер

102Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Phar Lap Macro Assembler Version 8.0 Fri Sep 02 17:42:56 2011

Page 1-1

page 255,125 name divlu20000 _TEXT segment public 'CODE' assume cs:_TEXT

; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public _divlu20000 _divlu2: = u1 equ [bp+4] = u0 equ [bp+8] = v equ [bp+12] = r equ [bp+16]

; unsigned short un1, un0, // Нормализирани младши цифри на делимото.; vn1, vn0, // Нормализирани цифри на делителя.; q1, q0; // Цифри на частното.; unsigned long un32, un21, un10, // Двойки цифри на делимото.; rhat; // Остатък.; int s; // Колко бита изместваме за нормализация.

= un1 equ word ptr [bp-2] = un0 equ word ptr [bp-4] = vn1 equ word ptr [bp-6] = vn0 equ word ptr [bp-8] = q1 equ word ptr [bp-10] = q0 equ word ptr [bp-12] = un32 equ [bp-16] = un21 equ [bp-20]0000 55 push bp0001 8B EC mov bp,sp0003 83 C4 EC add sp,-200006 57 push di0007 56 push si0008 8B 46 0C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в000B 8B 56 0E mov dx,word ptr v+2000E 39 46 04 cmp word ptr u1,ax0011 8B 5E 06 mov bx,word ptr u1+20014 1B DA sbb bx,dx0016 72 13 jb l20018 8B 5E 10 mov bx,r001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност001D B8 FFFF mov ax,-1 ;ffffH0020 74 05 jz l10022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0024 89 47 02 mov [bx+2],ax0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0028 E9 0179 jmp l24 ; }002B 8B F2 l2: mov si,dx ; if (v == 0)002D 0B F0 or si,ax002F BE 0020 mov si,32 ;0020H ; s = 32;0032 74 4C jz l7 ; else { // 0 <= s <= 31.0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя.0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) {0038 75 06 jne l3003A BE 0010 mov si,16 ;0010H ; s += 16;003D 92 xchg ax,dx ; v <<= 16;003E 33 C0 xor ax,ax0040 0A F6 l3: or dh,dh ; }0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) {0044 83 C6 08 add si,8 ; s += 8;0047 8A F2 mov dh,dl ; v <<= 8;0049 8A D4 mov dl,ah004B 8A E0 mov ah,al004D 2A C0 sub al,al004F 80 FE 0F l4: cmp dh,0Fh ; }

Page 103: Сборник задачи по програмиране на Асемблер

103Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) {0054 83 C6 04 add si,4 ; s += 4;0057 D1 E0 shl ax,1 ; v <<= 4;0059 D1 D2 rcl dx,1005B D1 E0 shl ax,1005D D1 D2 rcl dx,1005F D1 E0 shl ax,10061 D1 D2 rcl dx,10063 D1 E0 shl ax,10065 D1 D2 rcl dx,10067 80 FE 3F l5: cmp dh,3Fh ; }006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) {006C 83 C6 02 add si,2 ; s += 2;006F D1 E0 shl ax,1 ; v <<= 2;0071 D1 D2 rcl dx,10073 D1 E0 shl ax,10075 D1 D2 rcl dx,10077 0B D2 l6: or dx,dx ; }0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) {007B 46 inc si ; s++;007C D1 E0 shl ax,1 ; v <<= 1;007E D1 D2 rcl dx,10080 89 46 0C l7: mov word ptr v,ax ; }0083 89 56 0E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри.0086 89 56 FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16);0089 89 46 F8 mov vn0,ax ; vn0 = (unsigned short)v;008C 8B CE mov cx,si ; un32 = (u1 << s) |008E 8B 5E 04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31));0091 8B 7E 06 mov di,word ptr u1+20094 E3 06 jcxz l90096 D1 E3 l8: shl bx,10098 D1 D7 rcl di,1009A E2 FA loop l8009C B1 20 l9: mov cl,32 ;0020H009E 2B CE sub cx,si00A0 8B 46 08 mov ax,word ptr u000A3 8B 56 0A mov dx,word ptr u0+200A6 74 06 jz l1100A8 D1 EA l10: shr dx,100AA D1 D8 rcr ax,100AC E2 FA loop l1000AE 0B F6 l11: or si,si00B0 74 04 jz l1200B2 0B D8 or bx,ax00B4 0B FA or di,dx00B6 89 5E F0 l12: mov word ptr un32,bx00B9 89 7E F2 mov word ptr un32+2,di00BC 8B 46 08 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво.00BF 8B 56 0A mov dx,word ptr u0+200C2 8B CE mov cx,si00C4 E3 06 jcxz l1400C6 D1 E0 l13: shl ax,100C8 D1 D2 rcl dx,100CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри.00CC 89 56 FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16);00CF 89 46 FC mov un0,ax ; un0 = (unsigned short)un10;00D2 3B 7E FA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване00D5 C7 46 F6 FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1.00DC 8B 46 F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1);00DF 8B 56 F2 mov dx,word ptr un32+200E2 F7 76 FA div vn100E5 89 46 F6 mov q1,ax00E8 F7 66 FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1;00EB 8B 4E F0 mov cx,word ptr un3200EE 8B 5E F2 mov bx,word ptr un32+200F1 2B C8 sub cx,ax00F3 1B DA sbb bx,dx00F5 8B 46 F6 again1: mov ax,q1 ; again1:00F8 F7 66 F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16)00FB 3B D1 cmp dx,cx ; + un1) {00FD 72 12 jb l1700FF 77 05 ja l160101 3B 46 FE cmp ax,un10104 76 0B jbe l170106 FF 4E F6 l16: dec q1 ; q1--;0109 03 4E FA add cx,vn1 ; rhat += vn1;

Page 104: Сборник задачи по програмиране на Асемблер

104Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)010F 74 E4 jz again1 ; goto again1;0111 8B 46 F6 l17: mov ax,q1 ; }0114 F7 66 0E mul word ptr v+2 ; } // Умножи и извади.0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;0118 8B 46 F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване011B F7 66 0C mul word ptr v; CX = мл.д.(ст.д.(v)*q1)011E 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)0120 8B 7E F0 mov di,word ptr un320123 8B 5E FE mov bx,un10126 2B D8 sub bx,ax0128 1B FA sbb di,dx012A 89 5E EC mov word ptr un21,bx012D 89 7E EE mov word ptr un21+2,di0130 3B 7E FA cmp di,vn10133 C7 46 F4 FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене.0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0.013A 8B 46 EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1);013D 8B 56 EE mov dx,word ptr un21+20140 F7 76 FA div vn10143 89 46 F4 mov q0,ax0146 F7 66 FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1;0149 8B 4E EC mov cx,word ptr un21014C 8B 5E EE mov bx,word ptr un21+2014F 2B C8 sub cx,ax0151 1B DA sbb bx,dx0153 8B 46 F4 again0: mov ax,q0 ; again0:0156 F7 66 F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16)0159 3B D1 cmp dx,cx ; + un0) {015B 72 12 jb l20015D 77 05 ja l19015F 3B 46 FC cmp ax,un00162 76 0B jbe l200164 FF 4E F4 l19: dec q0 ; q0--;0167 03 4E FA add cx,vn1 ; rhat += vn1;016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0)016D 74 E4 jz again0 ; goto again0;016F 8B 5E 10 l20: mov bx,r ; }0172 0B DB or bx,bx ; }0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го.0176 8B 46 F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s;0179 F7 66 0E mul word ptr v+2017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)017D 8B 46 F4 mov ax,q00180 F7 66 0C mul word ptr v0183 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)0185 8B 4E FC mov cx,un00188 8B 7E EC mov di,word ptr un21018B 2B C8 sub cx,ax018D 1B FA sbb di,dx018F 87 CE xchg cx,si0191 E3 06 jcxz l220193 D1 EF l21: shr di,10195 D1 DE rcr si,10197 E2 FA loop l210199 89 37 l22: mov [bx],si019B 89 7F 02 mov [bx+2],di019E 8B 46 F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0;01A1 8B 56 F6 mov dx,q101A4 5E l24: pop si01A5 5F pop di01A6 8B E5 mov sp,bp01A8 5D pop bp01A9 C3 ret ; }01AA _TEXT ends end

14.3. Несъвместимис MASM

Тук се разглеждат асемблерите LZASM, NASM, NBASM и RASM-86, които не възприемат директивите и/илисинтаксиса на командите на „де факто“ стандарта MASM. Тази несъвместимост е или нарочна (както е приNASM), или просто не е цел или приоритет при проектирането на дадения асемблер. Така или иначе,несъвместими с MASM асемблери съществуват и значението на някои от тях е немалко.

Page 105: Сборник задачи по програмиране на Асемблер

105Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Заб.: За разлика от тт. 14.1 и 14.2, всеки даден в този раздел листинг е получен от отделен изходен текст, защотонесъвместимите с MASM асемблери са несъвместими и помежду си.

14.3.1. Lazy Assembler (LZASM)

Това е частично съвместим с TASM (т. 14.1.6) асемблер, разработван от Степан Половников от 2000 до 2007 г.Асемблерът използва ключовете на командния ред на TASM и поддържа неговия т.н. „идеален режим“, но не итози по подразбиране (режима „MASM“). Затова той не може да се счита за съвместим с MASM, въпреки чеподдържа формáта на директивите „proc“ и „local“ на MASM 5.1. Основен недостатък на LZASM е, че както иWASM (т. 14.1.3), той е лошо документиран.

Заб.: Както при TASM, единиците („1“) в листинга обозначават автоматично генерираните в пас №1 команди .За съжаление първата изрично зададена в изходния текст команда е неправилно отбелязана като автоматичногенерирана.

Page 106: Сборник задачи по програмиране на Асемблер

106Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Lazy Assembler Version 0.56 2011-07-31 21:41:52 Page 1DIVLUSS.LZA

1 0000 model small,c2 0000 codeseg3 ; /* Дълго делене, беззнаково (64/32 ==> 32).4 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово5 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.6 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя7 ; връща остатък 0xFFFFFFFF (невъзможна стойност).8 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.9 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */

10 ; 11 ; unsigned long 12 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 13 ; { 14 public divlu2 15 0000 proc divlu2 uses di si, u1:dword, u0:dword, v:dword, r:word1 16 0000 55 push bp1 17 0001 8B EC mov bp,sp1 18 0003 57 push di1 19 0004 56 push si1 20 0005 8B 46 0C mov ax,[word v] ; if (u1 >= v) {// При препълване, запиши в 21 0008 8B 56 0E mov dx,[word v+2] 22 000B 39 46 04 cmp [word u1],ax 23 000E 8B 5E 06 mov bx,[word u1+2] 24 0011 1B DA sbb bx,dx 25 0013 72 12 jb l2 26 0015 8B 5E 10 mov bx,[r] 27 0018 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност 28 001A B8 FFFF mov ax,-1 ;ffffH 29 001D 74 05 jz l1 30 001F 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 31 0021 89 47 02 mov [bx+2],ax 32 0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 33 0025 EB 40 jmp short l8 ; } 34 0027 B9 0040 l2: mov cx,64 ; благодарности на Peter Norton за този метод! 35 002A 33 C0 xor ax,ax 36 002C 33 D2 xor dx,dx 37 002E D1 66 08 l3: shl [word u0],1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво 38 0031 D1 56 0A rcl [word u0+2],1 39 0034 D1 56 04 rcl [word u1],1 40 0037 D1 56 06 rcl [word u1+2],1 41 003A D1 D0 rcl ax,1 42 003C D1 D2 rcl dx,1 43 003E 72 0A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2) 44 0040 3B 46 0C cmp ax,[word v] ; сравни DX:AX и делителя 45 0043 8B DA mov bx,dx 46 0045 1B 5E 0E sbb bx,[word v+2] ; DX:AX < делителя ? 47 0048 72 09 jb l5 ; да, прескочи 48 004A 2B 46 0C l4: sub ax,[word v] ; не, извади делителя 49 004D 1B 56 0E sbb dx,[word v+2] 50 0050 FF 46 08 inc [word u0] ; добави 1,защото делителят влезе в делимото 1 път 51 0053 E2 D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти 52 0055 8B 5E 10 mov bx,[r] ; if (r) // Ако се иска остатъкът, върни го. 53 0058 0B DB or bx,bx 54 005A 74 05 jz l7 55 005C 89 07 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX 56 005E 89 57 02 mov [bx+2],dx 57 0061 8B 46 08 l7: mov ax,[word u0] 58 0064 8B 56 0A mov dx,[word u0+2]1 59 0067 5E pop si1 60 0068 5F pop di1 61 0069 5D pop bp1 62 006A C3 ret 00000h 63 006B endp ; } 64 65 006B end

Page 107: Сборник задачи по програмиране на Асемблер

107Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Lazy Assembler Version 0.56 2011-07-31 21:41:55 Page 1DIVLU2.LZA

1 0000 model small,c2 0000 codeseg3 ; /* Дълго делене, беззнаково (64/32 ==> 32).4 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово5 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.6 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя7 ; връща остатък 0xFFFFFFFF (невъзможна стойност).8 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.9 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */

10 ; 11 ; unsigned long 12 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r) 13 ; { 14 public divlu2 15 0000 proc divlu2 uses di si, u1:dword, u0:dword, v:dword, r:word 16 ; unsigned short un1, un0, // Нормализирани младши цифри на делимото. 17 ; vn1, vn0, // Нормализирани цифри на делителя. 18 ; q1, q0; // Цифри на частното. 19 ; unsigned long un32, un21, un10, // Двойки цифри на делимото. 20 ; rhat; // Остатък. 21 ; int s; // Колко бита изместваме за нормализация. 22 local un1:word,un0:word,vn1:word,vn0:word,q1:word,q0:word,un32:dword,un21:dword1 23 0000 55 push bp1 24 0001 8B EC mov bp,sp1 25 0003 83 EC 14 sub sp,00014h1 26 0006 57 push di1 27 0007 56 push si1 28 0008 8B 46 0C mov ax,[word v] ; if (u1 >= v) {// При препълване, запиши в 29 000B 8B 56 0E mov dx,[word v+2] 30 000E 39 46 04 cmp [word u1],ax 31 0011 8B 5E 06 mov bx,[word u1+2] 32 0014 1B DA sbb bx,dx 33 0016 72 13 jb l2 34 0018 8B 5E 10 mov bx,[r] 35 001B 0B DB or bx,bx ; if (r) // остатъка невъзможна стойност 36 001D B8 FFFF mov ax,-1 ;ffffH 37 0020 74 05 jz l1 38 0022 89 07 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото 39 0024 89 47 02 mov [bx+2],ax 40 0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно. 41 0028 E9 0179 jmp l24 ; } 42 002B 8B F2 l2: mov si,dx ; if (v == 0) 43 002D 0B F0 or si,ax 44 002F BE 0020 mov si,32 ;0020H ; s = 32; 45 0032 74 4C jz l7 ; else { // 0 <= s <= 31. 46 0034 33 F6 xor si,si ; s = 0; // Нормализирай делителя. 47 0036 3B D6 cmp dx,si ; if (v <= 0x0000FFFFL) { 48 0038 75 06 jne l3 49 003A BE 0010 mov si,16 ;0010H ; s += 16; 50 003D 92 xchg ax,dx ; v <<= 16; 51 003E 33 C0 xor ax,ax 52 0040 0A F6 l3: or dh,dh ; } 53 0042 75 0B jnz l4 ; if (v <= 0x00FFFFFFL) { 54 0044 83 C6 08 add si,8 ; s += 8; 55 0047 8A F2 mov dh,dl ; v <<= 8; 56 0049 8A D4 mov dl,ah 57 004B 8A E0 mov ah,al 58 004D 2A C0 sub al,al 59 004F 80 FE 0F l4: cmp dh,0Fh ; } 60 0052 77 13 ja l5 ; if (v <= 0x0FFFFFFFL) { 61 0054 83 C6 04 add si,4 ; s += 4; 62 0057 D1 E0 shl ax,1 ; v <<= 4; 63 0059 D1 D2 rcl dx,1 64 005B D1 E0 shl ax,1 65 005D D1 D2 rcl dx,1 66 005F D1 E0 shl ax,1 67 0061 D1 D2 rcl dx,1 68 0063 D1 E0 shl ax,1 69 0065 D1 D2 rcl dx,1 70 0067 80 FE 3F l5: cmp dh,3Fh ; } 71 006A 77 0B ja l6 ; if (v <= 0x3FFFFFFFL) { 72 006C 83 C6 02 add si,2 ; s += 2;

Page 108: Сборник задачи по програмиране на Асемблер

108Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

73 006F D1 E0 shl ax,1 ; v <<= 2; 74 0071 D1 D2 rcl dx,1 75 0073 D1 E0 shl ax,1 76 0075 D1 D2 rcl dx,1 77 0077 0B D2 l6: or dx,dx ; } 78 0079 78 05 js l7 ; if (v <= 0x7FFFFFFFL) { 79 007B 46 inc si ; s++; 80 007C D1 E0 shl ax,1 ; v <<= 1; 81 007E D1 D2 rcl dx,1 82 0080 89 46 0C l7: mov [word v],ax ; } 83 0083 89 56 0E mov [word v+2],dx ; } // Раздели делителя на две 16-битови цифри. 84 0086 89 56 FA mov [vn1],dx ; vn1 = (unsigned short)(v >> 16); 85 0089 89 46 F8 mov [vn0],ax ; vn0 = (unsigned short)v; 86 008C 8B CE mov cx,si ; un32 = (u1 << s) | 87 008E 8B 5E 04 mov bx,[word u1] ; ((u0 >> (32 - s)) & ((long)-s >> 31)); 88 0091 8B 7E 06 mov di,[word u1+2] 89 0094 E3 06 jcxz l9 90 0096 D1 E3 l8: shl bx,1 91 0098 D1 D7 rcl di,1 92 009A E2 FA loop l8 93 009C B1 20 l9: mov cl,32 ;0020H 94 009E 2B CE sub cx,si 95 00A0 8B 46 08 mov ax,[word u0] 96 00A3 8B 56 0A mov dx,[word u0+2] 97 00A6 74 06 jz l11 98 00A8 D1 EA l10: shr dx,1 99 00AA D1 D8 rcr ax,1 100 00AC E2 FA loop l10 101 00AE 0B F6 l11: or si,si 102 00B0 74 04 jz l12 103 00B2 0B D8 or bx,ax 104 00B4 0B FA or di,dx 105 00B6 89 5E F0 l12: mov [word un32],bx 106 00B9 89 7E F2 mov [word un32+2],di 107 00BC 8B 46 08 mov ax,[word u0] ; un10 = u0 << s;// Измести делимото наляво. 108 00BF 8B 56 0A mov dx,[word u0+2] 109 00C2 8B CE mov cx,si 110 00C4 E3 06 jcxz l14 111 00C6 D1 E0 l13: shl ax,1 112 00C8 D1 D2 rcl dx,1 113 00CA E2 FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри. 114 00CC 89 56 FE l14: mov [un1],dx ; un1 = (unsigned short)(un10 >> 16); 115 00CF 89 46 FC mov [un0],ax ; un0 = (unsigned short)un10; 116 00D2 3B 7E FA cmp di,[vn1] ; if ((un32 >> 16) >= vn1)// Избегни препълване 117 00D5 C7 46 F6 FFFF mov [q1],-1 ; q1 = 0xFFFF; // при следващото делене. 118 00DA 73 35 jae l17 ; else {// Изчисли първата цифра на частното, q1. 119 00DC 8B 46 F0 mov ax,[word un32] ; q1 = (unsigned short)(un32/vn1); 120 00DF 8B 56 F2 mov dx,[word un32+2] 121 00E2 F7 76 FA div [vn1] 122 00E5 89 46 F6 mov [q1],ax 123 00E8 F7 66 FA mul [vn1] ; rhat = un32 - (unsigned long)q1*vn1; 124 00EB 8B 4E F0 mov cx,[word un32] 125 00EE 8B 5E F2 mov bx,[word un32+2] 126 00F1 2B C8 sub cx,ax 127 00F3 1B DA sbb bx,dx 128 00F5 8B 46 F6 again1: mov ax,[q1] ; again1: 129 00F8 F7 66 F8 mul [vn0] ; if ((unsigned long)q1*vn0 > (rhat << 16) 130 00FB 3B D1 cmp dx,cx ; + un1) { 131 00FD 72 12 jb l17 132 00FF 77 05 ja l16 133 0101 3B 46 FE cmp ax,[un1] 134 0104 76 0B jbe l17 135 0106 FF 4E F6 l16: dec [q1] ; q1--; 136 0109 03 4E FA add cx,[vn1] ; rhat += vn1; 137 010C 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 138 010F 74 E4 jz again1 ; goto again1; 139 0111 8B 46 F6 l17: mov ax,[q1] ; } 140 0114 F7 66 0E mul [word v+2] ; } // Умножи и извади. 141 0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v; 142 0118 8B 46 F6 mov ax,[q1] ; if ((un21 >> 16) >= vn1)// Избегни препълване 143 011B F7 66 0C mul [word v] ; CX = мл.д.(ст.д.(v)*q1) 144 011E 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1) 145 0120 8B 7E F0 mov di,[word un32] 146 0123 8B 5E FE mov bx,[un1] 147 0126 2B D8 sub bx,ax 148 0128 1B FA sbb di,dx 149 012A 89 5E EC mov [word un21],bx

Page 109: Сборник задачи по програмиране на Асемблер

109Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

150 012D 89 7E EE mov [word un21+2],di 151 0130 3B 7E FA cmp di,[vn1] 152 0133 C7 46 F4 FFFF mov [q0],-1 ; q0 = 0xFFFF; // при следващото делене. 153 0138 73 35 jae l20 ; else {// Изчисли втората цифра на частното, q0. 154 013A 8B 46 EC mov ax,[word un21] ; q0 = (unsigned short)(un21/vn1); 155 013D 8B 56 EE mov dx,[word un21+2] 156 0140 F7 76 FA div [vn1] 157 0143 89 46 F4 mov [q0],ax 158 0146 F7 66 FA mul [vn1] ; rhat = un21 - (unsigned long)q0*vn1; 159 0149 8B 4E EC mov cx,[word un21] 160 014C 8B 5E EE mov bx,[word un21+2] 161 014F 2B C8 sub cx,ax 162 0151 1B DA sbb bx,dx 163 0153 8B 46 F4 again0: mov ax,[q0] ; again0: 164 0156 F7 66 F8 mul [vn0] ; if ((unsigned long)q0*vn0 > (rhat << 16) 165 0159 3B D1 cmp dx,cx ; + un0) { 166 015B 72 12 jb l20 167 015D 77 05 ja l19 168 015F 3B 46 FC cmp ax,[un0] 169 0162 76 0B jbe l20 170 0164 FF 4E F4 l19: dec [q0] ; q0--; 171 0167 03 4E FA add cx,[vn1] ; rhat += vn1; 172 016A 83 D3 00 adc bx,0 ; if ((rhat >> 16) == 0) 173 016D 74 E4 jz again0 ; goto again0; 174 016F 8B 5E 10 l20: mov bx,[r] ; } 175 0172 0B DB or bx,bx ; } 176 0174 74 28 jz l23 ; if (r) // Ако се иска остатъкът, върни го. 177 0176 8B 46 F4 mov ax,[q0] ; *r = ((un21 << 16) + un0 - q0*v) >> s; 178 0179 F7 66 0E mul [word v+2] 179 017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0) 180 017D 8B 46 F4 mov ax,[q0] 181 0180 F7 66 0C mul [word v] 182 0183 03 D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0) 183 0185 8B 4E FC mov cx,[un0] 184 0188 8B 7E EC mov di,[word un21] 185 018B 2B C8 sub cx,ax 186 018D 1B FA sbb di,dx 187 018F 87 CE xchg cx,si 188 0191 E3 06 jcxz l22 189 0193 D1 EF l21: shr di,1 190 0195 D1 DE rcr si,1 191 0197 E2 FA loop l21 192 0199 89 37 l22: mov [bx],si 193 019B 89 7F 02 mov [bx+2],di 194 019E 8B 46 F4 l23: mov ax,[q0] ; return ((unsigned long)q1 << 16) + q0; 195 01A1 8B 56 F6 mov dx,[q1]1 196 01A4 5E pop si1 197 01A5 5F pop di1 198 01A6 8B E5 mov sp,bp1 199 01A8 5D pop bp1 200 01A9 C3 ret 00000h 201 01AA endp ; } 202 203 01AA end

14.3.2. Netwide Assembler (NASM)

Разработката на този асемблер с открит изходен текст започва през 1996 г. от Simon Tatham и Julian Hall. Сега тясе води от няколко програмиста начело с Hans Peter Anvin. NASM (заедно с GAS) е най-популярният среднесъвместимите с MASM асемблери. Например NASM и GAS са двата асемблера, включени в пакета „Xcode“ заоперационната система „Mac OS X“ на фирмата „Apple“. Причините за широкото използване на NASM са: (1)откритият изходен текст, (2) мобилността поради използвания език C, (3) дългият срок на разработка и най-важното (4) многото поддържани изходни файлови формати – много повече, отколкото при другитеразглеждани тук асемблери. Например NASM единствен измежду тях поддържа обектния файлов формат„Mach-O“, използван в „Mac OS X“. Синтаксисът на командите на NASM е такъв, че адресацията на операнда сеопределя еднозначно само от формáта му в командата, а не от дефиницията на използваното име, както е приMASM. Пряката и косвената регистрова (с или без отместване) адресации в NASM винаги се отбелязват сквадратни скоби, а непосредствената – без тях. Ключовите думи в синтаксиса на MASM „offset“ и „ptr“ не сеподдържат в NASM. Подобно на A386 (т. 14.2.7), при генериране на изпълним файл тип „.COM“ нямазадължителни директиви . Задаването на аргументите на процедурите и на локалните променливи е сложно, новъзможно, както се вижда от листингите по-долу.

Page 110: Сборник задачи по програмиране на Асемблер

110Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

Заб.: NASM е единственият от разглежданите тук асемблери, който не включва заглавен ред с името си влистинга.

1 segment _TEXT class=CODE2 ; /* Дълго делене, беззнаково (64/32 ==> 32).3 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово4 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.5 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя6 ; връща остатък 0xFFFFFFFF (невъзможна стойност).7 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.8 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */9 ;10 ; unsigned long11 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)12 ; {13 global _divlu214 _divlu2:15 %push context16 %stacksize large17 %arg u1:dword, u0:dword, v:dword, r:word18 00000000 55 push bp19 00000001 89E5 mov bp,sp20 00000003 57 push di21 00000004 56 push si22 00000005 8B460C mov ax,word [v] ; if (u1 >= v) {// При препълване, запиши в23 00000008 8B560E mov dx,word [v+2]24 0000000B 394604 cmp word [u1],ax25 0000000E 8B5E06 mov bx,word [u1+2]26 00000011 19D3 sbb bx,dx27 00000013 7212 jb l228 00000015 8B5E10 mov bx,[r]29 00000018 09DB or bx,bx ; if (r) // остатъка невъзможна стойност30 0000001A B8FFFF mov ax,-1 ;ffffH31 0000001D 7405 jz l132 0000001F 8907 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото33 00000021 894702 mov [bx+2],ax34 00000024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.35 00000025 EB40 jmp short l8 ; }36 00000027 B94000 l2: mov cx,64 ; благодарности на Peter Norton за този метод!37 0000002A 31C0 xor ax,ax38 0000002C 31D2 xor dx,dx39 0000002E D16608 l3: shl word [u0],1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво40 00000031 D1560A rcl word [u0+2],141 00000034 D15604 rcl word [u1],142 00000037 D15606 rcl word [u1+2],143 0000003A D1D0 rcl ax,144 0000003C D1D2 rcl dx,145 0000003E 720A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)46 00000040 3B460C cmp ax,word [v] ; сравни DX:AX и делителя47 00000043 89D3 mov bx,dx48 00000045 1B5E0E sbb bx,word [v+2] ; DX:AX < делителя ?49 00000048 7209 jb l5 ; да, прескочи50 0000004A 2B460C l4: sub ax,word [v] ; не, извади делителя51 0000004D 1B560E sbb dx,word [v+2]52 00000050 FF4608 inc word [u0] ; добави 1,защото делителят влезе в делимото 1 път53 00000053 E2D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти54 00000055 8B5E10 mov bx,[r] ; if (r) // Ако се иска остатъкът, върни го.55 00000058 09DB or bx,bx56 0000005A 7405 jz l757 0000005C 8907 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX58 0000005E 895702 mov [bx+2],dx59 00000061 8B4608 l7: mov ax,word [u0]60 00000064 8B560A mov dx,word [u0+2]61 00000067 5E l8: pop si62 00000068 5F pop di63 00000069 5D pop bp64 0000006A C3 ret65 %pop ; }

Page 111: Сборник задачи по програмиране на Асемблер

111Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

1 segment _TEXT class=CODE2 ; /* Дълго делене, беззнаково (64/32 ==> 32).3 ; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово4 ; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.5 ; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя6 ; връща остатък 0xFFFFFFFF (невъзможна стойност).7 ; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.8 ; Върнатата стойност е частното. Остатъкът се записва по указателя r. */9 ;10 ; unsigned long11 ; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)12 ; {13 global _divlu214 _divlu2:15 %push context16 %stacksize large17 %arg u1:dword, u0:dword, v:dword, r:word18 ; unsigned short un1, un0, // Нормализирани младши цифри на делимото.19 ; vn1, vn0, // Нормализирани цифри на делителя.20 ; q1, q0; // Цифри на частното.21 ; unsigned long un32, un21, un10, // Двойки цифри на делимото.22 ; rhat; // Остатък.23 ; int s; // Колко бита изместваме за нормализация.24 %if __NASM_VERSION_ID__ < 2080000h25 %error NASM 2.08 or never required! Older versions generate bad offsets!26 %endif27 %assign %$localsize 028 %local un1:word,un0:word,vn1:word,vn0:word,q1:word,q0:word,un32:dword,un21:dword29 00000000 55 push bp30 00000001 89E5 mov bp,sp31 00000003 83C4EC add sp,byte -%$localsize32 00000006 57 push di33 00000007 56 push si34 00000008 8B460C mov ax,word [v] ; if (u1 >= v) {// При препълване, запиши в35 0000000B 8B560E mov dx,word [v+2]36 0000000E 394604 cmp word [u1],ax37 00000011 8B5E06 mov bx,word [u1+2]38 00000014 19D3 sbb bx,dx39 00000016 7213 jb l240 00000018 8B5E10 mov bx,[r]41 0000001B 09DB or bx,bx ; if (r) // остатъка невъзможна стойност42 0000001D B8FFFF mov ax,-1 ;ffffH43 00000020 7405 jz l144 00000022 8907 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото45 00000024 894702 mov [bx+2],ax46 00000027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.47 00000028 E97901 jmp l24 ; }48 0000002B 89D6 l2: mov si,dx ; if (v == 0)49 0000002D 09C6 or si,ax50 0000002F BE2000 mov si,32 ;0020H ; s = 32;51 00000032 744C jz l7 ; else { // 0 <= s <= 31.52 00000034 31F6 xor si,si ; s = 0; // Нормализирай делителя.53 00000036 39F2 cmp dx,si ; if (v <= 0x0000FFFFL) {54 00000038 7506 jne l355 0000003A BE1000 mov si,16 ;0010H ; s += 16;56 0000003D 92 xchg ax,dx ; v <<= 16;57 0000003E 31C0 xor ax,ax58 00000040 08F6 l3: or dh,dh ; }59 00000042 750B jnz l4 ; if (v <= 0x00FFFFFFL) {60 00000044 83C608 add si,byte 8 ; s += 8;61 00000047 88D6 mov dh,dl ; v <<= 8;62 00000049 88E2 mov dl,ah63 0000004B 88C4 mov ah,al64 0000004D 28C0 sub al,al65 0000004F 80FE0F l4: cmp dh,0Fh ; }66 00000052 7713 ja l5 ; if (v <= 0x0FFFFFFFL) {67 00000054 83C604 add si,byte 4 ; s += 4;68 00000057 D1E0 shl ax,1 ; v <<= 4;69 00000059 D1D2 rcl dx,170 0000005B D1E0 shl ax,171 0000005D D1D2 rcl dx,172 0000005F D1E0 shl ax,173 00000061 D1D2 rcl dx,174 00000063 D1E0 shl ax,175 00000065 D1D2 rcl dx,176 00000067 80FE3F l5: cmp dh,3Fh ; }77 0000006A 770B ja l6 ; if (v <= 0x3FFFFFFFL) {

Page 112: Сборник задачи по програмиране на Асемблер

112Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

78 0000006C 83C602 add si,byte 2 ; s += 2;79 0000006F D1E0 shl ax,1 ; v <<= 2;80 00000071 D1D2 rcl dx,181 00000073 D1E0 shl ax,182 00000075 D1D2 rcl dx,183 00000077 09D2 l6: or dx,dx ; }84 00000079 7805 js l7 ; if (v <= 0x7FFFFFFFL) {85 0000007B 46 inc si ; s++;86 0000007C D1E0 shl ax,1 ; v <<= 1;87 0000007E D1D2 rcl dx,188 00000080 89460C l7: mov word [v],ax ; }89 00000083 89560E mov word [v+2],dx ; } // Раздели делителя на две 16-битови цифри.90 00000086 8956FA mov [vn1],dx ; vn1 = (unsigned short)(v >> 16);91 00000089 8946F8 mov [vn0],ax ; vn0 = (unsigned short)v;92 0000008C 89F1 mov cx,si ; un32 = (u1 << s) |93 0000008E 8B5E04 mov bx,word [u1] ; ((u0 >> (32 - s)) & ((long)-s >> 31));94 00000091 8B7E06 mov di,word [u1+2]95 00000094 E306 jcxz l996 00000096 D1E3 l8: shl bx,197 00000098 D1D7 rcl di,198 0000009A E2FA loop l899 0000009C B120 l9: mov cl,32 ;0020H100 0000009E 29F1 sub cx,si101 000000A0 8B4608 mov ax,word [u0]102 000000A3 8B560A mov dx,word [u0+2]103 000000A6 7406 jz l11104 000000A8 D1EA l10: shr dx,1105 000000AA D1D8 rcr ax,1106 000000AC E2FA loop l10107 000000AE 09F6 l11: or si,si108 000000B0 7404 jz l12109 000000B2 09C3 or bx,ax110 000000B4 09D7 or di,dx111 000000B6 895EF0 l12: mov word [un32],bx112 000000B9 897EF2 mov word [un32+2],di113 000000BC 8B4608 mov ax,word [u0] ; un10 = u0 << s;// Измести делимото наляво.114 000000BF 8B560A mov dx,word [u0+2]115 000000C2 89F1 mov cx,si116 000000C4 E306 jcxz l14117 000000C6 D1E0 l13: shl ax,1118 000000C8 D1D2 rcl dx,1119 000000CA E2FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри.120 000000CC 8956FE l14: mov [un1],dx ; un1 = (unsigned short)(un10 >> 16);121 000000CF 8946FC mov [un0],ax ; un0 = (unsigned short)un10;122 000000D2 3B7EFA cmp di,[vn1] ; if ((un32 >> 16) >= vn1)// Избегни препълване123 000000D5 C746F6FFFF mov word [q1],-1 ; q1 = 0xFFFF; // при следващото делене.124 000000DA 7335 jae l17 ; else {// Изчисли първата цифра на частното, q1.125 000000DC 8B46F0 mov ax,word [un32] ; q1 = (unsigned short)(un32/vn1);126 000000DF 8B56F2 mov dx,word [un32+2]127 000000E2 F776FA div word [vn1]128 000000E5 8946F6 mov [q1],ax129 000000E8 F766FA mul word [vn1] ; rhat = un32 - (unsigned long)q1*vn1;130 000000EB 8B4EF0 mov cx,word [un32]131 000000EE 8B5EF2 mov bx,word [un32+2]132 000000F1 29C1 sub cx,ax133 000000F3 19D3 sbb bx,dx134 000000F5 8B46F6 again1: mov ax,[q1] ; again1:135 000000F8 F766F8 mul word [vn0] ; if ((unsigned long)q1*vn0 > (rhat << 16)136 000000FB 39CA cmp dx,cx ; + un1) {137 000000FD 7212 jb l17138 000000FF 7705 ja l16139 00000101 3B46FE cmp ax,[un1]140 00000104 760B jbe l17141 00000106 FF4EF6 l16: dec word [q1] ; q1--;142 00000109 034EFA add cx,[vn1] ; rhat += vn1;143 0000010C 83D300 adc bx,byte 0 ; if ((rhat >> 16) == 0)144 0000010F 74E4 jz again1 ; goto again1;145 00000111 8B46F6 l17: mov ax,[q1] ; }146 00000114 F7660E mul word [v+2] ; } // Умножи и извади.147 00000117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;148 00000118 8B46F6 mov ax,[q1] ; if ((un21 >> 16) >= vn1)// Избегни препълване149 0000011B F7660C mul word [v] ; CX = мл.д.(ст.д.(v)*q1)150 0000011E 01CA add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)151 00000120 8B7EF0 mov di,word [un32]152 00000123 8B5EFE mov bx,[un1]153 00000126 29C3 sub bx,ax154 00000128 19D7 sbb di,dx

Page 113: Сборник задачи по програмиране на Асемблер

113Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

155 0000012A 895EEC mov word [un21],bx156 0000012D 897EEE mov word [un21+2],di157 00000130 3B7EFA cmp di,[vn1]158 00000133 C746F4FFFF mov word [q0],-1 ; q0 = 0xFFFF; // при следващото делене.159 00000138 7335 jae l20 ; else {// Изчисли втората цифра на частното, q0.160 0000013A 8B46EC mov ax,word [un21] ; q0 = (unsigned short)(un21/vn1);161 0000013D 8B56EE mov dx,word [un21+2]162 00000140 F776FA div word [vn1]163 00000143 8946F4 mov [q0],ax164 00000146 F766FA mul word [vn1] ; rhat = un21 - (unsigned long)q0*vn1;165 00000149 8B4EEC mov cx,word [un21]166 0000014C 8B5EEE mov bx,word [un21+2]167 0000014F 29C1 sub cx,ax168 00000151 19D3 sbb bx,dx169 00000153 8B46F4 again0: mov ax,[q0] ; again0:170 00000156 F766F8 mul word [vn0] ; if ((unsigned long)q0*vn0 > (rhat << 16)171 00000159 39CA cmp dx,cx ; + un0) {172 0000015B 7212 jb l20173 0000015D 7705 ja l19174 0000015F 3B46FC cmp ax,[un0]175 00000162 760B jbe l20176 00000164 FF4EF4 l19: dec word [q0] ; q0--;177 00000167 034EFA add cx,[vn1] ; rhat += vn1;178 0000016A 83D300 adc bx,byte 0 ; if ((rhat >> 16) == 0)179 0000016D 74E4 jz again0 ; goto again0;180 0000016F 8B5E10 l20: mov bx,[r] ; }181 00000172 09DB or bx,bx ; }182 00000174 7428 jz l23 ; if (r) // Ако се иска остатъкът, върни го.183 00000176 8B46F4 mov ax,[q0] ; *r = ((un21 << 16) + un0 - q0*v) >> s;184 00000179 F7660E mul word [v+2]185 0000017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)186 0000017D 8B46F4 mov ax,[q0]187 00000180 F7660C mul word [v]188 00000183 01CA add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)189 00000185 8B4EFC mov cx,[un0]190 00000188 8B7EEC mov di,word [un21]191 0000018B 29C1 sub cx,ax192 0000018D 19D7 sbb di,dx193 0000018F 87CE xchg cx,si194 00000191 E306 jcxz l22195 00000193 D1EF l21: shr di,1196 00000195 D1DE rcr si,1197 00000197 E2FA loop l21198 00000199 8937 l22: mov [bx],si199 0000019B 897F02 mov [bx+2],di200 0000019E 8B46F4 l23: mov ax,[q0] ; return ((unsigned long)q1 << 16) + q0;201 000001A1 8B56F6 mov dx,[q1]202 000001A4 5E l24: pop si203 000001A5 5F pop di204 000001A6 89EC mov sp,bp205 000001A8 5D pop bp206 000001A9 C3 ret207 %pop ; }

14.3.3. NewBasic Assembler (NBASM)

Това е асемблер, разработван от фирмата „Forever Young Software“ от 1998 г. насам (автор – Benjamin David Lunt). По синтаксиса на командите той прилича на YASM – асемблер, произлязъл от по-ранна версия на NASM (т.14.3.2). Подобно на YASM той генерира листинг с превръщане на табулаторите в интервали и орязване наредовете без код, което го прави трудно четим (вж. по-долу). За разлика от YASM той не поддържа макроси игенерира обектни файлове във формат „OMF“. За съжаление реализацията на директивата „equ“ в този асемблере твърде примитивна . Освен това при наличие на директива „.public“ NBASM генерира грешен обектен файл.Знаейки това, авторът е оставил тази директива недокументирана . Но без нея в обектния файл няма име, коетосвързващият редактор да намери, за да извлече по него обектния код. Така и в двата случая (с и без „.public“) сеполучава неизползваем обектен файл. Поправянето на тази грешка и разширяването на възможностите надирективата „equ“ би направило NBASМ използваем за по-сериозни проекти.

Page 114: Сборник задачи по програмиране на Асемблер

114Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

NewBasic Forever Young Software NBasm Lst FileNBASM list file for: DIVLUSS.NBA Sun Aug 28 11:52:57 2011

LOC line object code SOURCE0000 1 .model small0000 2 .code0000 14 .public _divlu20000 15 _divlu2:0000 16 u1 equ bp+40000 17 u0 equ bp+80000 18 v equ bp+120000 19 r equ bp+160000 20 55 push bp0001 21 8BEC mov bp,sp0003 22 57 push di0004 23 56 push si0005 24 8B460C mov ax,[v] ; if (u1 >= v) {// При препълване, запиши в0008 25 8B560E mov dx,[v+2]000B 26 394604 cmp [u1],ax000E 27 8B5E06 mov bx,[u1+2]0011 28 19D3 sbb bx,dx0013 29 7212 jb l20015 30 8B5E10 mov bx,[r]0018 31 09DB or bx,bx ; if (r) // остатъка невъзможна стойност001A 32 B8FFFF mov ax,-1 ;ffffH001D 33 7405 jz l1001F 34 8907 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0021 35 894702 mov [bx+2],ax0024 36 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0025 37 EB40 jmp short l8 ; }0027 38 B94000 l2: mov cx,64 ; благодарности на Peter Norton за този метод!002A 39 31C0 xor ax,ax002C 40 31D2 xor dx,dx002E 41 D16608 l3: shl word [u0],1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво0031 42 D1560A rcl word [u0+2],10034 43 D15604 rcl word [u1],10037 44 D15606 rcl word [u1+2],1003A 45 D1D0 rcl ax,1003C 46 D1D2 rcl dx,1003E 47 720A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)0040 48 3B460C cmp ax,[v] ; сравни DX:AX и делителя0043 49 8BDA mov bx,dx0045 50 1B5E0E sbb bx,[v+2] ; DX:AX < делителя ?0048 51 7209 jb l5 ; да, прескочи004A 52 2B460C l4: sub ax,[v] ; не, извади делителя004D 53 1B560E sbb dx,[v+2]0050 54 FF4608 inc word [u0] ; добави 1,защото делителят влезе в делимото 1 път0053 55 E2D9 l5: loop l3 ; продължи да преминаваш всичките 64 пъти0055 56 8B5E10 mov bx,[r] ; if (r) // Ако се иска остатъкът, върни го.0058 57 09DB or bx,bx005A 58 7405 jz l7005C 59 8907 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX005E 60 895702 mov [bx+2],dx0061 61 8B4608 l7: mov ax,[u0]0064 62 8B560A mov dx,[u0+2]0067 63 5E l8: pop si0068 64 5F pop di0069 65 5D pop bp006A 66 C3 ret ; }

Page 115: Сборник задачи по програмиране на Асемблер

115Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

NewBasic Forever Young Software NBasm Lst FileNBASM list file for: DIVLU2.NBA Sun Aug 28 11:53:02 2011

LOC line object code SOURCE0000 1 .model small0000 2 .code0000 14 .public _divlu20000 15 _divlu2:0000 16 u1 equ bp+40000 17 u0 equ bp+80000 18 v equ bp+120000 19 r equ bp+160000 26 un1 equ bp-20000 27 un0 equ bp-40000 28 vn1 equ bp-60000 29 vn0 equ bp-80000 30 q1 equ bp-100000 31 q0 equ bp-120000 32 un32 equ bp-160000 33 un21 equ bp-200000 34 55 push bp0001 35 8BEC mov bp,sp0003 36 83C4EC add sp,-200006 37 57 push di0007 38 56 push si0008 39 8B460C mov ax,[v] ; if (u1 >= v) {// При препълване, запиши в000B 40 8B560E mov dx,[v+2]000E 41 394604 cmp [u1],ax0011 42 8B5E06 mov bx,[u1+2]0014 43 19D3 sbb bx,dx0016 44 7213 jb l20018 45 8B5E10 mov bx,[r]001B 46 09DB or bx,bx ; if (r) // остатъка невъзможна стойност001D 47 B8FFFF mov ax,-1 ;ffffH0020 48 7405 jz l10022 49 8907 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0024 50 894702 mov [bx+2],ax0027 51 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0028 52 E97901 jmp l24 ; }002B 53 8BF2 l2: mov si,dx ; if (v == 0)002D 54 09C6 or si,ax002F 55 BE2000 mov si,32 ;0020H ; s = 32;0032 56 744C jz l7 ; else { // 0 <= s <= 31.0034 57 31F6 xor si,si ; s = 0; // Нормализирай делителя.0036 58 39F2 cmp dx,si ; if (v <= 0x0000FFFFL) {0038 59 7506 jne l3003A 60 BE1000 mov si,16 ;0010H ; s += 16;003D 61 92 xchg ax,dx ; v <<= 16;003E 62 31C0 xor ax,ax0040 63 08F6 l3: or dh,dh ; }0042 64 750B jnz l4 ; if (v <= 0x00FFFFFFL) {0044 65 83C608 add si,8 ; s += 8;0047 66 8AF2 mov dh,dl ; v <<= 8;0049 67 8AD4 mov dl,ah004B 68 8AE0 mov ah,al004D 69 28C0 sub al,al004F 70 80FE0F l4: cmp dh,0Fh ; }0052 71 7713 ja l5 ; if (v <= 0x0FFFFFFFL) {0054 72 83C604 add si,4 ; s += 4;0057 73 D1E0 shl ax,1 ; v <<= 4;0059 74 D1D2 rcl dx,1005B 75 D1E0 shl ax,1005D 76 D1D2 rcl dx,1005F 77 D1E0 shl ax,10061 78 D1D2 rcl dx,10063 79 D1E0 shl ax,10065 80 D1D2 rcl dx,10067 81 80FE3F l5: cmp dh,3Fh ; }006A 82 770B ja l6 ; if (v <= 0x3FFFFFFFL) {006C 83 83C602 add si,2 ; s += 2;006F 84 D1E0 shl ax,1 ; v <<= 2;0071 85 D1D2 rcl dx,10073 86 D1E0 shl ax,10075 87 D1D2 rcl dx,10077 88 09D2 l6: or dx,dx ; }0079 89 7805 js l7 ; if (v <= 0x7FFFFFFFL) {

Page 116: Сборник задачи по програмиране на Асемблер

116Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

007B 90 46 inc si ; s++;007C 91 D1E0 shl ax,1 ; v <<= 1;007E 92 D1D2 rcl dx,10080 93 89460C l7: mov [v],ax ; }0083 94 89560E mov [v+2],dx ; } // Раздели делителя на две 16-битови цифри.0086 95 8956FA mov [vn1],dx ; vn1 = (unsigned short)(v >> 16);0089 96 8946F8 mov [vn0],ax ; vn0 = (unsigned short)v;008C 97 8BCE mov cx,si ; un32 = (u1 << s) |008E 98 8B5E04 mov bx,[u1] ; ((u0 >> (32 - s)) & ((long)-s >> 31));0091 99 8B7E06 mov di,[u1+2]0094 100 E306 jcxz l90096 101 D1E3 l8: shl bx,10098 102 D1D7 rcl di,1009A 103 E2FA loop l8009C 104 B120 l9: mov cl,32 ;0020H009E 105 29F1 sub cx,si00A0 106 8B4608 mov ax,[u0]00A3 107 8B560A mov dx,[u0+2]00A6 108 7406 jz l1100A8 109 D1EA l10: shr dx,100AA 110 D1D8 rcr ax,100AC 111 E2FA loop l1000AE 112 09F6 l11: or si,si00B0 113 7404 jz l1200B2 114 09C3 or bx,ax00B4 115 09D7 or di,dx00B6 116 895EF0 l12: mov [un32],bx00B9 117 897EF2 mov [un32+2],di00BC 118 8B4608 mov ax,[u0] ; un10 = u0 << s;// Измести делимото наляво.00BF 119 8B560A mov dx,[u0+2]00C2 120 8BCE mov cx,si00C4 121 E306 jcxz l1400C6 122 D1E0 l13: shl ax,100C8 123 D1D2 rcl dx,100CA 124 E2FA loop l13 ; // Раздели мл. половина на делимото на 2 цифри.00CC 125 8956FE l14: mov [un1],dx ; un1 = (unsigned short)(un10 >> 16);00CF 126 8946FC mov [un0],ax ; un0 = (unsigned short)un10;00D2 127 3B7EFA cmp di,[vn1] ; if ((un32 >> 16) >= vn1)// Избегни препълване00D5 128 C746F6FFFF mov word [q1],-1 ; q1 = 0xFFFF; // при следващото делене.00DA 129 7335 jae l17 ; else {// Изчисли първата цифра на частното, q1.00DC 130 8B46F0 mov ax,[un32] ; q1 = (unsigned short)(un32/vn1);00DF 131 8B56F2 mov dx,[un32+2]00E2 132 F776FA div word [vn1]00E5 133 8946F6 mov [q1],ax00E8 134 F766FA mul word [vn1] ; rhat = un32 - (unsigned long)q1*vn1;00EB 135 8B4EF0 mov cx,[un32]00EE 136 8B5EF2 mov bx,[un32+2]00F1 137 29C1 sub cx,ax00F3 138 19D3 sbb bx,dx00F5 139 8B46F6 again1: mov ax,[q1] ; again1:00F8 140 F766F8 mul word [vn0] ; if ((unsigned long)q1*vn0 > (rhat << 16)00FB 141 39CA cmp dx,cx ; + un1) {00FD 142 7212 jb l1700FF 143 7705 ja l160101 144 3B46FE cmp ax,[un1]0104 145 760B jbe l170106 146 FF4EF6 l16: dec word [q1] ; q1--;0109 147 034EFA add cx,[vn1] ; rhat += vn1;010C 148 83D300 adc bx,0 ; if ((rhat >> 16) == 0)010F 149 74E4 jz again1 ; goto again1;0111 150 8B46F6 l17: mov ax,[q1] ; }0114 151 F7660E mul word [v+2] ; } // Умножи и извади.0117 152 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;0118 153 8B46F6 mov ax,[q1] ; if ((un21 >> 16) >= vn1) // Избегни препълване011B 154 F7660C mul word [v] ; CX = мл.д.(ст.д.(v)*q1)011E 155 01CA add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)0120 156 8B7EF0 mov di,[un32]0123 157 8B5EFE mov bx,[un1]0126 158 29C3 sub bx,ax0128 159 19D7 sbb di,dx012A 160 895EEC mov [un21],bx012D 161 897EEE mov [un21+2],di0130 162 3B7EFA cmp di,[vn1]0133 163 C746F4FFFF mov word [q0],-1 ; q0 = 0xFFFF; // при следващото делене.0138 164 7335 jae l20 ; else {// Изчисли втората цифра на частното, q0.013A 165 8B46EC mov ax,[un21] ; q0 = (unsigned short)(un21/vn1);013D 166 8B56EE mov dx,[un21+2]

Page 117: Сборник задачи по програмиране на Асемблер

117Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0140 167 F776FA div word [vn1]0143 168 8946F4 mov [q0],ax0146 169 F766FA mul word [vn1] ; rhat = un21 - (unsigned long)q0*vn1;0149 170 8B4EEC mov cx,[un21]014C 171 8B5EEE mov bx,[un21+2]014F 172 29C1 sub cx,ax0151 173 19D3 sbb bx,dx0153 174 8B46F4 again0: mov ax,[q0] ; again0:0156 175 F766F8 mul word [vn0] ; if ((unsigned long)q0*vn0 > (rhat << 16)0159 176 39CA cmp dx,cx ; + un0) {015B 177 7212 jb l20015D 178 7705 ja l19015F 179 3B46FC cmp ax,[un0]0162 180 760B jbe l200164 181 FF4EF4 l19: dec word [q0] ; q0--;0167 182 034EFA add cx,[vn1] ; rhat += vn1;016A 183 83D300 adc bx,0 ; if ((rhat >> 16) == 0)016D 184 74E4 jz again0 ; goto again0;016F 185 8B5E10 l20: mov bx,[r] ; }0172 186 09DB or bx,bx ; }0174 187 7428 jz l23 ; if (r) // Ако се иска остатъкът, върни го.0176 188 8B46F4 mov ax,[q0] ; *r = ((un21 << 16) + un0 - q0*v) >> s;0179 189 F7660E mul word [v+2]017C 190 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)017D 191 8B46F4 mov ax,[q0]0180 192 F7660C mul word [v]0183 193 01CA add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)0185 194 8B4EFC mov cx,[un0]0188 195 8B7EEC mov di,[un21]018B 196 29C1 sub cx,ax018D 197 19D7 sbb di,dx018F 198 87F1 xchg cx,si0191 199 E306 jcxz l220193 200 D1EF l21: shr di,10195 201 D1DE rcr si,10197 202 E2FA loop l210199 203 8937 l22: mov [bx],si019B 204 897F02 mov [bx+2],di019E 205 8B46F4 l23: mov ax,[q0] ; return ((unsigned long)q1 << 16) + q0;01A1 206 8B56F6 mov dx,[q1]01A4 207 5E l24: pop si01A5 208 5F pop di01A6 209 8BE5 mov sp,bp01A8 210 5D pop bp01A9 211 C3 ret ; }

14.3.4. Relocatable Assembler (RASM-86)

Този асемблер на фирмата „Digital Research“ е разработван от 1982 г. до 1987 г. С него те асемблират повечето отизходните текстове на Асемблер за x86 в операционните си системи. Той има по-различни директиви от MASMи разпознава само единия от двата поддържани от него синтаксиса на операндите с базова, индексна и базово-индексна адресация с отместване.

Заб.: Единият синтаксис (с квадратни скоби около сбора на регистрите и отместването) се използва в дадените втт. 14.1–14.2 варианти на решението на зад. №№49–50 (тт. 13.4–13.5). Другият (с квадратни скоби около всекирегистър) е даден в листингите по-долу. RASM-86 разпознава само него, с някои особености.

Page 118: Сборник задачи по програмиране на Асемблер

118Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

PC-DOS RASM-86 1.4a Source: DIVLUSS.R86 Page 1

_TEXT cseg; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public _divlu2

_divlu2: 0004 u1 equ ss:dword ptr 4[bp] 0008 u0 equ ss:dword ptr 8[bp] 000C v equ ss:dword ptr 12[bp] 0010 r equ ss:word ptr 16[bp]0000 55 push bp0001 8BEC mov bp,sp0003 57 push di0004 56 push si0005 8B460C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в0008 8B560E mov dx,word ptr v+2000B 394604 cmp word ptr u1,ax000E 8B5E06 mov bx,word ptr u1+20011 1BDA sbb bx,dx0013 7212 0027 jb l20015 8B5E10 mov bx,r0018 0BDB or bx,bx ; if (r) // остатъка невъзможна стойност001A B8FFFF mov ax,-1 ;ffffH001D 7405 0024 jz l1001F 8907 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0021 894702 mov 2[bx],ax0024 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0025 EB40 0067 jmps l8 ; }0027 B94000 l2: mov cx,64 ; благодарности на Peter Norton за този метод!002A 33C0 xor ax,ax002C 33D2 xor dx,dx002E D16608 l3: shl word ptr u0,1 ; измести 96 бита от DX:AX:делимо с 1 бит наляво0031 D1560A rcl word ptr u0+2,10034 D15604 rcl word ptr u1,10037 D15606 rcl word ptr u1+2,1003A D1D0 rcl ax,1003C D1D2 rcl dx,1003E 720A 004A jc l4 ; бит 97 (оправена грешка, вж. „Hacker’s Delight“, листинг 9-2)0040 3B460C cmp ax,word ptr v ; сравни DX:AX и делителя0043 8BDA mov bx,dx0045 1B5E0E sbb bx,word ptr v+2 ; DX:AX < делителя ?0048 7209 0053 jb l5 ; да, прескочи004A 2B460C l4: sub ax,word ptr v ; не, извади делителя004D 1B560E sbb dx,word ptr v+20050 FF4608 inc word ptr u0 ; добави 1,защото делителят влезе в делимото 1 път0053 E2D9 002E l5: loop l3 ; продължи да преминаваш всичките 64 пъти0055 8B5E10 mov bx,r ; if (r) // Ако се иска остатъкът, върни го.0058 0BDB or bx,bx005A 7405 0061 jz l7005C 8907 l6: mov [bx],ax ; завършваме с частното в u0 и остатъка в DX:AX005E 895702 mov 2[bx],dx0061 8B4608 l7: mov ax,word ptr u00064 8B560A mov dx,word ptr u0+20067 5E l8: pop si0068 5F pop di0069 5D pop bp006A C3 ret ; }

Page 119: Сборник задачи по програмиране на Асемблер

119Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

PC-DOS RASM-86 1.4a Source: DIVLU2.R86 Page 1

_TEXT cseg; /* Дълго делене, беззнаково (64/32 ==> 32).; Тази процедура изпълнява беззнаково „дълго делене“, т.е. дели 64-битово; беззнаково делимо на 32-битов беззнаков делител, давайки 32-битово частно.; В случаите на препълване (делене на 0 или частното надхвърля 32 бита), тя; връща остатък 0xFFFFFFFF (невъзможна стойност).; Делимото е u1 и u0, като u1 е старшата част. Делителят е параметърът v.; Върнатата стойност е частното. Остатъкът се записва по указателя r. */;; unsigned long; divlu2(unsigned long u1, unsigned long u0, unsigned long v, unsigned long *r)

; { public _divlu2

_divlu2: 0004 u1 equ ss:dword ptr 4[bp] 0008 u0 equ ss:dword ptr 8[bp] 000C v equ ss:dword ptr 12[bp] 0010 r equ ss:word ptr 16[bp]

; unsigned short un1, un0, // Нормализирани младши цифри на делимото.; vn1, vn0, // Нормализирани цифри на делителя.; q1, q0; // Цифри на частното.; unsigned long un32, un21, un10, // Двойки цифри на делимото.; rhat; // Остатък.; int s; // Колко бита изместваме за нормализация.

FFFE un1 equ ss:word ptr -2[bp] FFFC un0 equ ss:word ptr -4[bp] FFFA vn1 equ ss:word ptr -6[bp] FFF8 vn0 equ ss:word ptr -8[bp] FFF6 q1 equ ss:word ptr -10[bp] FFF4 q0 equ ss:word ptr -12[bp] FFF0 un32 equ ss:dword ptr -16[bp] FFEC un21 equ ss:dword ptr -20[bp]0000 55 push bp0001 8BEC mov bp,sp0003 83C4EC add sp,-200006 57 push di0007 56 push si0008 8B460C mov ax,word ptr v ; if (u1 >= v) {// При препълване, запиши в000B 8B560E mov dx,word ptr v+2000E 394604 cmp word ptr u1,ax0011 8B5E06 mov bx,word ptr u1+20014 1BDA sbb bx,dx0016 7213 002B jb l20018 8B5E10 mov bx,r001B 0BDB or bx,bx ; if (r) // остатъка невъзможна стойност001D B8FFFF mov ax,-1 ;ffffH0020 7405 0027 jz l10022 8907 mov [bx],ax ; *r = 0xFFFFFFFFL;// и върни най-голямото0024 894702 mov 2[bx],ax0027 99 l1: cwd ; return 0xFFFFFFFFL;// възможно частно.0028 E97901 01A4 jmp l24 ; }002B 8BF2 l2: mov si,dx ; if (v == 0)002D 0BF0 or si,ax002F BE2000 mov si,32 ;0020H ; s = 32;0032 744C 0080 jz l7 ; else { // 0 <= s <= 31.0034 33F6 xor si,si ; s = 0; // Нормализирай делителя.0036 3BD6 cmp dx,si ; if (v <= 0x0000FFFFL) {0038 7506 0040 jne l3003A BE1000 mov si,16 ;0010H ; s += 16;003D 92 xchg ax,dx ; v <<= 16;003E 33C0 xor ax,ax0040 0AF6 l3: or dh,dh ; }0042 750B 004F jnz l4 ; if (v <= 0x00FFFFFFL) {0044 83C608 add si,8 ; s += 8;0047 8AF2 mov dh,dl ; v <<= 8;0049 8AD4 mov dl,ah004B 8AE0 mov ah,al004D 2AC0 sub al,al004F 80FE0F l4: cmp dh,0Fh ; }0052 7713 0067 ja l5 ; if (v <= 0x0FFFFFFFL) {0054 83C604 add si,4 ; s += 4;0057 D1E0 shl ax,1 ; v <<= 4;0059 D1D2 rcl dx,1005B D1E0 shl ax,1

Page 120: Сборник задачи по програмиране на Асемблер

120Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

005D D1D2 rcl dx,1005F D1E0 shl ax,10061 D1D2 rcl dx,10063 D1E0 shl ax,10065 D1D2 rcl dx,10067 80FE3F l5: cmp dh,3Fh ; }006A 770B 0077 ja l6 ; if (v <= 0x3FFFFFFFL) {006C 83C602 add si,2 ; s += 2;006F D1E0 shl ax,1 ; v <<= 2;0071 D1D2 rcl dx,10073 D1E0 shl ax,10075 D1D2 rcl dx,10077 0BD2 l6: or dx,dx ; }0079 7805 0080 js l7 ; if (v <= 0x7FFFFFFFL) {007B 46 inc si ; s++;007C D1E0 shl ax,1 ; v <<= 1;007E D1D2 rcl dx,10080 89460C l7: mov word ptr v,ax ; }0083 89560E mov word ptr v+2,dx ; } // Раздели делителя на две 16-битови цифри.0086 8956FA mov vn1,dx ; vn1 = (unsigned short)(v >> 16);0089 8946F8 mov vn0,ax ; vn0 = (unsigned short)v;008C 8BCE mov cx,si ; un32 = (u1 << s) |008E 8B5E04 mov bx,word ptr u1 ; ((u0 >> (32 - s)) & ((long)-s >> 31));0091 8B7E06 mov di,word ptr u1+20094 E306 009C jcxz l90096 D1E3 l8: shl bx,10098 D1D7 rcl di,1009A E2FA 0096 loop l8009C B120 l9: mov cl,32 ;0020H009E 2BCE sub cx,si00A0 8B4608 mov ax,word ptr u000A3 8B560A mov dx,word ptr u0+200A6 7406 00AE jz l1100A8 D1EA l10: shr dx,100AA D1D8 rcr ax,100AC E2FA 00A8 loop l1000AE 0BF6 l11: or si,si00B0 7404 00B6 jz l1200B2 0BD8 or bx,ax00B4 0BFA or di,dx00B6 895EF0 l12: mov word ptr un32,bx00B9 897EF2 mov word ptr un32+2,di00BC 8B4608 mov ax,word ptr u0 ; un10 = u0 << s;// Измести делимото наляво.00BF 8B560A mov dx,word ptr u0+200C2 8BCE mov cx,si00C4 E306 00CC jcxz l1400C6 D1E0 l13: shl ax,100C8 D1D2 rcl dx,100CA E2FA 00C6 loop l13 ; // Раздели мл. половина на делимото на 2 цифри.00CC 8956FE l14: mov un1,dx ; un1 = (unsigned short)(un10 >> 16);00CF 8946FC mov un0,ax ; un0 = (unsigned short)un10;00D2 3B7EFA cmp di,vn1 ; if ((un32 >> 16) >= vn1)// Избегни препълване00D5 C746F6FFFF mov q1,-1 ; q1 = 0xFFFF; // при следващото делене.00DA 7335 0111 jae l17 ; else {// Изчисли първата цифра на частното, q1.00DC 8B46F0 mov ax,word ptr un32; q1 = (unsigned short)(un32/vn1);00DF 8B56F2 mov dx,word ptr un32+200E2 F776FA div vn100E5 8946F6 mov q1,ax00E8 F766FA mul vn1 ; rhat = un32 - (unsigned long)q1*vn1;00EB 8B4EF0 mov cx,word ptr un3200EE 8B5EF2 mov bx,word ptr un32+200F1 2BC8 sub cx,ax00F3 1BDA sbb bx,dx00F5 8B46F6 again1: mov ax,q1 ; again1:00F8 F766F8 mul vn0 ; if ((unsigned long)q1*vn0 > (rhat << 16)00FB 3BD1 cmp dx,cx ; + un1) {00FD 7212 0111 jb l1700FF 7705 0106 ja l160101 3B46FE cmp ax,un10104 760B 0111 jbe l170106 FF4EF6 l16: dec q1 ; q1--;0109 034EFA add cx,vn1 ; rhat += vn1;010C 83D300 adc bx,0 ; if ((rhat >> 16) == 0)010F 74E4 00F5 jz again1 ; goto again1;0111 8B46F6 l17: mov ax,q1 ; }0114 F7660E mul word ptr v+2 ; } // Умножи и извади.0117 91 xchg ax,cx ; un21 = (un32 << 16) + un1 - q1*v;

Page 121: Сборник задачи по програмиране на Асемблер

121Сборник задачи по програмиране на Асемблер

08 септември 2011 16:04:09http://x86.hit.bg/

0118 8B46F6 mov ax,q1 ; if ((un21 >> 16) >= vn1)// Избегни препълване011B F7660C mul word ptr v; CX = мл.д.(ст.д.(v)*q1)011E 03D1 add dx,cx ; ст.д.(мл.д.(v)*q1) + мл.д.(ст.д.(v)*q1)0120 8B7EF0 mov di,word ptr un320123 8B5EFE mov bx,un10126 2BD8 sub bx,ax0128 1BFA sbb di,dx012A 895EEC mov word ptr un21,bx012D 897EEE mov word ptr un21+2,di0130 3B7EFA cmp di,vn10133 C746F4FFFF mov q0,-1 ; q0 = 0xFFFF; // при следващото делене.0138 7335 016F jae l20 ; else {// Изчисли втората цифра на частното, q0.013A 8B46EC mov ax,word ptr un21; q0 = (unsigned short)(un21/vn1);013D 8B56EE mov dx,word ptr un21+20140 F776FA div vn10143 8946F4 mov q0,ax0146 F766FA mul vn1 ; rhat = un21 - (unsigned long)q0*vn1;0149 8B4EEC mov cx,word ptr un21014C 8B5EEE mov bx,word ptr un21+2014F 2BC8 sub cx,ax0151 1BDA sbb bx,dx0153 8B46F4 again0: mov ax,q0 ; again0:0156 F766F8 mul vn0 ; if ((unsigned long)q0*vn0 > (rhat << 16)0159 3BD1 cmp dx,cx ; + un0) {015B 7212 016F jb l20015D 7705 0164 ja l19015F 3B46FC cmp ax,un00162 760B 016F jbe l200164 FF4EF4 l19: dec q0 ; q0--;0167 034EFA add cx,vn1 ; rhat += vn1;016A 83D300 adc bx,0 ; if ((rhat >> 16) == 0)016D 74E4 0153 jz again0 ; goto again0;016F 8B5E10 l20: mov bx,r ; }0172 0BDB or bx,bx ; }0174 7428 019E jz l23 ; if (r) // Ако се иска остатъкът, върни го.0176 8B46F4 mov ax,q0 ; *r = ((un21 << 16) + un0 - q0*v) >> s;0179 F7660E mul word ptr v+2017C 91 xchg ax,cx ; мл.д.(ст.д.(v)*q0)017D 8B46F4 mov ax,q00180 F7660C mul word ptr v0183 03D1 add dx,cx ; ст.д.(мл.д.(v)*q0) + мл.д.(ст.д.(v)*q0)0185 8B4EFC mov cx,un00188 8B7EEC mov di,word ptr un21018B 2BC8 sub cx,ax018D 1BFA sbb di,dx018F 87CE xchg cx,si0191 E306 0199 jcxz l220193 D1EF l21: shr di,10195 D1DE rcr si,10197 E2FA 0193 loop l210199 8937 l22: mov [bx],si019B 897F02 mov 2[bx],di019E 8B46F4 l23: mov ax,q0 ; return ((unsigned long)q1 << 16) + q0;01A1 8B56F6 mov dx,q101A4 5E l24: pop si01A5 5F pop di01A6 8BE5 mov sp,bp01A8 5D pop bp01A9 C3 ret ; }

—♦—