Uploaded by mrngrushetskyi07

Грушецький Н.М. КІ-37

advertisement
Міністерство освіти і науки
Національний університет «Львівська політехніка»
Пояснювальна записка
до курсової роботи
з дисципліни: “Архітектура комп’ютерів”
на тему: “Проектування комп’ютера”
Виконав:
ст.гр. КІ-37
Грушецький Н.М.
Перевірив:
Грицик І.В.
Львів
2021
1
Анотація
Курсова робота складається з трьох частин: перша частина являє собою
розробку програми, яка перетворює вхідну програму на мові асемблер в мову
машинних кодів. В цій частині проводиться аналіз всіх команд, які потрібно
реалізувати, а також розробка тестів для тестування даної програми.
В другій частині здійснюється розробка симулятора, який може
відсимулювати
машинний код, а також розробка тестів для тестування
розробленої програми.
Третє частина являє собою розробку асемблерної програми для
перевірки коректності роботи моделі спрощеного комп’ютера. Програма має
бути ефективною:
- не перевищувати 50 рядків;
- і не має виходити за 1000 інструкцій для будь-яких вхідних даних.
2
Зміст
Анотація ........................................................................................................................................ 2
Вихідні дані на проектування ................................................................................................... 4
1. Аналітичний розділ ........................................................................................................ 7
1.1. Система команд........................................................................................ 11
1.2. Способи адресації ..................................................................................... 12
2. ПРОЕКТНИЙ РОЗДІЛ ................................................................................................ 14
2.1. Алгоритми роботи розробленого симулятора і асемблера ..................... 14
2.2.Функціональна схема комп’ютера до модифікації .................................. 20
2.3. Функціональна схема комп’ютера після модифікації ............................. 22
2.4. Множина інструкцій комп’ютера ............................................................ 23
2.5. Опис форматів інструкцій розробленого комп’ютера .......................... 25
2.6. Опис виконання кожного типу інструкцій в по тактовому режимі ... 27
3. Тестовий розділ ...................................................................................................................... 29
Використана література ........................................................................................................... 37
Висновок...................................................................................................................................... 38
Додатки ........................................................................................................................................ 39
3
Вихідні дані на проектування
Перелік завдань:
1.
8 додаткових інструкцій без використання регістрів стану:
3 – арифметичні
3 – логічні
2 – керування
2.
3 додаткові інструкції з використання регістрів стану.
3.
Передбачити на власний вибір 3 інструкцій (з розроблених в п. 1, 2), які
підтримують додатковий тип адресації.
Таблиця 1. Варіант №11.
Варіант 11
Арифметичні
1
11
1.
4
2
3
6
3
10
Логічні
4
5
6
4
8
10
Керуван
ня
Прапорці
7
1
1
8
Адресація
№
Розряд
№ ність
2 3
17 CF 2
3
4
3
Визначити формати команд згідно з розрядністю шини даних, розміру
пам’яті та регістрового файлу.
Таблиця 2. Розрядність.
№
4
2.
Розрядність шини
Розмір пам’яті
Розмір регістрового
даних
Байт
файлу(к-сть регістрів)
40
1048576
32
Реалізація додаткових команд. Необхідно реалізувати 8 додаткових
команд. Серед них 3 арифметичні, 3 логічні та 2 команди керування згідно з
варіантом. Команди не мають повторюватися.
4
Таблиця 3. Арифметичні команди.
№
Мнемонічнй код
Зміст
3
DIV regA regB destReg
Беззнакове ділення destReg=regA/regB
6
XADD regA regB destReg
Додати і обміняти операнди місцями destReg=regA+regB
regA<=>regB
10 XIMUL
destReg
regA
regB Знакове множення і обмін операндів місцями
destReg=regA*regB regA<=>regB
Таблиця 4. Логічні команди.
№
Мнемонічнй код
Зміст
4
SHR regA regB destReg
8
ROR regA regB destReg Циклічний зсув вправо destReg=regA << regB
10
OR regA regB destReg
Логічний зсув вправо destReg=regA >> regB
Логічне побітове АБО destReg=regA | regB
Таблиця 5. Команди керування.
№
1
Мнемонічнй код
JMA regA regB offSet
17 JMNLE
offSet
3.
regA
Зміст
Беззнакове більше if (regA> regB) PC=PC+1+offSet
regB Знакове не менше/рівно
PC=PC+1+offSet
if
(regA!<=
regB)
Реалізувати додатковий спосіб адресації. Передбачити, що 3 інструкції
підтримують інший вид адресації згідно з варіантом. Визначення операндів,
які підтримують інший спосіб адресації узгодити з викладачем. (крім
безадресної)
Примітка: безадресний варіант передбачає створення стеку та реалізацію
2 додаткових команд наведених в таблиці.
5
Таблиця 6. Додаткова адресація.
№
Адресація
3
Непряма
4.
Регістри стану: CF –регістр переносу, SF – регістр знаку, ZF – регістр 0.
Таблиця 7. Регістр переносу CF.
№
Мнемонічнй код
Зміст
2
SBB regA regB destReg
Віднімання з переносом: destReg=regA-regB-СF
3
BT regA regB
Копіює значення біта з regA по позиції regB в CF
= regA[regB]
4
CMP regA regB
Порівняти regA regB і встановити прапорці
СF SF ZF
regA < regB 1 1 0
regA = regB 0 0 1
regA > regB 0 0 0
6
1. Аналітичний розділ
CISC (англ. Complex Instruction Set Computer — комп'ютер зі складним
набором команд) — це архітектура системи команд, в якій більшість команд є
комплексними,
тобто
реалізують
певний
набір
простіших
інструкцій процесора або шляхом зіставлення з кожною CISC-командою
певної мікропрограми, або принаймні можуть бути зведені до набору таких
простих інструкцій. Крім того, ознаками CISC-архітектури можна вважати
також наявність великої кількості методів адресації пам'яті з можливістю
безпосередньої роботи з операндами в основній пам'яті комп'ютера. Тобто,
CISC-архітектури відносяться, як правило, до класу двохадресних.
Архітектури з комплексними наборами команд, розвиток яких припав на
кінець 60-х — 70-ті роки пропонували програмісту досить різноманітний набір
порівняно високорівневих інструкцій машинної мови, таких, наприклад, як
«виклик підпрограми» або «відняти одиницю та перейти, якщо результат
ненульовий», а також велику кількість способів звертання до операндів в
пам'яті для полегшення роботи зі складними структурами даних. У ті часи, за
відсутності
повноцінних мов
відповідних компіляторів,
інструментарію
така
програмування
програмування
апаратна
могла
високого
підтримка
рівня
та
високорівневого
підвищити продуктивність
праці програміста. До того ж, програма, складена з таких команд займала
небагато в пам'яті комп'ютера.
Типовими прикладами CISC-архітектур були системи VAX, PDP-
11, IBM System/360, сімейства мікропроцесорів Motorola 68000 та Intel x86.
Але з появою високорівневих мов та оптимізуючих компіляторів, розвитком
електроніки, який спричинив здешевлення комп'ютерної пам'яті виявилось,
що використання високорівневих машинних команд суттєво обмежує
можливості до оптимізації програми, підвищення її швидкодії.
Зокрема, складні команди потребували багато часу на процедуру
декодування, потребували багато апаратурних ресурсів для реалізації, що
негативно відображалось на загальній швидкодії та складності системи. Далі,
7
наявність спеціальних команд, таких як «виклик підпрограми» не завжди
виправдовувала себе, в багатьох випадках доцільніше було б замість такої
загальної команди використати набір елементарніших інструкцій, які в
результаті спричиняли б виконання меншої кількості обчислень процесором.
Це стосувалось і обчислювальних команд, які підтримували роботу з
операндами в повільній пам'яті, що далеко не завжди давало оптимальний
результат. Набагато ефективніше було б відокремити процедури роботи з
пам'яттю (завантаження та збереження операндів) від проведення обчислень,
що дало б можливість оптимізувати процедури звертання до запам'ятовуючого
пристрою. Зрозуміло, що ці оптимізації повинні були виконуватись вже
компіляторами з мов високого рівня, які до того часу досягли досить високого
рівня функціональності.
Ці та інші проблеми CISC-архітектур призвели до створення в 80-ті
роки RISC-архітектур (від англ. Reduced Instruction Set Computer — комп'ютер
зі скороченим набором команд), які вибудовують прямо протилежну модель
системи команд з максимальним спрощенням семантики машинної команди,
зведенням її до елементарної, мінімізацією методів адресації пам'яті тощо та
принциповою орієнтацією на мови високого рівня й оптимізуючі компілятори
з них, аніж на програмування безпосередньо в машинних мовах.
Сучасні CISC-архітектури, такі як останні втілення сімейства процесорів
x86, хоч і відповідають CISC-концепції на рівні архітектури системи команд,
але всередині процесора реалізують якраз пристосованішу до сьогоднішніх
реалій RISC-модель, трансформуючи потік CISC-команд в процесі виконання
в набори з простіших RISC-мікрооперацій, які й виконуються процесором.
Для виконання задачі на комп’ютері необхідно:
 забезпечити вибірку команди програми із його пам’яті в заданій
послідовності, організувати звернення до неї за відповідними
адресами;
 забезпечити розпізнавання типів виконуваних операцій;
8
 організувати звернення до пам’яті за відповідними адресами для
вибірки необхідних для виконання кожної команди даних;
 організувати виконання над даними операцій відповідно до вказівок
команд;
 запам’ятати результат обчислень.
Комп'ютер виконує кожну команду як послідовність простих операцій:
1. Вибірка чергової команди із основної пам'яті.
2. Визначення типу вибраної команди, тобто її дешифрування.
3. Визначення адрес даних, необхідних для виконання цієї команди.
4. Виконання операцій пересилання даних (зчитування даних із пам'яті в
регістри процесора).
5. Виконання операції відповідно до її коду в полі коду операції команди.
6. Визначення адрес, за якими запам'ятовуються результати.
7. Запам'ятовування результатів.
8. Підготовка до виконання наступної команди, тобто обчислення її
адреси.
Для процесора комп'ютера із складною системою команд характерні
наступні особливості:
 виконання команди за багато тактів, оскільки для цього потрібно
здійснити багаторазові операції звернення до основної пам'яті та до
програмно-доступних регістрів процесора;
 орієнтація АЛП на виконання великої кількості операцій, що пов'язано
з розширеним складом системи команд;
 складна система розпізнавання команди, що пов'язано з великою
кількістю методів адресації та великою кількістю форматів команд
різної розрядності;
 програмне дешифрування команд з метою зменшення затрат
обладнання;
9
 складна організація конвеєризації виконання команд, що пов'язано, в
першу чергу, з різнотипністю їх виконання;
 орієнтація структури на виконання команд типу регістр-пам'ять та
пам'ять-пам'ять.
Основні елементи процесора - арифметико-логічний пристрій, пристрій
керування і регістрова пам'ять або, як її ще називають, надоперативний
запам'ятовуючий пристрій. До складу регістрової пам'яті, в свою чергу,
входять наступні вузли - програмний лічильник, регістри: адреси, команди,
даних, слова стану програми, а також регістровий файл, який складається з
програмно доступних регістрів.
Структура регістрової (надоперативної) пам'яті процесора складається з
регістрів спеціального та зального призначення. До регістрів спеціального
призначення належать:
 регістри адреси (РгА);
 регістри команд (РгК);
 програмний лічильник(ПЛ)
 регістри даних (РгД).
РгА зберігає адресу даного або команди при зверненні до основної
пам'яті. РгД зберігає операнд при його запису або зчитуванні з основної
пам'яті. В ролі операнда може бути дане, команда або адреса. РгК зберігає
команду після її зчитування з основної пам'яті. ПЛ підраховує команди та
зберігає адресу поточної команди. Комп'ютер з архітектурою Джона фон
Неймана має один програмний лічильник.
10
1.1. Система команд
Різноманітність типів даних, форм представлення та опрацювання,
необхідні дії для обробки та керування ходом виконання обчислень
призводить до необхідності використання різноманітних команд – набора
команд.
Кожен процесор має власний набір команд, який називається системою
команд процесора.
Система команд характеризується трьома аспектами:

формат,

способи адресації,

система операцій.
Форматом команди – є довжина команди, кількість, розмір, положення,
призначення та спосіб кодування полів. Команди мають включати наступні
види інформації:

тип операції, яку необхідно реалізувати в даній команді
(поле команду операції - КОП);

місце в пам’яті звідки треба взяти перший операнд (А1);

місце в пам’яті звідки треба взяти другий операнд (А2);

місце в пам’яті куди треба помістити результат (А3).
Кожному з цих видів інформації відповідає своя частина двійкового
слова – поле. Реальна система команд зазвичай має команди декількох
форматів, тип формату визначає КОП.
Команда в комп'ютері зберігається в двійковій формі. Вона вказує тип
операції, яка має бути виконаною, адреси операндів, над якими виконується
операція, та адреси розміщення результатів виконання операції. Відповідно до
цього команда складається з двох частин, коду операції та адресної частини.
11
1.2. Способи адресації
Варіанти інтерпретації бітів (розрядів) поля адреси з метою знаходження
операнда називаються способами адресації. Коли команда вказує на операнд,
він може знаходитись в самій команді, в основній або зовнішній пам'яті чи в
регістровій пам'яті процесора. За роки існування комп'ютерів була створена
своєрідна технологія адресації, яка передбачає реалізацію різних способів
адресації, чому послужило ряд причин:

забезпечення ефективного використання розрядної сітки команди;

забезпечення ефективної апаратної підтримки роботи з масивами
даних;

забезпечення задання параметрів операндів;

можливість генерації великих адрес на основі малих.
Існує велика кількість способів адресації. Розглянемо п’ять основних
способів адресації операндів в командах.
Пряма – в цьому випадку адресне поле зберігає адресу операнда. Її
різновидом є пряма регістрова адресація, яка адресує не комірку пам’яті а
номер регістру.
Безпосередня – в поле адреси команди поміщається не адреса, а сам
операнд.
Непряма – в полі адреси команди зберігається адреса комірки пам’яті в
якій знаходиться адреса операнда. Такій спосіб дозволяє оперувати з адресами
як з даними. Різновид непряма-регістрова адресація, адреса адреси
зберігається в регістрі загального призначення.
Відносна – адреса формується, як сума з двох доданків: бази, яка
зберігається в спеціальному регістрі чи в одному з регістрів спеціального
призначення, та зміщення, яке задається в полі адреси команди. Різновид
індексна та базова індексна. При індексній замість базового регістра є
індексний, який автоматично модифікується (зазвичай збільшується на 1).
Базова-індексна адресація формується адреса як сума трьох доданків: бази,
індексу та зміщення.
12
Безадресна – поле адреси в команді відсутнє. Адреса операнда, або
немає змісту або є по замовчуванню (наприклад дії на спеціальним регістром
- акумулятором). Безадресні команди неможливо використати для інших
регістрів чи комірок пам’яті. Одним з різновидів безадресної адресації є
використання стеку.
В команду вводяться спеціальні ознаки з тим, щоб пристрій керування міг
розпізнати використаний спосіб. Це можуть бути додаткові розряди в команді,
або для різних типів команд закріплюватись різні способи адресації.
13
2. ПРОЕКТНИЙ РОЗДІЛ
2.1. Алгоритми роботи розробленого симулятора і асемблера
Перш ніж почати роботу над цією програмою, потрібно визначитись з
синтаксисом асемблерної програми, яка буде використовуватись як вхідні дані
програми – перетворювача.
Отже, формат лінійки асемблерного коду буде наступний (<пробіл>
означає послідовність табуляцій і/або пробілів):
мітка <пробіл> інструкція<пробіл>поле№1<пробіл> поле№2<пробіл>
поле№3<пробіл>коментар
Де, поле№1-№3 – номер регістру або адреса (символьна або числова) в
залежності від команди.
Алгоритм перетворення асемблерних команд у машинні інструкції
передбачає 2 «проходи» по вхідній текстовій (асемблерній) програмі. У
процесі першого проходу програма перевіряє коректність синтаксису команд
(Перевіряє кожну вхідну команду на наявність її у множині команд
комп’ютера (Простіше кажучи, перевіряє, чи ця команда відома комп’ютеру),
перевіряє відповідність кількості операндів вхідної команди та задекларованої
кількості операндів для цієї команди, правильність зазначених у команді
операндів (Операдом можуть бути: регістр, символьне зміщення (мітка),
числове зміщення).
14
Відкриття
файлу з
текстом
програми
Program.as
readAndParse
label
opcode
arg0
arg1
arg2
testRegArg
testAddrArg
Рис. 2.1.1. Схема першого проходу асемблера.
Примітка до рисунку 2.1.1: блок readAndParse зчитує з вхідного
файлу 1 рядок і отримує з нього значення таких полів: мітка, код операції,
операнд 1 (якщо операція цього потребує), операнд 2 (якщо операція цього
потребує) та операнд 3 (якщо операція цього потребує). На цьому етапі
перевіряється наявність цієї команди у комп’ютері, кількість та тип операндів.
В залежності від команди, операнд (операнди) перевіряються на коректність
(номеру регістра (для команд, які працюють з регістрами), символьної або
числової адреси (для команд завантаження, зберігання даних, команд
керування і т.д., де одним з операндів є адреса у пам’яті))).
Блок testRegArg перевіряє правильність номеру регістру. На схемі, до
цього блоку підведені всі 3 регістри.
Це було обумовлено можливістю
використання команд, в яких всі 3 операнди - регістри. (ADD reg1 reg2 reg3,
DIV reg1 reg2 reg3, NAND reg1 reg2 reg3 і т.д.)
testAddrArg перевіряє коректність вказання адреси (символьної або
числової). На схемі, до цього блоку підведені тільки перший та третій
операнди. Це обумовлено специфікацією команд розробленого комп’ютера.
Тобто, у наборі інструкцій мого комп’ютера немає такої інструкції, у якій
другим операндом буде адреса. Перший операнд, як адресу, може
15
використовувати тільки одна інструкція - .fill (збереження у мітці числа або
адреси). Приклад цієї операції:
one .fill 1 – Цей приклад «покладе» за адресою, де знаходиться ця
команда одиницю. Доступитись до цієї адреси можна буде за допомогою мітки
one. Грубо кажучи, виконалась операція one = 1;
one .fill start – У цьому випадку в адресі, де знаходиться ця команда
збережеться адреса мітки start.
З
третім
операндом,
як
адресою,
працюють
операції
завантаження\збереження даних, операції керування (lw,sw,jma,jmnl,beq)
Program.as у схемі – це текстовий файл з програмою.
У процесі другого проходу рядку програми виконується генерування
відповідних машинних команд, тобто числового еквіваленту асемблерним
командам. Зазираючи глибше у цей процес, можна сказати, що незалежно від
операції, її код зсувається вліво на 24 розряди, цим самим, лишаючи за кодом
24 розряди, які використовуються для операндів (якщо операція їх потребує),
4 розряди для першого операнду, 4 – для другого і 16 – для третього (регістр
або адреса)).
16
Рис. 2.1.2. Схема другого проходу асемблера.
Блок isNumber перевіряє, чи є операнд числом. Якщо так, то програма
записує це число у відповідній позиції адресного поля команди. Якщо ні, то
переходить до блоку translateSymbol
translateSymbol – Підставляє замість символьної адреси (мітки)
числову. Числова адреса береться з, сформованої на першому проході,
своєрідної таблиці міток, де вказані адреси для кожної мітки. Після виконання
переходить до блоку формування машинного коду.
Утворення машинного коду – Формує машинний код з коду операції
та операндів. На виході цього блоку утворюється 2 файли:
Program.mc – файл з послідовністю машинних інструкцій, який буде
читатися програмою – симулятором.
Ця програма покликана виконати послідовність машинних команд, які
були сформовані програмою – перетворювачем . По суті, це виконуюча
частина мого «Спрощеного комп’ютера».
17
Рис. 2.1.3. Функціональна схема симулятора
Симулятор починає свою роботу ініціалізацією пам’яті та регістрів
нульовими значеннями. Наступним кроком відбувається завантаження
програми у машинних кодах у пам’ять з текстового файлу. Далі відбувається
покрокове виконання програми (один крок – одна команда) та вивід стану у
файл. (під виводом стану мається на увазі вивід вмістимого всіх задіяних
комірок пам’яті та регістрів, вмістиме прапорців SF,CF,ZF, вмістиме ПЛ та
мнемонічний запис останньої виконаної команди).
У блоці stateStruct зберігається стан машини – значення регістрів,
пам’яті, програмного лічильника та прапорця (CF).
18
Run – виконує вибірку інструкції з пам’яті, декодує інструкцію та
виконує її. На етапі декодування викликається блок convertNum, який
перетворює 20-ти розрядне число у число int, прийняте у «Сі-подібних» мовах
як 40 розрядне. Також, у кінці кожного run циклу викликається блок printState,
який «звітує» про пророблену роботу. (виводить стан машини, про це було
зазначено вище).
Додатковим завдання було - розробити додатковий спосіб адресації
та передбачити, що 3 інструкції його підтримують.
Адресація згідно з варіантом – непряма.
Непряма адресація – в полі адреси команди зберігається адреса
комірки пам’яті в якій знаходиться адреса операнда. Такій спосіб дозволяє
оперувати з адресами як з даними. Різновид непряма-регістрова адресація,
адреса адреси зберігається в регістрі загального призначення.
19
2.2.Функціональна схема комп’ютера до модифікації
В спрощеному комп’ютері (СК) в пам’яті зберігаються, як дані так і
інструкції. Кожна інструкція закодована числом. Це число складається з
декількох полів: поле назви команди чи код операції (КОП) та полів операндів.
В СК є два види пам’яті: загальна пам’ять, та регістрова пам’ять. В загальній
пам’яті зберігаються інструкції програми та дані над якими оперують
інструкції. В регістровий пам’яті зберігаються дані над якими виконуються
інструкції. У реальних комп’ютерах регістрова пам’ять є малою за розмірами
та швидкою, працює на швидкості ядра процесора, загальна пам’ять є великою
за розміром, але набагато повільніша за ядро процесора. Регістрова пам’ять
підтримує лише пряму адресацію, загальна пам’ять підтримує декілька типів
адресації. У СК є 8 регістрів по 32 розряди, пам’ять складається з 65536 слів
по 32 розряди. Отже СК є 32 розрядним комп’ютером. Він підтримує 8
інструкцій. У СК є спеціальний регістр – лічильник команд (programCounter),
в якому зберігається адреса інструкції. За прийнятою домовленістю нульовий
регістр завжди містить 0.
Рис. 2.2.1. Функціональна схема спрощеного комп’ютера до модифікації
20
В регістрі instReg міститься код інструкції, яка виконується. При роботі
з пам’яттю (MEMORY) використовуються два регістри – в memAddress
зберігається адреса пам’яті, а в memData значення, яке читається/записується
в пам’ять.
Комп’ютер містить арифметико-логічний пристрій (ALU), який може
виконувати 3 дії: додавання, логічне І-НЕ і перевірку на рівність. ALU
керується двохрозрядним сигналом alu_op:
alu_op
Дія
00
+
01
І-НЕ
10
==
ALU працює з двома операндами, один з них завжди є в регістрі
aluOperand, інший береться з головної шини (main bus), результат зберігається
в регістрі aluResult.
21
2.3. Функціональна схема комп’ютера після модифікації
Після модифікації відбулись зміни :
1.
Шина даних залишилась такою ж
2.
Додано 8 додаткових операцій:

3 арифметичні операції (XADD, DIV, XIMUL);

3 логічні операції (SHR, ROR, OR);

2 операції керування (JMA, JMNLE).
3.
Було додано прапорець CF – (Cery flag), а також команди BT, CMP,
SBB, які працюють в парі з прапорцем.
4.
Пам’ять зросла до 1048576.
5.
Збільшилось число регістрів (з 8 до 32).
Отже , СК є 40 розрядним комп’ютером. У СК є 32 регістрів по 40
розрядів. Пам’ять складається з 1048576 слів по 40 розрядів. Він підтримує 24
інструкцій, кожна з яких буде розписана нижче.
За прийнятою домовленістю нульовий регістр завжди містить 0 (це не
обмовлено апаратними вимогами проте асемблерна програма ніколи не має
змінювати значення нльового регістра, який ініціалізуються 0 ).
22
2.4. Множина інструкцій комп’ютера
№ Код
2СУТНІСТЬ ІНСТРУКЦІЇ
інструкції
кове
R-тип
1
add regA regB destReg
00000 Додає вміст регістру regA до вмісту regB, та
зберігає в destReg
2
nand regA regB destReg
00001 Виконує логічне побітове І-НЕ вмісту regA з
вмістом regB, та зберігає в destReg
3
div regA regB destReg
00010 Беззнакове ділення destReg=regA/regB
4
xadd regA regB destReg
00011 Додавання і обмін операндів місцями
5
shr regA regB destReg
00100 Логічний зсув вправо
6
or regA regB destReg
00101 Логічне побітове АБО
7
ror regA regB destReg
00110 Циклічний зсув вправо
8
sbb regA regB destReg
00111 Віднімання з переносом: destReg=regA-regBСF
9
XIMUL
regA
destReg
regB 01000 Знакове множення і обмін операндів місцями
destReg=regA*regB
I-тип
10
lw regA regB offSet
01001 Завантажує regB з пам’яті. Адреса пам’яті
формується додаванням offSet до вмісту
regA.
11
sw regA regB offSet
01010 Зберігає вміст регістру regB в пам’ять.
Адреса
пам’яті
формується
додаванням
offSet до вмісту regA.
12
beq regA regB offSet
01011 Якщо вміст регістрів regA та regB однаковий,
виконується перехід на адресу програмний
лічильник(ПЛ) + 1+ offSet, в ПЛ адреса
поточної, тобто beq інструкції.
23
13
jma regA regB offSet
01100 Беззнакове
більше
if
(regA>
regB)
PC=PC+1+offSet
14
jmnle regA regB offset
01101 Знакове не менше/рівно if (regA!<= regB)
PC=PC+1+offSet
J-тип
15
jalr regA regB
01110 Спочатку зберігає ПЛ+1 в regB, в ПЛ адреса
поточної (jalr) інструкції. Виконує перехід на
адресу, яка зберігається в regA. Якщо в якості
regA regB задано один і той самий регістр, то
спочатку в цей регістр запишеться ПЛ+1, а
потім виконається перехід до ПЛ+1.
16
bt regA regB
01111 Копіює значення біта з regA по позиції regB в
CF=regA[regB]
17
cmp regA regB
10000 Порівняти regA regB і встановити прапорець
CF=1 якщо regA<regB
O-тип
18
halt
10011 Збільшує значення ПЛ на 1, потім припиняє
виконання, стимулятор має повідомляти, що
виконано зупинку.
24
2.5. Опис форматів інструкцій розробленого комп’ютера
Інструкції R-типу (add, nand, div, xadd, shr, or, ror, sbb, ximul):
біти 34-30: код операції;
біти 29-25: reg A;
біти 24-20: reg B;
біти 19-5: не використовуються;
біти 4-0: destReg.
34-30
29-25
24-20
19-5
4-0
opcode
regA
regB
unused
destReg
Рис.2.5.1. Інструкція R-типу після модифікації.
I-тип інструкцій (lw, sw, beq, jma , jmnle):
біти 34-30: код операції;
біти 29-25: reg A;
біти 24-20: reg B;
біти 19-0: зміщення (20 біт, значення від -524288 до 524287).
34-30
29-25
24-20
19-0
opcode
regA
regB
Offset
Рис.2.5.2. Інструкція I-типу після модифікації.
J-тип інструкцій (jalr, bt, cmp):
біти 34-30: код операції;
біти 29-25: reg A;
біти 24-20: reg B;
біти 19-0: не використовуються;
34-30
29-25
24-20
19-0
opcode
regA
regB
Unused
Рис.2.5.3. Інструкція J-типу після модифікації.
25
O-тип (halt):
біти 34-30: код операції;
біти 29-0: не використовуються.
34-30
29-0
opcode
Unused
Рис.2.5.4. Інструкція O-типу після модифікації.
CF – використовується в командах CMP, BT, SBB.
26
2.6. Опис виконання кожного типу інструкцій в потактовому режимі.
Інструкція DIV ;SHR; OR; ROR; regA regB destReg
1.
2.
3.
4.
5.
6.
7.
memAddress <= programCounter
memDate <= MEMORY[memAdress]
programCounter++
instReg <= memDate
aluOperand <= RegisterFile[29-25]
aluResult <= aluOperand, operation, RegisterFile[24-20]
RegisterFile[0-4] <= aluResult
Інструкція XADD; XIMUL regA regB destReg
1.
2.
3.
4.
5.
6.
7.
8.
9.
memAddress <= programCounter
memDate <= MEMORY[memAdress]
programCounter++
instReg <= memDate
aluOperand <= RegisterFile[29-25]
aluResult <= aluOperand, operation, RegisterFile[24-20]
RegisterFile[29-25] <= RegisterFile[24-20]
RegisterFile[24-20] <= aluOperand
RegisterFile[0-4] <= aluResult
Інструкція JMA; JMNLE RegA RegB offset
1. memAddress <= programCounter
2. memDate <= MEMORY[memAdress]
3. programCounter++
4. instReg <= memDate
5. aluOperand <= RegisterFile[29-25]
6. aluResult <= aluOperand, operation, RegisterFile[24-20]
7. aluOperand <= aluResult
8. aluResult <= aluResult, operation, RegisterFile[19-0]
9. aluOperand <= aluResult
10.aluResult <= programCounter, operation, aluOperand
11.programCounter <= aluResult
27
Інструкція BT regA regB
1. memAddress <= programCounter
2. memDate <= MEMORY[memAdress]
3. programCounter++
4. instReg <= memDate
5. aluOperand <= RegisterFile[23-20]
6. CF = aluOperand[RegisterFile[19-16]
28
3. Тестовий розділ
В даному розділі розроблено тестові програми для перевірки
правильності роботи створеного проекту.
Арифметичні операції
lw
0
lw
0
start
div
1
xadd
1
ximul 1
end
halt
a .fill 10
b .fill 2
1
2
2
2
2
a
b
3
4
5
Результат:
1. Виконується перша команда div (ділення).
@@@
state:
pc 3
memory:
mem[ 0 ] 2148532230
mem[ 1 ] 2149580807
mem[ 2 ] 9699328003
mem[ 3 ] 8625586180
mem[ 4 ] 10773069829
mem[ 5 ] 6442450944
mem[ 6 ] 10
mem[ 7 ] 2
registers:
reg[ 0 ] 0
reg[ 1 ] 10
reg[ 2 ] 2
reg[ 3 ] 5
2. Виконується друга команда xadd (додавання і обмін регістрами).
@@@
state:
pc 4
memory:
mem[ 0 ] 2148532230
mem[ 1 ] 2149580807
mem[ 2 ] 9699328003
29
mem[ 3 ] 8625586180
mem[ 4 ] 10773069829
mem[ 5 ] 6442450944
mem[ 6 ] 10
mem[ 7 ] 2
registers:
reg[ 0 ] 0
reg[ 1 ] 2
reg[ 2 ] 10
reg[ 3 ] 5
reg[ 4 ] 12
3. Виконується третя команда ximul (множення і обмін регістрами).
@@@
state:
pc 5
memory:
mem[ 0 ] 2148532230
mem[ 1 ] 2149580807
mem[ 2 ] 9699328003
mem[ 3 ] 8625586180
mem[ 4 ] 10773069829
mem[ 5 ] 6442450944
mem[ 6 ] 10
mem[ 7 ] 2
registers:
reg[ 0 ] 0
reg[ 1 ] 10
reg[ 2 ] 2
reg[ 3 ] 5
reg[ 4 ] 12
reg[ 5 ] 20
4. Кінцевий стан.
machine halted
@@@
total of 6 instructions executed
state:
final state of machine:
pc 6
memory:
30
mem[ 0 ] 2148532230
reg[ 13 ] 0
mem[ 1 ] 2149580807
reg[ 14 ] 0
mem[ 2 ] 9699328003
reg[ 15 ] 0
mem[ 3 ] 8625586180
reg[ 16 ] 0
mem[ 4 ] 10773069829
reg[ 17 ] 0
mem[ 5 ] 6442450944
reg[ 18 ] 0
mem[ 6 ] 10
reg[ 19 ] 0
mem[ 7 ] 2
reg[ 20 ] 0
registers:
reg[ 21 ] 0
reg[ 0 ] 0
reg[ 22 ] 0
reg[ 1 ] 10
reg[ 23 ] 0
reg[ 2 ] 2
reg[ 24 ] 0
reg[ 3 ] 5
reg[ 25 ] 0
reg[ 4 ] 12
reg[ 26 ] 0
reg[ 5 ] 20
reg[ 27 ] 0
reg[ 6 ] 0
reg[ 28 ] 0
reg[ 7 ] 0
reg[ 29 ] 0
reg[ 8 ] 0
reg[ 30 ] 0
reg[ 9 ] 0
reg[ 31 ] 0
reg[ 10 ] 0
CF = 0
reg[ 11 ] 0
end state
reg[ 12 ] 0
Логічні операції
lw 0
lw 0
start shr
1
or
1
ror
1
end
halt
a .fill 7
b .fill 2
1
2
2
2
2
a
b
4
5
6
Результат:
У регістрах 4,5,6 зберігається результат виконання команд shr, or та ror
відповідно.
final state of machine:
mem[ 0 ] 2148532230
@@@
mem[ 1 ] 2149580807
state:
mem[ 2 ] 12920553476
pc 6
mem[ 3 ] 11846811653
memory:
mem[ 4 ] 13994295302
31
mem[ 5 ] 6442450944
reg[ 15 ] 0
mem[ 6 ] 7
reg[ 16 ] 0
mem[ 7 ] 2
reg[ 17 ] 0
registers:
reg[ 18 ] 0
reg[ 0 ] 0
reg[ 19 ] 0
reg[ 1 ] 7
reg[ 20 ] 0
reg[ 2 ] 2
reg[ 21 ] 0
reg[ 3 ] 0
reg[ 22 ] 0
reg[ 4 ] 1
reg[ 23 ] 0
reg[ 5 ] 7
reg[ 24 ] 0
reg[ 6 ] -1073741823
reg[ 25 ] 0
reg[ 7 ] 0
reg[ 26 ] 0
reg[ 8 ] 0
reg[ 27 ] 0
reg[ 9 ] 0
reg[ 28 ] 0
reg[ 10 ] 0
reg[ 29 ] 0
reg[ 11 ] 0
reg[ 30 ] 0
reg[ 12 ] 0
reg[ 31 ] 0
reg[ 13 ] 0
CF = 0
reg[ 14 ] 0
end state
Операції керування
lw
lw
start jma
add
add
add
add
jmnle
add
add
add
add
end
halt
a .fill 10
b .fill 5
0
0
1
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
2
a
b
2
4
5
6
7
2
8
9
10
11
Результат:
final state of machine:
memory:
@@@
mem[ 0 ] 2148532237
state:
mem[ 1 ] 2149580814
pc 13
mem[ 2 ] 15068037122
32
mem[ 3 ] 35651588
reg[ 11 ] 15
mem[ 4 ] 35651589
reg[ 12 ] 0
mem[ 5 ] 35651590
reg[ 13 ] 0
mem[ 6 ] 35651591
reg[ 14 ] 0
mem[ 7 ] 16141778946
reg[ 15 ] 0
mem[ 8 ] 35651592
reg[ 16 ] 0
mem[ 9 ] 35651593
reg[ 17 ] 0
mem[ 10 ]35651594
reg[ 18 ] 0
mem[ 11 ]35651595
reg[ 19 ] 0
mem[ 12 ] 6442450944
reg[ 20 ] 0
mem[ 13 ] 10
reg[ 21 ] 0
mem[ 14 ] 5
reg[ 22 ] 0
registers:
reg[ 23 ] 0
reg[ 0 ] 0
reg[ 24 ] 0
reg[ 1 ] 10
reg[ 25 ] 0
reg[ 2 ] 5
reg[ 26 ] 0
reg[ 3 ] 0
reg[ 27 ] 0
reg[ 4 ] 0
reg[ 28 ] 0
reg[ 5 ] 0
reg[ 29 ] 0
reg[ 6 ] 15
reg[ 30 ] 0
reg[ 7 ] 15
reg[ 31 ] 0
reg[ 8 ] 0
CF = 0
reg[ 9 ] 0
end state
reg[ 10 ] 15
Операції, які працюють з прапорцями
start
lw 0
lw 0
bt
cmp
sbb
1
2
1
1
1
a
b
1
2
2
3
end
halt
a .fill 4
b .fill 9
Результат:
final state of machine:
pc 6
@@@
memory:
state:
mem[ 0 ] 2148532230
33
mem[ 1 ] 2149580807
reg[ 13 ] 0
mem[ 2 ] 17214472192
reg[ 14 ] 0
mem[ 3 ] 18289262592
reg[ 15 ] 0
mem[ 4 ] 19363004419
reg[ 16 ] 0
mem[ 5 ] 6442450944
reg[ 17 ] 0
mem[ 6 ] 4
reg[ 18 ] 0
mem[ 7 ] 9
reg[ 19 ] 0
registers:
reg[ 20 ] 0
reg[ 0 ] 0
reg[ 21 ] 0
reg[ 1 ] 4
reg[ 22 ] 0
reg[ 2 ] 9
reg[ 23 ] 0
reg[ 3 ] -6
reg[ 24 ] 0
reg[ 4 ] 0
reg[ 25 ] 0
reg[ 5 ] 0
reg[ 26 ] 0
reg[ 6 ] 0
reg[ 27 ] 0
reg[ 7 ] 0
reg[ 28 ] 0
reg[ 8 ] 0
reg[ 29 ] 0
reg[ 9 ] 0
reg[ 30 ] 0
reg[ 10 ] 0
reg[ 31 ] 0
reg[ 11 ] 0
CF = 1
reg[ 12 ] 0
end state
Операції, які працюють з додатковою адресацією
lw 0 1
lw 0 2
lw 0 3
lw 0 4
lw 0 5
lw 0 6
lw 0 7
lw 0 8
lw 0 9
lw 0 10
divn 1 3
addn 1 3
subn 1 3
halt
x1 .fill
x2 .fill
x3 .fill
x4 .fill
x5 .fill
x6 .fill
x1
x2
x3
x4
x5
x6
x7
x8
x9
x10
5
7
9
2
20
4
5
6
0
34
x7 .fill
x8 .fill
x9 .fill
x10 .fill
8
0
10
0
Результат:
Спочатку виконується команда divn 1 3 5. Із регістра 1 отримує значення 2, яке
у свою чергу є номером регістра, який зберігає значення 20. Отже, отримуємо
значення 20. Аналогічно отримуємо інше значення, яке дорівнює 5. Результат
необхідно записати у регістр 6. Після цієї операції туди буде записано
значення 20/5 = 4. За цим же ж алгоритмом у регістр 8 буде записано результат
20+5 = 25, у регістр 10 результат 20-5 = 15.
final state of machine:
mem[ 20 ] 8
mem[ 21 ] 0
@@@
mem[ 22 ] 10
state:
mem[ 23 ] 0
pc 14
registers:
memory:
reg[ 0 ] 0
mem[ 0 ] 2148532238
reg[ 1 ] 2
mem[ 1 ] 2149580815
reg[ 2 ] 20
mem[ 2 ] 2150629392
reg[ 3 ] 4
mem[ 3 ] 2151677969
reg[ 4 ] 5
mem[ 4 ] 2152726546
reg[ 5 ] 6
mem[ 5 ] 2153775123
reg[ 6 ] 4
mem[ 6 ] 2154823700
reg[ 7 ] 8
mem[ 7 ] 2155872277
reg[ 8 ] 25
mem[ 8 ] 2156920854
reg[ 9 ] 10
mem[ 9 ] 2157969431
reg[ 10 ] 15
mem[ 10 ] 2157969431
reg[ 11 ] 0
mem[ 11 ] 2157969431
reg[ 12 ] 0
mem[ 12 ] 2157969431
reg[ 13 ] 0
mem[ 13 ] 6442450944
reg[ 14 ] 0
mem[ 14 ] 2
reg[ 15 ] 0
mem[ 15 ] 20
reg[ 16 ] 0
mem[ 16 ] 4
reg[ 17 ] 0
mem[ 17 ] 5
reg[ 18 ] 0
mem[ 18 ] 6
reg[ 19 ] 0
mem[ 19 ] 0
reg[ 20 ] 0
35
reg[ 21 ] 0
reg[ 28 ] 0
reg[ 22 ] 0
reg[ 29 ] 0
reg[ 23 ] 0
reg[ 30 ] 0
reg[ 24 ] 0
reg[ 31 ] 0
reg[ 25 ] 0
CF = 0
reg[ 26 ] 0
end state
reg[ 27 ] 0
36
Використана література
1. Мельник А. О. Архітектура комп’ютера. Наукове видання. – Луцьк:
Волинська
обласна друкарня, 2008. – 470 с.
2. Методичні вказівки до курсової роботи на тему “Проектування
комп’ютера”. Мельник А.О., Троценко В.В.
3. Жмакин А. П. Архитектура ЭВМ. – СПб.: БХВ-Петербург, 2006. — 320 с:
ил.
4. Таненбаум Э. Архитектура компьютера. 5-е изд. (+CD). — СПб.: Питер,
2007. — 844 с: ил.
5. Patterson D., and Hennessy J. Computer Architecture. A quantitative Approach.
Second Edition. - Morgan Kaufmann Publishers, Inc., San Francisco, California,
1996. - 760 p.
37
Висновок
В ході виконання курсової роботи був розроблений асемблер, який
дозволяє переводити всі задані варіантом команди у машинні інструкції.
Даною програмою виконується безліч перевірок на відповідність правилам
синтаксису і граматики.
Також була розроблена програма симулятора, що дозволяє дешифрувати
всі машинні інструкції і виконати потрібну операцію, а також створює звіт
потактового виконання програми з виводом стану пам’яті і регістрів.
Симулятор відповідає всім поставленим вимогам, робить перевірки на
помилки і в разі їх виникнення виводить відповідне повідомлення.
Для тестування роботи обох програм створено систему тестів, що
спрямована на виявлення помилок в створенні машинних інструкцій,
розшифруванні машинних інструкцій, а також у виконанні команд.
38
Додатки
ASOL
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
MAXNUMLABELS 65536
MAXLABELLENGTH 7
MAXLINELENGTH 1000
ADD 0
NAND 1
LW 2
SW 3
BEQ 4
JALR 5
HALT 6
NOOP 7
XADD 8
DIV 9
XIMUL 10
OR 11
SHR 12
ROR 13
JMA 14
JMNLE 15
BT 16
CMP 17
SBB 18
DIVN 19
ADDN 20
SUBN 21
long long int readAndParse(FILE*, char*, char*, char*, char*, char*);
long long int translateSymbol(char labelArray[MAXNUMLABELS][MAXLABELLENGTH], long long
int labelAddress[MAXNUMLABELS], long long int, char*);
int isNumber(char*);
void testRegArg(char*);
void testAddrArg(char*);
void testArg(char*);
int
main(int argc, char* argv[])
{
char* inFileString, * outFileString;
FILE* inFilePtr, * outFilePtr;
long long int address;
char label[MAXLINELENGTH], opcode[MAXLINELENGTH], arg0[MAXLINELENGTH],
arg1[MAXLINELENGTH], arg2[MAXLINELENGTH], argTmp[MAXLINELENGTH];
int i;
long long int numLabels = 0;
long long int num;
long long int addressField;
long long int constant;
char labelArray[MAXNUMLABELS][MAXLABELLENGTH];
long long int labelAddress[MAXNUMLABELS];
if (argc != 3) {
printf("error: usage: %s <assembly-code-file> <machine-code-file>\n",
argv[0]);
39
exit(1);
}
inFileString = argv[1];
outFileString = argv[2];
inFilePtr = fopen(inFileString, "r");
if (inFilePtr == NULL) {
printf("error in opening %s\n", inFileString);
exit(1);
}
outFilePtr = fopen(outFileString, "w");
if (outFilePtr == NULL) {
printf("error in opening %s\n", outFileString);
exit(1);
}
/* map symbols to addresses */
/* assume address start at 0 */
for (address = 0; readAndParse(inFilePtr, label, opcode, arg0, arg1, arg2);
address++) {
/* check for illegal opcode */
if (strcmp(opcode, "add") && strcmp(opcode, "nand") &&
strcmp(opcode, "lw") && strcmp(opcode, "sw") &&
strcmp(opcode, "beq") && strcmp(opcode, "jalr") &&
strcmp(opcode, "halt") && strcmp(opcode, "noop") &&
strcmp(opcode, ".fill") && strcmp(opcode, "xadd") && strcmp(opcode,
"div")
&& strcmp(opcode, "ximul") && strcmp(opcode, "or") && strcmp(opcode,
"shr")
&& strcmp(opcode, "ror") && strcmp(opcode, "jma") && strcmp(opcode,
"jmnle")
&& strcmp(opcode, "bt") && strcmp(opcode, "cmp") && strcmp(opcode,
"sbb") && strcmp(opcode, "divn")
&& strcmp(opcode, "addn") && strcmp(opcode, "subn")) {
printf("error: unrecognized opcode %s at address %lli\n", opcode,
address);
exit(1);
}
/* check register fields */
if (!strcmp(opcode, "add") || !strcmp(opcode, "nand") ||
!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "beq") || !strcmp(opcode, "jalr") || !strcmp(opcode,
"div")
|| !strcmp(opcode,
!strcmp(opcode, "shr")
|| !strcmp(opcode,
"jma")
|| !strcmp(opcode,
"jmnle")
|| !strcmp(opcode,
!strcmp(opcode, "divn")
|| !strcmp(opcode,
testRegArg(arg0);
testRegArg(arg1);
}
"xadd") || !strcmp(opcode, "ximul") ||
"ror") || !strcmp(opcode, "or") || !strcmp(opcode,
"bt") || !strcmp(opcode, "cmp") || !strcmp(opcode,
"sbb") || !strcmp(opcode, "addn") ||
"subn")) {
if (!strcmp(opcode, "add") || !strcmp(opcode, "nand") || !strcmp(opcode,
"div")
|| !strcmp(opcode, "xadd") || !strcmp(opcode, "ximul") ||
!strcmp(opcode, "shr")
40
|| !strcmp(opcode, "ror") || !strcmp(opcode, "or") || !strcmp(opcode,
"sbb")
|| !strcmp(opcode, "addn") || !strcmp(opcode, "divn") ||
!strcmp(opcode, "subn")) {
testRegArg(arg2);
}
/* check addressField */
if (!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "beq") || !strcmp(opcode, "jma") || !strcmp(opcode,
"jmnle")) {
testAddrArg(arg2);
}
if (!strcmp(opcode, ".fill")) {
testAddrArg(arg0);
}
/* check for enough arguments */
if ((strcmp(opcode, "halt") && strcmp(opcode, "noop") &&
strcmp(opcode, ".fill") && strcmp(opcode, "jalr")
&& strcmp(opcode, "bt") && strcmp(opcode, "cmp")
&& arg2[0] == '\0') ||
(!strcmp(opcode, "jalr") && !strcmp(opcode, "bt") &&
!strcmp(opcode, "cmp") && arg1[0] == '\0') ||
(!strcmp(opcode, ".fill") && !strcmp(opcode, "inc") && arg0[0]
== '\0')) {
printf("error at address %lli: not enough arguments\n", address);
exit(2);
}
if (label[0] != '\0') {
/* check for labels that are too long */
if (strlen(label) >= MAXLABELLENGTH) {
printf("label too long\n");
exit(2);
}
/* make sure label starts with letter */
if (!sscanf(label, "%[a-zA-Z]", argTmp)) {
printf("label doesn't start with letter\n");
exit(2);
}
/* make sure label consists of only letters and numbers */
sscanf(label, "%[a-zA-Z0-9]", argTmp);
if (strcmp(argTmp, label)) {
printf("label has character other than letters and
numbers\n");
exit(2);
}
/* look for duplicate label */
for (i = 0; i < numLabels; i++) {
if (!strcmp(label, labelArray[i])) {
printf("error: duplicate label %s at address %lli\n",
label, address);
exit(1);
}
}
/* see if there are too many labels */
if (numLabels >= MAXNUMLABELS) {
printf("error: too many labels (label=%s)\n", label);
exit(2);
}
41
strcpy(labelArray[numLabels], label);
labelAddress[numLabels++] = address;
}
}
for (i = 0; i < numLabels; i++) {
/* printf("%s = %d\n", labelArray[i], labelAddress[i]); */
}
/* now do second pass (print machine code, with symbols filled in as
addresses) */
rewind(inFilePtr);
for (address = 0; readAndParse(inFilePtr, label, opcode, arg0, arg1, arg2);
address++) {
if (!strcmp(opcode, "add")) {
num = ((long long int) ADD << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20)
| atoll(arg2);
}
else if (!strcmp(opcode, "nand")) {
num = ((long long int) NAND << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20)
| atoll(arg2);
}
else if (!strcmp(opcode, "div")) {
num = ((long long int) DIV << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20)
| atoll(arg2);
}
(atoll(arg1)
(atoll(arg1)
(atoll(arg1)
(atoll(arg1)
else if (!strcmp(opcode, "xadd")) {
num = ((long long int) XADD << 30) | (atoll(arg0) << 25) |
<< 20)
| atoll(arg2);
}
else if (!strcmp(opcode, "ximul")) {
num = ((long long int) XIMUL << 30) | (atoll(arg0) << 25) |
<< 20)
| atoll(arg2);
}
else if (!strcmp(opcode, "shr")) {
num = ((long long int) SHR << 30) | (atoll(arg0) << 25) |
<< 20)
| atoll(arg2);
}
else if (!strcmp(opcode, "ror")) {
num = ((long long int) ROR << 30) | (atoll(arg0) << 25) |
<< 20)
| atoll(arg2);
}
else if (!strcmp(opcode, "or")) {
num = ((long long int) OR << 30) | (atoll(arg0) << 25) | (atoll(arg1)
<< 20)
| atoll(arg2);
}
else if (!strcmp(opcode, "sbb")) {
num = ((long long int) SBB << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20)
| atoll(arg2);
}
else if (!strcmp(opcode, "jalr")) {
num = ((long long int) JALR << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20);
42
}
else if (!strcmp(opcode, "bt")) {
num = ((long long int) BT << 30) | (atoll(arg0) << 25) | (atoll(arg1)
<< 20);
}
else if (!strcmp(opcode, "cmp")) {
num = ((long long int)CMP << 30) | (atoll(arg0) << 25) | (atoll(arg1)
<< 20);
}
else if (!strcmp(opcode,
num = ((long long
}
else if (!strcmp(opcode,
num = ((long long
}
"halt")) {
int) HALT << 30);
"noop")) {
int) NOOP << 30);
else if (!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "beq") || !strcmp(opcode, "jma") || !strcmp(opcode,
"jmnle")) {
/* if arg2 is symbolic, then translate into an address */
if (!isNumber(arg2)) {
addressField = translateSymbol(labelArray, labelAddress,
numLabels, arg2);
/*
printf("%s being translated into %d\n", arg2, addressField);
*/
if (!strcmp(opcode, "beq")) {
addressField = addressField - address - 1;
}
}
else {
addressField = atoll(arg2);
}
if (addressField < -524288 || addressField > 524287) {
printf("error: offset %lli out of range\n", addressField);
exit(1);
}
/* truncate the offset field, in case it's negative */
addressField = addressField & 0xFFFFF;
if (!strcmp(opcode, "beq")) {
num = ((long long int)BEQ << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20)
| addressField;
}
else if (!strcmp(opcode, "lw")) {
num = ((long long int) LW << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20) | addressField;
}
else if (!strcmp(opcode, "sw")) {
num = ((long long int) SW << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20) | addressField;
}
else if (!strcmp(opcode, "jma")) {
num = ((long long int) JMA << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20) | addressField;
}
else if (!strcmp(opcode, "jmnle")) {
num = ((long long int) JMNLE << 30) | (atoll(arg0) << 25) |
(atoll(arg1) << 20) | addressField;
43
}
}
else if (!strcmp(opcode, ".fill")) {
if (!isNumber(arg0)) {
num = translateSymbol(labelArray, labelAddress, numLabels,
arg0);
}
else {
num = atoll(arg0);
if (num >= 0) {
num = num & 0xFFFFFFFFFF;
}
else {
num = num & 0xFFFFFFFFFF;
num = num | 0xFFFFFF0000000000;
}
}
}
/* printf("(address %d): %d (hex 0x%x)\n", address, num, num); */
fprintf(outFilePtr, "%lli\n", num);
}
exit(0);
}
long long int
readAndParse(FILE* inFilePtr, char* label, char* opcode, char* arg0,
char* arg1, char* arg2)
{
char line[MAXLINELENGTH];
char* ptr = line;
label[0] = opcode[0] = arg0[0] = arg1[0] = arg2[0] = '\0';
if (fgets(line, MAXLINELENGTH, inFilePtr) == NULL) {
/* reached end of file */
return(0);
}
/* check for line too long */
if (strlen(line) == MAXLINELENGTH - 1) {
printf("error: line too long\n");
exit(1);
}
ptr = line;
if (sscanf(ptr, "%[^\t\n ]", label)) {
ptr += strlen(label);
}
sscanf(ptr, "%*[\t\n\r ]%[^\t\n\r ]%*[\t\n\r ]%[^\t\n\r ]%*[\t\n\r ]%[^\t\n\r
]%*[\t\n\r ]%[^\t\n\r ]",
opcode, arg0, arg1, arg2);
return(1);
}
long long int
translateSymbol(char labelArray[MAXNUMLABELS][MAXLABELLENGTH],
long long int labelAddress[MAXNUMLABELS], long long int numLabels, char* symbol)
44
{
int i;
for (i = 0; i < numLabels && strcmp(symbol, labelArray[i]); i++) {
}
if (i >= numLabels) {
printf("error: missing label %s\n", symbol);
exit(1);
}
return(labelAddress[i]);
}
int
isNumber(char* string)
{
int i;
return((sscanf(string, "%d", &i)) == 1);
}
void
testRegArg(char* arg)
{
long long int num;
char c;
if (atoll(arg) < 0 || atoll(arg) > 31) {
printf("error: register out of range\n");
exit(2);
}
if (sscanf(arg, "%lli%c", &num, &c) != 1) {
printf("bad character in register argument\n");
exit(2);
}
}
void
testAddrArg(char* arg)
{
long long int num;
char c;
/* test numeric addressField */
if (isNumber(arg)) {
if (sscanf(arg, "%lli%c", &num, &c) != 1) {
printf("bad character in addressField\n");
exit(2);
}
}
}
void
testArg(char* arg)
{
long long int num;
char c;
if (atoll(arg) < 0) {
atoll(arg) == atoll(arg) && 0xFFFFF;
}
/* test numeric addressField */
45
if (isNumber(arg)) {
if (sscanf(arg, "%lli%c", &num, &c) != 1) {
printf("bad character in constant\n");
exit(2);
}
}
}
SSOLL
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define NUMMEMORY 65536 /* maximum number of words in memory */
#define NUMREGS 32 /* number of machine registers */
#define MAXLINELENGTH 1000
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
ADD 0
NAND 1
LW 2
SW 3
BEQ 4
JALR 5
HALT 6
NOOP 7
XADD 8
DIV 9
XIMUL 10
OR 11
SHR 12
ROR 13
JMA 14
JMNLE 15
BT 16
CMP 17
SBB 18
DIVN 19
ADDN 20
SUBN 21
typedef struct stateStruct {
long long int pc;
long long int mem[NUMMEMORY];
long long int reg[NUMREGS];
long long int numMemory;
long long int CF;
} stateType;
void printState(stateType*);
void run(stateType*);
int convertNum(int);
int lr(int number, int shatal) {
for (int i = 0; i < shatal; i++)
{
if (number < 0)
46
{
number *= -1;
number = number >> 1;
}
else
{
number = number >> 1;
}
}
return number;
}
int loopr(int number, int shatal) {
int l_n;
for (int i = 0; i < shatal; i++)
{
if (number % 2 == 1) {
if (number > 0)
{
number = lr(number, 1);
number = number | 2147483648;
}
else
{
number = lr(number, 1);
}
}
else
{
if (number > 0)
{
number = lr(number, 1);
}
else
{
number = number >> 1;
printf("hhhh");
}
}
}
return number;
}
int
main(int argc, char* argv[])
{
int i;
char line[MAXLINELENGTH];
stateType* state = new stateType;
FILE* filePtr;
if (argc != 2) {
printf("error: usage: %s <machine-code file>\n", argv[0]);
exit(1);
}
/* initialize memories and registers */
for (i = 0; i < NUMMEMORY; i++) {
47
state->mem[i] = 0;
}
for (i = 0; i < NUMREGS; i++) {
state->reg[i] = 0;
}
state->pc = 0;
state->CF = 0;
filePtr = fopen(argv[1], "r");
if (filePtr == NULL) {
printf("error: can't open file %s\n", argv[1]);
perror("fopen");
exit(1);
}
for (state->numMemory = 0; fgets(line, MAXLINELENGTH, filePtr) != NULL;
state->numMemory++) {
if (state->numMemory >= NUMMEMORY) {
printf("exceeded memory size\n");
exit(1);
}
if (sscanf(line, "%lli", state->mem + state->numMemory) != 1) {
printf("error in reading address %lli\n", state->numMemory);
exit(1);
}
printf("memory[%lli]=%lld\n", state->numMemory, state->mem[state>numMemory]);
}
printf("\n");
/* run never returns */
run(state);
return(0);
}
void
run(stateType* state)
{
long long int arg0, arg1, arg2, addressField;
int constant;
long long int instructions = 0;
long long int opcode;
long long int maxMem = -1;
for (; 1; instructions++) {
printState(state);
if (state->pc < 0 || state->pc >= NUMMEMORY) {
printf("pc went out of the memory range\n");
exit(1);
}
maxMem = (state->pc > maxMem) ? state->pc : maxMem;
/* this is to make the following code easier to read */
opcode = state->mem[state->pc] >> 30;
arg0 = (state->mem[state->pc] >> 25) & 0x1F;
arg1 = (state->mem[state->pc] >> 20) & 0x1F;
arg2 = state->mem[state->pc] & 0x1F; /* only for add, nand */
constant = state->mem[state->pc] & 0xFFFFF;
48
addressField = convertNum(state->mem[state->pc] & 0xFFFFF); /* for beq,
lw, sw */
state->pc++;
if (opcode == ADD) {
state->reg[arg2] = state->reg[arg0] + state->reg[arg1];
if (state->reg[arg2] > 1099511627775)
{
state->reg[arg2] = 0;
}
}
else if (opcode == NAND) {
state->reg[arg2] = ~(state->reg[arg0] & state->reg[arg1]);
}
else if (opcode == DIVN) {
state->reg[state->reg[arg2]] = state->reg[state->reg[arg0]] / state>reg[state->reg[arg1]];
}
else if (opcode == ADDN) {
state->reg[state->reg[arg2]] = state->reg[state->reg[arg0]] + state>reg[state->reg[arg1]];
}
else if (opcode == SUBN) {
state->reg[state->reg[arg2]] = state->reg[state->reg[arg0]] - state>reg[state->reg[arg1]];
}
else if (opcode == XADD) {
state->reg[arg2] = state->reg[arg0] + state->reg[arg1];
long long int a = 0;
a = state->reg[arg0];
state->reg[arg0] = state->reg[arg1];
state->reg[arg1] = a;
}
else if (opcode == XIMUL) {
state->reg[arg2] = state->reg[arg0] + state->reg[arg1];
long long int a = 0;
a = state->reg[arg0];
state->reg[arg0] = state->reg[arg1];
state->reg[arg1] = a;
}
else if (opcode == DIV) {
long long int a;
long long int b;
if (state->reg[arg0] < 0) {
a = (state->reg[arg0] ^ 0xFFFFFFFFFFFFFFFF) + 1;
}
else { a = state->reg[arg0]; }
if (state->reg[arg1] < 0) {
b = (state->reg[arg1] ^ 0xFFFFFFFFFFFFFFFF) + 1;
}
else { b = state->reg[arg1]; }
state->reg[arg2] = a / b;
}
else if (opcode == OR) {
state->reg[arg2] = state->reg[arg0] | state->reg[arg1];
}
else if (opcode == SHR) {
state->reg[arg2] = lr(state->reg[arg0], state->reg[arg1]);
}
else if (opcode == ROR) {
state->reg[arg2] = loopr(state->reg[arg0], state->reg[arg1]);
}
else if (opcode == LW) {
49
if ((state->reg[arg0]) + addressField < 0 ||
(state->reg[arg0]) + addressField >= NUMMEMORY) {
printf("address out of bounds\n");
exit(1);
}
state->reg[arg1] = state->mem[(state->reg[arg0]) + addressField];
state->reg[arg1] = state->reg[arg1];
if ((state->reg[arg0]) + addressField > maxMem) {
maxMem = (state->reg[arg0]) + addressField;
}
}
else if (opcode == SW) {
if (state->reg[arg0] + addressField < 0 ||
state->reg[arg0] + addressField >= NUMMEMORY) {
printf("address out of bounds\n");
exit(1);
}
state->mem[state->reg[arg0] + addressField] = state->reg[arg1];
if (state->reg[arg0] + addressField > maxMem) {
maxMem = state->reg[arg0] + addressField;
}
}
else if (opcode == BEQ) {
if (state->reg[arg0] == state->reg[arg1]) {
state->pc += addressField;
}
}
else if (opcode == JMA) {
if ((unsigned int)state->reg[arg0] > (unsigned int)state->reg[arg1])
{
state->pc += addressField;
}
}
else if (opcode == JMNLE) {
if (state->reg[arg0] > state->reg[arg1]) {
state->pc += addressField;
}
}
else if (opcode == BT) {
long long int temp = state->reg[arg0];
int i;
for (i = 0; i < state->reg[arg1]; i++)
temp = temp >> 1;
temp = temp & 0x000000001;
state->CF = temp;
}
else if (opcode == CMP) {
if (state->reg[arg0] < state->reg[arg1]) {
state->CF = 1;
}
else state->CF = 0;
}
else if (opcode == SBB) {
state->reg[arg2] = state->reg[arg0] - state->reg[arg1] - state->CF;
}
else if (opcode == JALR) {
state->reg[arg1] = state->pc;
if (arg0 != 0)
state->pc = state->reg[arg0];
else
state->pc = 0;
}
else if (opcode == NOOP) {
50
}
else if (opcode == HALT) {
printf("machine halted\n");
printf("total of %lli instructions executed\n", instructions + 1);
printf("final state of machine:\n");
printState(state);
exit(0);
}
else {
printf("error: illegal opcode 0x%lli\n", opcode);
exit(1);
}
state->reg[0] = 0;
}
}
void
printState(stateType* statePtr)
{
int i;
printf("\n@@@\nstate:\n");
printf("\tpc %lli\n", statePtr->pc);
printf("\tmemory:\n");
for (i = 0; i < statePtr->numMemory; i++) {
printf("\t\tmem[ %d ] %lld\n", i, statePtr->mem[i]);
}
printf("\tregisters:\n");
for (i = 0; i < NUMREGS; i++) {
printf("\t\treg[ %d ] %lli\n", i, statePtr->reg[i]);
}
//printf("Index Register: %d\n", statePtr->inReg);
printf("CF = %lli\n", statePtr->CF);
printf("end state\n");
}
int
convertNum(int num)
{
/* convert a 16-bit number into a 32-bit Sun integer */
if (num & (1 << 19)) {
num -= (1 << 20);
}
return(num);
}
51
Download