XT: 8086 дизасемблер

Передмова

У попередній статті про InfoSec редактор BE ми розглянули просте завдання написати термінальний дизасемблер використовуючи існуючі бібліотеки дизасемблювання. У цій статті ми покажемо як написати таку бібліотеку самим.

Ця вправа може стати лабораторною роботою, або курсовою роботою по курсу "КВ-123-17: Мікропроцесорні архітектури" для студентів першого курсу, які тільки знайомляться з існуючими архітектурами. Зазвичай у педагогічному процесі вибираються прості мікропроцесорні архітектури (такі як MIPS) або спеціалізовані для викладання (такі як RISC-V). Так чи інакше, працюючи у цій області, вам доведеться зіткнутися зі всіма архітектурами.

У якості асемблера, або мови програмування на якій пропонується виконувати завдання ми вибрали NASM, який вже використали для побудови термінального дизасемблера. Заодно буде з чим порівнювати, так як ndisasm з поставки nasm займає у бінарному вигляді 1.5 мегабайта. Ми ж даємо обмеження у цьому завданні на дизасемблер — 64 кілобайта.

Щоб вернути x86 архітектуру в академічне русло, ми обмежемо набір інструкцій системою команд мікропроцесора 8086, яка розширяє систему команд 8-бітних процесорів 8085 за допомогою додаткового байта MOD_R/M і збільшує таким чином розмір команди до 6 байт (довжина конвеєру).

Вступ

У курсі "КВ-123-16 Основи схемотехніки" для студентів першого курсу ви знайомитеся з чого складається елементна база 2-бітних мікропроцесорів Intel 3000, та як збирати такі системи самому. У якості підручника ми використовуємо "Цифрові ЕОМ: Практикум". Самофалов, Корнійчук, Тарасенко, Жабін. Київ, "Вища школа", 1990. Однак після 2-бітних, де ви самі програмували матрицю мікрокоду для команд, 4-бітних та 8-бітних процесорів, тільки 16-бітні процесори відкривають нову епоху сучасних процесорів Intel. Починаючи з цієї архітектури далі усі подальші процесори компанії Intel були сумісними між собою, тож вивчивши архітектуру 8086 ви зрозумієте контекст розвитку EM64T.

РікКодБітністьОписЧіпсет
197030002Intel 3000DIY
197140004Intel 4000MCS-4
1974ZX8Intel 8085/8080MCS-85
1976XT16Intel 8086/8088MCS-86
1982AT16Intel 80286PS/2 Model 30
1985IA-3232Intel 80386PS/2 Model 50
1989IA-3232Intel 80486PS/2 Model 70
1993IA-3232Intel Pentium (P5/P54C)Triton
1995IA-3232Intel Pentium Pro/II/III82443BX
2000EM64T64Intel Pentium 482865P
2006EM64T64Intel CoreQ35
2008EM64T64Intel Atom
2011AVX128Intel Core (Sandy Bridge)Z77, X99
2013AVX-2256Intel Core (Haswell)Z97
2017AVX-512512Intel Phi, Core (Skylake)Z370, X299
2023AMX1024Intel Core (Sapphire Rapids)W790

Мотивація

Особливо важливий це курс для тих, хто на третьому році навчання буде слухати курси на кафедрі мовного забезпечення КА-121, такі як "КА-121-04 Верифікація програмного забезпечення", "КА-121-03 Верифікація мікропроцесорних архітектур", та "КА-121-05 Верифікація об`єктного коду".

Корисним цей курс буде для всіх, хто хоче навчитися вивчити асемблер у повному обсязі, так як ми пишемо асемблер та/або дизасеблер. У залежності від технічної орієнтації студентів, можна взяти альтернативний курс розробки LISP інтерпретатора на асемблері, але це вимагає знання програми другого року навчання.

Анотація

TL;DR — Дизасемблер на асемблері для Mac за 1 день на 672 байта для 45 опкодів, де використовується байт MOD_R/M (00, 01, 02, 03, 08, 09, 0A, 0B, 10, 11, 12, 13, 18, 19, 1A, 1B, 20, 21, 22, 23, 29, 29, 2A, 2B, 30, 31, 32, 33, 3C, 3D, 3E, 3F, 88, 89, 8A, 8B, 8D, 80, 81, 84, 85, 86, 87, C4, C5), тобто одразу для половини найскладніших мнемонік. Інші мнемоніки простіші та нагадуються 8085 асемблер.

Специфікація на архітектуру опкодів

Система команд 8086 розділяється на наступні категорії (загалом 89 мнемонік + 4 сегментних префікси): 1) інструкції пересилки даних (14): MOV, PUSH, POP, XCHG, IN, OUT, XLAT, LEA, LDS, LES, LAHF, SAHF, PUSHF, POPF; 2) арифметичні інструкції (20): ADD, ADC, INC, AAA, BAA, SUB, SSB, DEC, NEG, CMP, AAS, DAS, MUL, IMUL, AAM, DIV, IDIV, AAD, CBW, CWD; 3) логічні інструкції (11): NOT, SHL, SHR, ROL, ROR, RCL, RCR, AND, TEST, OR, XOR; 4) маніпуляція з послідовностями (6): REP, MOVS, CMPS, SCAS, LODS, STOS; 5) управління потоком виконання (27): CALL, JMP, RET, INT, INT 3, INTO, IRET, JE, LJ, JLE, JB, JBE, JP, JO, JS, JNE, JNL, JNLE, JNB, JUNBE, JNP, JNO, JNS, LOOP, LOOPZ, LOOPNZ, JCXZ; 6) управління станом процесору (11): CLC, CMC, STC, CLD, STD, CLI, STI, HLT, WAIT, ESC, LOCK.

Тут ми використовуємо виключно синтаксичну специфікацію, тобто визначення на формат бінарної серіалізації певної програми певної архітектури. На курсі формальної верифікації будуть використовуватися семантичні формальні моделі для кожної інструкції які будуть у сутності формальними програмами на MLTT які відповідають псевдокоду для кожної інструкції з документації Intel SDM. В цих специфікацях детально буде показано які біти яких регістрів змінюються та що відбувається з шиною пам'яті та даних. Зараз же ми соредимося виключно на серіалазації та компактифікації дизасемблера, тобто на синтаксичній специфікації.

Легенда

v 0: count=1; 1: count=CL; d 0: from reg; 1: to reg; w 0: byte; 1: word; sg 00: ES; 01: CS; 10: SS; 11: DS; mod 00: disp=0, disp-lo=N/A, disp-hi=N/A; 01: disp=disp-lo sign-extended to 16-bit, disp-hi=N/A; 10: disp=[disp-hi,disp-lo]; 11: r/m treaded as reg field; reg 000: AX AL; 001: CX CL; 010: DX DL; 011: BX BL; 100: SP AH; 101: BP CH; 110: SI DH; 111: DI BH; r/m 000: (BX) + (SI) + disp; 001: (BX) + (DI) + disp; 010: (BP) + (SI) + disp; 011: (BP) + (DI) + disp; 100: (SI) + disp; 101: (DI) + disp; 110: (BP) + disp or direct address when mod=00; 111: (BX) + disp;

Загальний формат інструкції

LOCK | префікс блокування шини REP | префікс циклічних операцій SEG | префікс перевизначення сегменту OPCODE | головний байт по якому здійснюється діспатч MOD R/M | байт mod r/m для кодування адресації та порядку операндів DISP | відносна адреса на шині IMM | безпосередній операнд

Таблиця мнемонік

Таблиця мнемонік буде вміщувати 136 рядків, однак ми звузимо завдання до лобораторної роботи для для першого рядка, це одразу 4 варіанта інструкції ADD.

1: ADD | 1000 00sw | mod 000 r/m | data | data if sw=01 ADC | 1000 00sw | mod 010 r/m | data | data if sw=01 SBB | 1000 00sw | mod 011 r/m | data | data if sw=01 SUB | 1000 00sw | mod 101 r/m | data | data if sw=01 CMP | 1000 00sw | mod 111 r/m | data | data if sw=01 2: OR | 1000 000w | mod reg r/m | data | data if w=1 TEST | 1111 011w | mod 000 r/m | data | data if w=1 MOV | 1100 011w | mod 000 r/m | data | data if w=1 AND | 1000 000w | mod 100 r/m | data | data if w=1 XOR | 1000 000w | mod 110 r/m | data | data if w=1 3: LDS | 1100 0101 | mod reg r/m LES | 1100 0100 | mod reg r/m ADD | 0000 00dw | mod reg r/m OR | 0000 10dw | mod reg r/m ADC | 0001 00dw | mod reg r/m SBB | 0001 10dw | mod reg r/m SUB | 0010 10dw | mod reg r/m AND | 0010 00dw | mod reg r/m CMP | 0011 10dw | mod reg r/m XOR | 0011 00dw | mod reg r/m MOV | 1000 10dw | mod reg r/m LEA | 1000 1101 | mod reg r/m TEST | 1000 010w | mod reg r/m XCHG | 1000 011w | mod reg r/m 4: NEG | 1111 011w | mod 011 r/m MUL | 1111 011w | mod 100 r/m IMUL | 1111 011w | mod 101 r/m NOT | 1111 011w | mod 010 r/m DIV | 1111 011w | mod 110 r/m IDIV | 1111 011w | mod 111 r/m INC | 1111 111w | mod 000 r/m DEC | 1111 111w | mod 001 r/m CALL | 1111 1111 | mod 010 r/m CALL | 1111 1111 | mod 011 r/m JMP | 1111 1111 | mod 100 r/m JMP | 1111 1111 | mod 101 r/m PUSH | 1111 1111 | mod 110 r/m SHL | 1101 00vw | mod 100 r/m SHR | 1101 00vw | mod 101 r/m SAR | 1101 00vw | mod 111 r/m ROL | 1101 00vw | mod 000 r/m ROR | 1101 00vw | mod 001 r/m RCL | 1101 00vw | mod 010 r/m RCR | 1101 00vw | mod 011 r/m POP | 1000 1111 | mod 000 r/m 5: ESC | 1101 1xxx | mod xxx r/m MOV | 1000 1100 | mod 0sg r/m MOV | 1000 1110 | mod 0sg r/m 6: ADD | 0000 010w | data | data if w=1 OR | 0000 110w | data | data if w=1 ADC | 0001 010w | data | data if w=1 SBB | 0001 110w | data | data if w=1 SUB | 0010 110w | data | data if w=1 AND | 0010 010w | data | data if w=1 CMP | 0011 110w | data | data if w=1 XOR | 0011 010w | data | data if w=1 TEST | 1010 100w | data | data if w=1 MOV | 1011 wreg | data | data if w=1 7: JMP | 1110 1010 | olo | ohi | slo | shi CALL | 1001 1010 | olo | ohi | slo | shi 8: MOV | 1010 000w | lo | hi MOV | 1010 001w | lo | hi RET | 1100 0010 | lo | hi RET | 1100 1010 | lo | hi CALL | 1110 1000 | lo | hi JMP | 1110 1001 | lo | hi 9: LOOPNE | 1110 0000 | disp LOOPE | 1110 0001 | disp LOOP | 1110 0010 | disp JCXZ | 1110 0011 | disp JMP | 1110 1011 | disp JE | 0111 0100 | disp JL | 0111 1100 | disp JLE | 0111 1110 | disp JB | 0111 0010 | disp JBE | 0111 0110 | disp JP | 0111 1010 | disp JO | 0111 0000 | disp JS | 0111 1000 | disp JNE | 0111 0101 | disp JNL | 0111 1101 | disp JNLE | 0111 1111 | disp JNB | 0111 0011 | disp JNBE | 0111 0111 | disp JNP | 0111 1011 | disp JNO | 0111 0001 | disp JNS | 0111 1001 | disp 10: IN | 1110 010w | port OUT | 1110 011w | port INT | 1100 1101 | type 11: AAM | 1101 0100 | 0000 1010 AAD | 1101 0101 | 0000 1010 12: PUSH | 000s g110 POP | 000s g111 13: INC | 0100 0reg DEC | 0100 1reg PUSH | 0101 0reg POP | 0101 1reg XCHG | 1001 0reg 14: MOVS | 1010 010w CMPS | 1010 011w SCAS | 1010 111w LODS | 1010 110w STOS | 1010 101w IN | 1110 110w OUT | 1110 111w 15: RET | 1100 0011 RET | 1100 1011 INT 3 | 1100 1100 INTO | 1100 1110 IRET | 1100 1111 XLAT | 1101 0111 NOP | 1001 0000 CBW | 1001 1000 CWD | 1001 1001 WAIT | 1001 1011 PUSHF | 1001 1100 POPF | 1001 1101 SAHF | 1001 1110 LAHF | 1001 1111 DAS | 0010 1111 AAA | 0011 0111 BAA | 0010 0111 AAS | 0011 1111 DAS | 0010 1111 AAA | 0011 0111 BAA | 0010 0111 AAS | 0011 1111 HLT | 1111 0100 CMC | 1111 0101 CLC | 1111 1000 STC | 1111 1001 CLI | 1111 1010 STI | 1111 1011 CLD | 1111 1100 STD | 1111 1101 16: LOCK | 1111 0000 REP | 1111 0010 REPNE | 1111 0011

Асемблер nasm

Ставимо популярний x86 асемблер з підтримкою усіх розширень Intel: MMX, SSE, SSE2, SSE3, SSE4.1 SSE4.2, SSE5, KNI, AVX, AVX-2, AVX-512, AES, AMX, ADX, TSX, MPX, FMA, VNNI, GFNI, SGX, CET, PKU. Ми будемо працювати в x86-64 режимі, з Mach-O вихідним форматом.

$ brew install nasm

Нуль-програма.

$ cat dasm.asm global _main default rel section .text _main: ret section .data

Асемблювання, лінковка та запуск.

$ nasm -f macho64 -o dasm.o dasm.asm gcc -fno-pie -o dasm dasm.o ./dasm

Таблиці

Почнемо практикум. Ми будемо робити діспатч по першому байту інструкції ігноруючи поки префікси, якшо це префікс ми будемо його просто запам`ятовувати у якості змінної в контексті. Будемо робити демонстрацію для перших чотирьох опкодів 00 01 02 03, але покажемо як заповнюється таблиця основного діспатча. Тут число 7 означає кількість послідовних комбінаторів мотузок які викликаються в процесі парса інструкції та друкують символи на термінал. Вже можна зробити покращення виділивши сімки в окрему область вірівняну по границі байту. Але тут для простоти вирівняємо і адресу мікрокоду діспатчу і кількість його елементів по 8-байтовій границі.

opcode: dq inst00, 7, inst01, 7, inst02, 7, inst03, 7 dq inst04, 7, inst05, 7, inst06, 7, inst07, 7 dq inst08, 7, inst09, 7, inst0A, 7, inst0B, 7 dq inst0C, 7, inst0D, 7, inst0E, 7, inst0F, 7

Кожна адреса з таблиці опкодів містить вирівняну по границі слова послідовність кожен елемент якої 16-бітне число, старший (по адресам) байт якого містить параметр, а молодший номер функції-комбінатора в таблиці функцій ropes), який друкує певну частину візуалізації інструкції (назву, операнд, дужки та навіть line feed).

0 — парсер mod_rm байт (преамбула);
1 — друк назви операції з таблиці символьних мнемонік;
2 — друк відкриття дужки та початку адресації;
3 — друк відносної адреси, якщо така використовується;
4 — друк закриття дужки;
5 — друк коми;
6 — друк регістру;
7 — друк A.

Для байткодів 02 та 03 де порядок аргументів звороній, алгоритм побудови зображення інструкції буде видозміненим: (0,1,6,5,2,3,4,7). У випадку коли mod=11 такий алгоритм повинен бути наступним: (0,1,6,5,6,7).

inst0: db 0,0,1,0,2,0,3,0,4,0,5,0,6,0,7,0 inst2: db 0,0,1,0,6,0,5,0,2,0,3,0,4,0,7,0 inst1 equ inst0 inst3 equ inst2

Таблиця функцій

Для побудови дизасемблера для mod r/m-байтових інструкцій достатньо наступного набору комбінаторів:

ropes: dq parse_mod_rm dq print_add dq print_rm dq print_hex dq brace dq comma dq print_reg dq eol

У якості прикладу розмістимо в пам'яті секції .data наступнs інструкції. Для простоти ми неправильно оброблямо mod r/m байт, та трактуємо поле mod без виключення коли mod=11, тобто трактуємо це як відностну трьохбайтову адресу (4 та 6 інструкції).

machine: db 0x00, 0b00100101 db 0x01, 0b10010011, 0x26, 0x25 db 0x02, 0b01001110, 0x34 db 0x03, 0b11101110, 0x76, 0x75, 0x77 db 0x02, 0b10111111, 0x66, 0x65 db 0x03, 0b11011101, 0x86, 0x85, 0x88 machine.ptr: dq machine

Допоміжні символьні таблиці

rm000: db '[BX+SI' rm001: db '[BX+DI' rm010: db '[BP+SI' rm011: db '[BP+DI' rm100: db '[SI' rm101: db '[DI' rm110: db '[BP' rm111: db '[BX' rmter: db ']' com: db ',' linefeed: db 0xa rm: dq rm000, rm001, rm010, rm011, rm100, rm101, rm101, rm110, rm111 hexout: db '+0x01234567890123456789012345678901' regb: db "ALCLDLBLAHCHDHBH" regw: db "AXCXDXBXSPBPSIDI" hex: db '01234567890ABCDEF'

Синоніми для syscall операційної системи MacOS.

syscall_exit equ 0x2000001 syscall_write equ 0x2000004 syscall_open equ 0x2000005 syscall_close equ 0x2000006

Передача параметрів

Основні вимоги до функції: мінімізація використання операцій зі стеком, використовувати передачу параметрів через регістри, мінімізація звертань до шини, бампінг короткими версіями інструкцій (бажано мінімальною довжиною 2 байти), функції-мотузочки (ropes) довжиною у розмір конвеєра.

Бібліотека функцій

Rope #0. Функція обробки байту MOD-R/M

Перед початком виконання після головного діспатчу ми попадаємо в парсер mod r/m байта, де ми завантажуємо адресу поточної інструкції в rsi (джерело для дизасемблера) та адресу таблиці опкодів в rdi (ціль дизасемблювання). У цій функції ми заглядаємо у попередній байт [rsi-1] чим вносимо цей парсер до класу L(1). Попередній байт (опкод інструкці) ми зберігаємо в регістрі BL, а байт r/m в регістрі AL. Використовуючи зсув BL на 4 ми визначаємо позицію а таблиці опкодів та використовуємо її у відносній адресації [rdi+rbx] щоб визначити адресу фукнції-мотузки у яку небхідно модифікувати параметри для розпізнання на наступних кроках. В даному випадку йдеться про дві функції мотузки from_reg і to_reg, які модифікують аргументи в таблицях inst0 та inst1 або таблицях inst2 та inst3. Ці функції записують в байт-аргумент фукнці-мотузки наступні параметри: 1) для фукнкції регістру — номер регістру з байту mod r/m у молодих 4-бітах AL та розрядність інструкції у 5-му біті AL (береться з байткоду), 2) для r/m відображення — значення mod з байту яке індексує вигляд адресації у масиві rm, 3) для функції відображення відносної адреси — кількість байт які небходно прочитати з потоку інструкцій (значення поля mod). Тому кожна з фунції 7 та 8 пише в кожні з цих трох місць, по суті готує майбутні параметри для лінійного проходження. Після виконання функція збільшує адресу поточної інструкції.

parse_mod_rm: mov rsi, [machine.ptr] mov rdi, opcodes mov al, [rsi] mov ah, [rsi-1] mov bl, ah mov cl, 4 shl ah, cl shl bl, cl mov rdi, [rdi+rbx] dec cl mov bl, al mov dl, al shr bl, cl add cl, cl shr al, cl inc cl and bl, cl and dl, cl or bl, ah inc cl add cl, cl mem: test bl, cl jnz odd call from_reg jmp quit_rm odd: call to_reg quit_rm: inc qword [machine.ptr] mov rax, 2 ret

Rope #1. Функція друку операції

print_add: mov rsi, add mov dl, 4 call write mov rax, 2 ret

Rope #2. Функція друку адресації памʼяті

print_rm: and rax, 255 mov rdi, rax shl rdi, 3 mov rbx, rm mov rsi, [rbx+rdi] mov rbx, rd mov dl, [rbx+rdi] call write mov rax, 2 ret

Rope #3. Функція друку шістнадцяткового числа

print_hex: push rax cmp rax, 0 jz empty xor edx, edx mov edx, eax mov rdi, hex mov rsi, [machine.ptr] mov rbp, hexout mov eax, edx shl eax, 1 add al, 3 mov qword [hexout.len], rax dec eax add rbp, rax pair: mov bl, [rsi] and ebx, 15 mov al, [rbx+rdi] mov [rbp], al dec rbp mov bl, [rsi] shr ebx, 4 and ebx, 15 mov al, [rbx+rdi] mov [rbp], al dec rbp inc rsi dec edx jne pair mov rdx, qword [hexout.len] mov rax, syscall_write mov rsi, hexout mov rdi, 1 syscall empty: pop rdx add qword [machine.ptr], rdx mov rax, 2 ret

Rope #4. Функція друку дужки

brace: mov rsi, rmter mov dl, 1 call write mov rax, 2 ret

Rope #5. Функція друку коми

comma: mov rsi, com mov dl, 1 call write mov rax, 2 ret

Rope #6. Функція друку регістра

print_reg: test al, 16 jnz w mov rsi, regb jmp e w: mov rsi, regw e: and al, 15 shl al, 1 add rsi, rax mov dl, 2 call write mov rax, 2 ret

Rope #7. Функція-патч для регістрових вихідних операндів

from_reg: mov [rdi+7], al ; mod mov [rdi+13], bl ; reg mov [rdi+5], dl ; r/m ret

Rope #8. Функція-патч для регістрових вхідних операндів

to_reg: mov [rdi+11], al ; mod mov [rdi+5], bl ; reg mov [rdi+9], dl ; r/m ret

Головна програма

У головній програмі ми вимушені скористатися стеком для приховування регістра rdx та rdi у процесі виконання функцій мотузок. Саме в цій функції ми передаємо однобайтові паратери функціям через регістр AL, а регістр rsi в циклі завжди виставляється заново в адресу мотузок для відносної адресації по [rsi+rbx], де rbx=8*i, де i — номер функції мотузки в таблиці функцій. Після виходу з функції мотузки ми беремо то що вернулося в rax (використовуємо як вихідний результат функції), та збільшуємо на це число адресу наступної функції мотузки rdi (тобто використовуємо результат для обчислення адреси де знаходиться адреса наступної функції-мотузки). Регістр rdx використовуємо як лічильник циклу (в даному випадку ми крутимо число 7), та кожен раз декриментуємо його. Адресу display.ptr використовуємо як лінільник дизасембльованих інструкцій.

_main: mov rsi, [machine.ptr] xor rbx, rbx inc qword [machine.ptr] mov bl, [rsi] shl rbx, 4 mov rsi, opcodes mov rdx, [rsi+rbx+8] mov rdi, [rsi+rbx] line: push rdi push rdx xor rbx, rbx mov bl, [rdi] shl bl, 3 mov rsi, ropes mov al, [rdi+1] call [rsi+rbx] pop rdx pop rdi add rdi, rax shr rax, 1 sub rdx, rax jnz line call eol dec qword [display.ptr] jnz _main mov rax, 0x2000001 xor rdi, rdi syscall ret

Виконання

nasm -f macho64 -o dasm.o dasm.asm gcc -fno-pie -o dasm dasm.o be dasm

[:0x3d2f] [d]

$ ./dasm ADD [DI],AH ADD DX,[BP+DI+0x2526] ADD [DI+0x34],CL ADD BP,[DI+0x777576] ADD [BP+0x6566],BH ADD BX,[DI+0x888586]

DYI PC XT

На курсі "КВ-123-16 Схемотехніка" вивчається як будувати XT комп`ютери та збирати їх самому, на прикладі платформи PC/104. Тут показана повна елементна база для побудови ХТ комп`ютера для максимального та мінімального режимів.

ЧіпОпис
121421024 x 4-bit SRAM
227648K EPROM
362648K SRAM
4808616-bit bus CPU
5808716-bit bus MPU
680888-bit bus CPU
7808916-bit bus MPU
88155256 x 8-bit SRAM, I/O, Timer
982128-bit I/O port
A8214PICU
A8254PIC 8086
B8257DMA
C82828-bit latch
D8284Clock generator
E8286Transeiver
F8288Bus controller
G8289Bus arbiter
H74LS244Octal buffer
I74LS245Bi-directional octal buffer
J74LS3738-bit latch

Приклад компоновки процесору 8088 у мінімальному режимі

Код

Репозиторій проекту github.com/5HT/xt-dasm.

Рекомендована література

[1]. Rector, Alexy. 8086 Book. Berkley. 1980.
[2]. Brey. The Intel Microprocessors. 2009.
[3]. Mathur. 8086: Architecture, Programming and Interfacing. Delhi. 2011.
[4]. Intel Mnemonics. 1980.

Альтернативний практикум на асемблері

Розробка дизасемблера ARM або RISC-V на вибір.
Розробка LISP інтерпретатора.

Наступні кроки

Імітаційне моделювання (розробка емулятора) та математичне моделювання (доведення коректності бінарного коду мікропроцесора). Нас цікавитимуть такі моделі, які враховують мультиагентний доступ до шини та операції LOCK.


˙