Вступ
В ході виконання даного курсового проекту передбачає ознайомлення та опанування архітектуру CISC – комп’ютера. Основні принципи даної архітектури, які запропонував Джон фон Нейман:
1. Інформація кодується в двійковому представленні.
2. Інформація в комп’ютері ділиться на команди і дані.
3. Різнотипні за змістом слова розрізняються за способом застосування, а не по способу кодування.
4. Слова інформації розміщаються в комірках пам’яті та ідентифікуються номерами комірок – адресами слів.
5. Пам’ять є лінійною.
6. Пам’ять має довільну адресацію.
7. Команди і дані зберігаються в одній пам’яті.
8. Алгоритми представляються у вигляді послідовності керуючих слів, як називаються командами. Команда визначається найменуванням операції та слів інформації, які в ній приймають участь. Алгоритм записаний у вигляді послідовності команд, називається програмою.
9. Весь набір виконуваних комп’ютером команд називається системою команд комп’ютера.
10. Виконання обчислень, які визначені алгоритмом, являють собою послідовне виконання команд в порядку визначеному програмою.Для виконання задачі на комп’ютері необхідно:
- забезпечити вибірку команди програми із його пам’яті в заданій послідовності, організувати звернення до неї за відповідними адресами;
- забезпечити розпізнавання типів виконуваних операцій;
- організувати звернення до пам’яті за відповідними адресами для вибірки необхідних для виконання кожної команди даних;
- організувати виконання над даними операцій відповідно до вказівок команд;
- запам’ятати результат обчислень.
Комп'ютер виконує кожну команду як послідовність простих операцій:
1. Вибірка чергової команди із основної пам'яті.
2. Визначення типу вибраної команди, тобто її дешифрування.
3. Визначення адрес даних, необхідних для виконання цієї команди.
4. Виконання операцій пересилання даних (зчитування даних із пам'яті в регістри процесора).
5. Виконання операції відповідно до її коду в полі коду операції команди.
6. Визначення адрес, за якими запам'ятовуються результати.
7. Запам'ятовування результатів.
8. Підготовка до виконання наступної команди, тобто обчислення її адреси.Для процесора комп'ютера із складною системою команд характерні наступні особливості:
- виконання команди за багато тактів, оскільки для цього потрібно здійснитибагаторазові операції звернення до основної пам'яті та до програмно-доступнихрегістрів процесора;
- орієнтація АЛП на виконання великої кількості операцій, що пов'язано з розширенимскладом системи команд;
- складна система розпізнавання команди, що пов'язано з великою кількістю методівадресації та великою кількістю форматів команд різної розрядності;
- програмне дешифрування команд з метою зменшення затрат обладнання;
- складна організація конвеєризації виконання команд, що пов'язано, в першу чергу, різнотипністю їх виконання;
- орієнтація структури на виконання команд типу регістр-пам'ять та пам'ять-пам'ять.
Основні елементи процесора - арифметико-логічний пристрій, пристрій керування і регістрова пам'ять або, як її ще називають, надоперативний запам'ятовуючий пристрій. До складу регістрової пам'яті, в свою чергу, входять наступні вузли - програмний лічильник, регістри: адреси, команди, даних, слова стану програми, а також регістровий файл, який складається з програмно доступних регістрів. Cтруктура регістрової (надоперативної) пам'яті процесора складається з регістрів спеціального та зального призначення. До регістрів спеціального призначення належать:
- регістри адреси (РгА);
- регістри команд (РгК);
- програмний лічильник(ПЛ)
- регістри даних (РгД).
РгА зберігає адресу даного або команди при зверненні до основної пам'яті. РгД зберігає операнд при його запису або зчитуванні з основної пам'яті. В ролі операнда може бути дане, команда або адреса. РгК зберігає команду після її зчитування з основної пам'яті. ПЛ підраховує команди та зберігає адресу поточної команди. Комп'ютер з архітектурою Джона фон Неймана має один програмний лічильник. Більшість комп'ютерів мають в складі процесора тригери для зберігання бітів стану процесора, або, як їх іще називають, прапорців. Кожен прапорець має спеціальне призначення. Частина прапорців вказує на результати арифметичних і логічних операцій: додатній результат (Р), від'ємний результат (N), нульовий результат (Z), перенос (С), арифметичне переповнення (V), і т. д. В системі команд комп'ютера є команди, які вказують процесору коли встановити чи скинути ці тригери. Інша частина прапорців вказує режими захисту пам'яті. Існують також прапорці, які вказують пріоритети виконуваних програм. В деяких процесорах додаткові тригери служать для зберігання кодів умов, формуючи регістр кодів умов. Взяті разом описані прапорці формують слово стану програми (ССП), а відповідні тригери - регістр ССП. Регістри загального призначення (РЗП) є програмно доступними. Зазвичай 'їх називають регістровим файлом. Вони можуть використовуватись програмістом в якості регістрів для зберігання вхідних та вихідних даних, а також проміжних результатів обчислень, в якості адресних та індексних регістрів при виконанні операцій модифікації адрес.
Система машинних інструкцій СК
В першій частині даного курсового проекту будується архітектура «спрощеного
комп’ютера», який скорочено будемо називати СК. Архітектура даного комп’ютера хоч проста, але достатня для рішення складних задач. Для виконання даного курсового проекту необхідно володіти знаннями про набір інструкцій та формат інструкцій СК.
Рис 1. Функціональна схема СК.
Рис 1. Функціональна схема СК.
В спрощеному комп’ютері (СК) в пам’яті зберігаються, як дані так і інструкції.
Кожна інструкція закодована числом. Це число складається з декількох полів: поле назви команди чи код операції (КОП) та полів операндів. В СК є два види пам’яті: загальна пам’ять, та регістрова пам’ять. В загальній пам’яті зберігаються інструкції програми та дані над якими оперують інструкції. В регістровий пам’яті зберігаються дані над якими виконуються інструкції. У реальних комп’ютерах регістрова пам’ять є малою за розмірами та швидкою, працює на швидкості ядра процесора, загальна пам’ять є великою за розміром, але набагато повільніша за ядро процесора. Регістрова пам’ять підтримує лише пряму адресацію, загальна пам’ять підтримує декілька типів адресації. У СК є 8 регістрів по 32 розряди, пам’ять складається з 65536 слів по 32 розряди. Одже СК є 32 розрядним комп’ютером. Він підтримує 8 інструкцій, кожна з яких розписана нижче. У СК є спеціальний регістр лічільник команд (ЛК).
За прийнятою домовленістю 0вий регістр завжди містить 0 (це не обмовлено
апаратними вимогами проте асемблерна програма ніколи не має змінювати значення 0ого регістра, який ініціалізуються 0 ).
Завдання на виконання
1.Реалізація додаткових команд. Необхідно реалізувати 8 додаткових команд. Серед них 3 арифметичні, 3 логічні та 2 команди керування згідно варіанту. Команди не мають повторюватися.
INC regA
Збільшити на 1
XADD regA regB destReg
Додати і обміняти операнди місцями destReg=regA+regB
regA<=>regB
SUB regA regB destReg
Віднімання : destReg=regA-regB
Арифметичні
Логічні
XOR regA regB destReg
Додавання по модулю 2: destReg=regA # regB
SHL regA regB destReg
Логічний зсув вліво destReg=regA << regB
CMPG regA regB destReg
Порівняти regA regB destReg= regA > regB
Керування.Умовні переходи
JMB regA regB offSet
Беззнакове менше if (regA< regB) PC=PC+1+offSet
JMGE regA regB offSet
Знакове більше/рівно if (regA >= regB) PC=PC+1+offSet
Регістр ознаки нуля ZF
BSF regA destReg
Побітове сканування в прямому( від молодших
до старших) напрямку regA в пошуках біта з 1 ,
повертає номер позиції в destReg. Якщо 1 знайдено
ZF=1, інакше ZF=0
JE offSet
Перейти, якщо менше, if(ZF==0)PC=offset
JNE offSet
Перейти, якщо більше чи рівно, if(ZF!
=0)PC=offset
Адресація
Індексна (розробити IR – індексний регістр)
XADD 1 2 3
31 27 26 22 21 19 18 16 15 3 2 0
unused
SUB
Reg A
Reg B
unused
destReg
СК підтримує 4 формати інструкцій. Біти 31-27 не використовує жодна інструкція
тому вони завжди мають дорівнювати 0.
Інструкції (sub,xadd,and,xor,cmpg):
біти 26-22: код операції
біти 21-19: reg A
біти 18-16: reg B
біти 15-3: не використовуються ( =0)
біти 2-0: destReg
JMB 1 2
31 27 26 22 21 19 18 16 15 0
unused
JMA
Reg A
Reg B
Offset
Інструкції (jmb,jmge)
біти 27-22: код операції
біти 21-19: reg A
біти 18-16: reg B
біти 15-0: не використовуються ( =0)
Роз’яснення та аналіз основних принципів побудови симулятора комп’ютера.
Асемблерна мова та асемблер.
В першій частині даного курсового проекту необхідно написати програму, якаперетворює вхідну програму на мові асемблер в мову машинних кодів. Програма маєперетворити асемблерні імена команд в числові еквіваленти, наприклад асемблернукоманду beq в 100, також перетворити символьні імена адрес в числові значення.Результуючий файл має складатися з послідовності 32 бітних інструкцій (біти 31-25інструкції завжди рівні 0).
Формат лінійки асемблерного коду наступний (<пробіл> означає послідовністьтабуляцій і/або пробілів):
мітка <пробіл>інструкція<пробіл>поле№1<пробіл>поле№2<пробіл>поле№3<пробіл>коментар
Крайнє ліве поле лінійки асемблерного коду – поле мітки. Коректна мітка маєскладатися максимуму з 6 символів, символами можуть бути літери або цифри, алепочинатися з букви. Поле мітки є необов’язковим, проте пробіл після даного поля єобов’язковим. Мітки дозволяють значно спростити процес написання асемблер нихпрограм, в іншому випадку прийшлось би модифікувати всі адресні частини кожен разколи додавався рядок коду!
Після не обов’язкової мітки іде обов’язковий пробіл. Далі іде поле назви інструкції,в якому може бути ім’я будь якої асемблерної інструкції зазначені вище в таблиці. Післяпробілів ідуть відповідні поля. Всі поля можуть зберігати або десяткові значення абомітки. Кількість полів залежить від інструкції, поля які не використовуються ігноруються.
Інструкції r-типу (add, nand) потребують наявності 3 полів: поле№1 – regA, поле№2regB поле№3 destReg.
Інструкції і-типу (lw,sw,beq) вимагають 3 полів: поле№1 – regA, поле№2 regBполе№3 – числове значення зміщення чи символьна адреса. Числове значення може бутияк додатнім так і відємним. Символьні адреси описані нижче.
Інструкція J-типу (jalr) вимагає 2 полів: поле№1 – regA, поле№2 regB
Інструкція 0-типу (noop, halt) не вимагає жодного.
Символьні адреси посилаються на відповідні мітки. Для інструкцій lw та swасемблер має згенерувати зміщення, яке дорівнює адресі мітки. Вона можевикористовуватися з 0 регістром, тоді буде посилання на мітку, або можевикористовуватися з не нульовим базовим регістром у якості індексу масиву, якийпочинається з мітки. Для інструкції beq, асемблер має перетворити мітку в числовезміщення куди має відбуватися перехід.
Після останнього поля має йти пробіл за яким може розміщуватися коментар.Коментар закінчується з кінцем лінії асемблерної програми. Коментарі дуже важливі дляотримання зрозумілої асемблерної програми, тому що інструкції самі по собі малозрозумілі.
Крім інструкцій СК, асемблерна програма може містити директиви для асемблера.В даному курсовому проекті для асемблера використовується лише одна директива - .fill(зверніть увагу на точку попереду). Директива . fill повідомляє компілятору про те, що вінмає зберегти число за адресою відповідно де дана інструкція знаходиться. Директива .fillвикористовує одне поле, в якому може бути як число так і символьна адреса. Наприклад«.fill 32» означає зберегти число 32 за адресою де дана інструкція знаходиться. (Оскількив нас кожен рядок програми відповідає адресі починаючи з 0, то відповідно адреса будедорівнювати номеру рядка - 1). Директива . fill з символьною адресою збереже адрессуданої мітки.
Асемблер має виконувати два проходи через асемблерну програму. На першомупроході, асемблер має вирахувати адреси кожної символьної мітки. Виходячи з того, щоперша інструкція знаходить по нульовій адресі. На другому проході, асемблер маєгенерувати машинні інструкції (у вигляді десткових чисел) для кожного рядкуасемблерної мови. Зауважимо, що програмі імена файлів мають передаватися у якості аргументівкомандного рядка. Асемблер має зберігати в результуючому файлі лише машинні командиу вигляді десяткових чисел, одну команду в одному рядку. Порушення даного форматупризведе до того, що вихідний файл не можна буде виконати. Інший вивід (наприклад длявідладки ) програма може виконувати у консоль.
Асемблер має визначати наступні помилки в асемблер ній програмі: використанняне визначених міток, використання однакових міток, використання змішення якеперевищує 16 біт, не визначені команди. Асемблер має повертати 1, якщо він визначивпомилку та 0 у випадку успішного виходу з програми. Асемблер не має визначатипомилки виконання програми, тобто помилки які виникають під час виконання програми.
Одною з частин виконання даного курсової роботи створити можину тестів длятого щоб протестувати роботу асемблера. Створення наботу тестів є розповсюдженоюпрактикою при розробці ПЗ. Це дозволяє впевнитися в правильності виконання програмипри її модифікаціях. Створення всебічних тестів дозволить глибше зрозуміти специфікупроекту та вашої програми, та допоможе налагодити програму.Множиною тестів буде набір невеличких асемблер них програм в якості вхіднихеталонів. При захисті курсової роботи необхідно продемонструвати не менше як 20 тестів,кожен з яких складається не менше ніж з 50 рядків.Важливо створити тести для перевірки можливості асемблера визначити помилки вкоді.
Поведінкова симуляція.
Другою частино даної курсової роботи є створення програми, яка може відсимулювати роботу любого вірного машинного коду СК. Вхідним має бути файл змашинним кодом програми, якій має створити асемблер. Наприклад, якщо назва програмиsimulate та машинний код зберігається в файлі program.mc, програма має запускатисянаступнимчином:
simulateprogram.mc>output
При цьому весь вивід буде виконуватися в файл "output".
Симулятор має розпочинати роботи з ініціалізації вмісту всіх регістрів
Симулятор має виконувати програму доти не зустріне команду halt.
Симулятор має виводити вивід стану комп’ютера перед виконанням кожноїінструкції та один раз перед виходом з програми. Вивід стану має включати вивід вмістувсіх регістрів, ПЛ, пам’яті. Пам’ять має виводитися лише для комірок визначених в файліз машинними кодами (наприклад у наведеному вище прикладі це адреси від 0 до 9).
Так само як і для асемблера, необхідно написати набір тестів і для перевіркироботи симулятора СК.Набір тестів для симулятра є простою задачею адже вже буде набір тестів дляасемблера. Отже необхідно дише відібрати коректні програми, в якості вхідних тестовихфайлів для симулятора. При захисті необхідно буде представити набір тестів і длясимулятора. Кожен тест має виконуватися не менше як на 200 інструкціях, набір тестівмає складатися не менше ніж з 20 тестових прикладів.
Результати роботи.
В ході виконання курсової роботи був розроблений асемблер, який дозволяє переводити всі команди, які були подані, як вхідні дані у машинні іструкції, а також асемблер виконує всі перевірки, які потрібно виконувати згідно з завданням і у випадку помилки видає відповідне повідомлення.
Правельність роботи асемблера була перевірена за допомогою спеціально розроблених тестів, які написані за допомогою мови асемблера. Ці тести перевіряють корекність роботи всіх реалізованих команд і директив.
Система тестів
inc.as
Програма збільшує значення на 1
lw 0 1 x1
inc 1
done halt
x1 .fill 6
2.sub.as
Програма обчилює віднімання двох операндів
lw 0 1 x1
lw 0 2 x2
sub 1 2 3
done halt
x1 .fill 1
x2 .fill 2
3.xadd.as
Програма виконує додавання двох операндів і міняє їх місцями
lw 0 1 x1
lw 0 2 x2
xadd 1 2 3
done halt
x1 .fill 1
x2 .fill 2
Код програми симулятора поданий в додатку В.
Висновок.
В ході виконання курсової роботи була досягнута головна мета – розроблена модель архітектури спрощеного комп’ютера (СП). Дана модель побудована на основі CISCархітектури і забезпечила виконання всіх основних принципів описаних фон Нейманом:
Вся інформація ділиться на команди і дані
Команди і дані розміщуються в одній пам’яті, доспуп до них можна отримати по адресі комірки
Пам’ять є лінійною
Пам’ять має довільну адресацію
Алгоритми представляються у послідовності команд
Та інші.
Одже дана модель відповідає всім поставленим вимог і є простим прикладом комп’ютера з CISCархітектурою.
Список використаної літератури.
1. Мельник А. О. Архітектура комп’ютера. Наукове видання. – Луцьк: Волинська
обласна друкарня, 2008. – 470 с.
2. Жмакин А. П. Архитектура ЭВМ. – СПб.: БХВ-Петербург, 2006. — 320 с: ил.
3. Таненбаум Э. Архитектура компьютера. 5-е изд. (+CD). — СПб.: Питер, 2007.
844 с: ил.
4. Patterson D., and Hennessy J. Computer Architecture. A quantitative Approach. SecondEdition. - Morgan Kaufmann Publishers, Inc., San Francisco, California, 1996. - 760 p.
Додаток 1
Asol.c
/* Assembler for LC */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAXLINELENGTH 1000
#define MAXNUMLABELS 65536
#define MAXLABELLENGTH 7 /* includes the null character termination */
#define ADD 0
#define NAND 1
#define LW 2
#define SW 3
#define BEQ 4
#define JALR 5
#define HALT 6
#define NOT 7
#define INC 8
#define XADD 9
#define SUB 10
#define XOR 11
#define SHL 12
#define CMPG 13
#define JMB 14
#define JMGE 15
#define LWI 16
#define AND 17
#define SHR 18
#define BSR 19
#define JE 20
#define JNE 21
int readAndParse(FILE *, char *, char *, char *, char *, char *);
int translateSymbol(char labelArray[MAXNUMLABELS][MAXLABELLENGTH], int labelAddress[MAXNUMLABELS], int, char *);
int isNumber(char *);
void testRegArg(char *);
void testAddrArg(char *);
int
main(int argc, char *argv[])
{
char *inFileString, *outFileString;
FILE *inFilePtr, *outFilePtr;
int address;
char label[MAXLINELENGTH], opcode[MAXLINELENGTH], arg0[MAXLINELENGTH],
arg1[MAXLINELENGTH], arg2[MAXLINELENGTH], argTmp[MAXLINELENGTH];
int i;
int numLabels=0;
int num;
int addressField, addressField1, addressField2, addressField3;
char labelArray[MAXNUMLABELS][MAXLABELLENGTH];
int labelAddress[MAXNUMLABELS];
if (argc != 3) {
printf("error: usage: %s <assembly-code-file> <machine-code-file>\n",
argv[0]);
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++) {
/*
printf("%d: label=%s, opcode=%s, arg0=%s, arg1=%s, arg2=%s\n",
address, label, opcode, arg0, arg1, arg2);
*/
/* 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, "not") &&
strcmp(opcode, ".fill") && strcmp(opcode, "inc") &&
strcmp(opcode, "xadd") && strcmp(opcode, "sub") &&
strcmp(opcode, "xor") && strcmp(opcode, "shl") &&
strcmp(opcode, "cmpg") && strcmp(opcode, "jmb") &&
strcmp(opcode, "lwi") && strcmp(opcode, "shr") &&
strcmp(opcode, "bsr") && strcmp(opcode, "je") &&
strcmp(opcode, "jne") &&
strcmp(opcode, "and") && strcmp(opcode, "jmge") ) {
printf("error: unrecognized opcode %s at address %d\n", opcode,
address);
exit(1);
}
if ((!strcmp(opcode, "sub") || !strcmp(opcode, "xadd") || !strcmp(opcode, "cmpg") ||
!strcmp(opcode, "xor") || !strcmp(opcode, "shl")) && isNumber(arg0) && isNumber(arg1) && isNumber(arg2))
{
testRegArg(arg0);
testRegArg(arg1);
testRegArg(arg2);
}
else
{
testAddrArg(arg0);
}
/* check register fields */
if (!strcmp(opcode, "add") || !strcmp(opcode, "nand") ||
!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "beq") || !strcmp(opcode, "jalr") ||
!strcmp(opcode, "not") || !strcmp(opcode, "inc") ||
!strcmp(opcode, "jmb" ) || !strcmp(opcode, "lwi") ||
!strcmp(opcode, "shr") || !strcmp(opcode, "and") ||
!strcmp(opcode, "jmge") || !strcmp(opcode, "bsr")) {
testRegArg(arg0);
}
if (!strcmp(opcode, "add") || !strcmp(opcode, "nand") ||
!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "beq") || !strcmp(opcode, "jalr") ||
!strcmp(opcode, "not") || !strcmp(opcode, "jmb" ) ||
!strcmp(opcode, "jmge") || !strcmp(opcode, "shr") ||
!strcmp(opcode, "and") || !strcmp(opcode, "bsr")) {
testRegArg(arg1);
}
if (!strcmp(opcode, "add") || !strcmp(opcode, "nand") ||
!strcmp(opcode, "shr") || !strcmp(opcode, "and") ||
!strcmp(opcode, "bsr")) {
testRegArg(arg2);
}
/* check addressField */
if (!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "beq") || !strcmp(opcode, "jmb") ||
!strcmp(opcode, "jmge")) {
testAddrArg(arg2);
}
if (!strcmp(opcode, "lwi"))
{
testAddrArg(arg1);
}
if (!strcmp(opcode, ".fill") || !strcmp(opcode, "je") || !strcmp(opcode, "jne") ) {
testAddrArg(arg0);
}
/* check for enough arguments */
if(isNumber(arg0) && isNumber(arg1) && isNumber(arg2))
if ( (strcmp(opcode, "halt") && strcmp(opcode, "not") && strcmp(opcode, "lwi") &&
strcmp(opcode, ".fill") && strcmp(opcode, "jalr") && strcmp(opcode, "inc")
&& strcmp(opcode, "je") && strcmp(opcode, "jne") && arg2[0]=='\0') ||
(!strcmp(opcode, "jalr") && arg1[0]=='\0') ||
(!strcmp(opcode, ".fill") && arg0[0]=='\0') ||
(!strcmp(opcode, "jne") && arg0[0]=='\0') ||
(!strcmp(opcode, "je") && arg0[0]=='\0')) {
printf("error at address %d: 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 %d\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);
}
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 = (ADD << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
} else if (!strcmp(opcode, "nand")) {
num = (NAND << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
} else if (!strcmp(opcode, "xor") && isNumber(arg0) && isNumber(arg1) && isNumber(arg2)) {
num = (XOR << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
} else if (!strcmp(opcode, "sub") && isNumber(arg0) && isNumber(arg1) && isNumber(arg2)) {
num = (SUB << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
} else if (!strcmp(opcode, "cmpg") && isNumber(arg0) && isNumber(arg1) && isNumber(arg2)) {
num = (CMPG << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
} else if (!strcmp(opcode, "shl") && isNumber(arg0) && isNumber(arg1) && isNumber(arg2)) {
num = (SHL << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
}else if (!strcmp(opcode, "xadd") && isNumber(arg0) && isNumber(arg1) && isNumber(arg2)) {
num = (XADD << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
} else if (!strcmp(opcode, "bsr")) {
num = (BSR << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16);
} else if (!strcmp(opcode, "shr")) {
num = (SHR << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
} else if (!strcmp(opcode, "and")) {
num = (AND << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
} else if (!strcmp(opcode, "jalr")) {
num = (JALR << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16);
} else if (!strcmp(opcode, "halt")) {
num = (HALT << 22);
} else if (!strcmp(opcode, "not")) {
num = (NOT << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16);
} else if (!strcmp(opcode, "inc")) {
num = (INC << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16);
} else if (!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "beq") || !strcmp(opcode, "jmb") ||
!strcmp(opcode, "jmge") || !strcmp(opcode, "lwi") ||
!isNumber(arg0)) {
/* if arg2 is symbolic, then translate into an address */
if (!isNumber(arg2) && strcmp(opcode, "lwi") &&
strcmp(opcode, "xadd") && strcmp(opcode, "sub") &&
strcmp(opcode, "shl") && strcmp(opcode, "cmpg")&&
strcmp(opcode, "xor")) {
addressField = translateSymbol(labelArray, labelAddress,
numLabels, arg2);
/*
printf("%s being translated into %d\n", arg2, addressField);
*/
if (!strcmp(opcode, "beq") || !strcmp(opcode, "jmb" ) || !strcmp(opcode, "jmge") ) {
addressField = addressField-address-1;
}
} else {
addressField = atoi(arg2);
}
if (!strcmp(opcode, "lwi"))
{
if (!isNumber(arg1)) {
addressField = translateSymbol(labelArray, labelAddress,
numLabels, arg1);
}
}
if(!isNumber(arg0) && strcmp(opcode, ".fill"))
{
addressField1 = translateSymbol(labelArray, labelAddress, numLabels, arg0);
if (addressField1 < -32768 || addressField1 > 32767) {
printf("error: offset %d out of range\n", addressField1);
exit(1);
}
/* truncate the offset field, in case it's negative */
addressField1 = addressField1 & 0xFFFF;
if (!strcmp(opcode, "xadd")) {
num = (XADD << 24) | addressField1;
num = -2147483648 | num;
} else if (!strcmp(opcode, "shl")) {
num = (SHL << 24) | addressField1;
num = -2147483648 | num;
} else if (!strcmp(opcode, "sub")) {
num = (SUB