МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ “ЛЬВІВСЬКА ПОЛІТЕХНІКА”
КАФЕДРА ЕЛЕКТРОННИХ ОБЧИСЛЮВАЛЬНИХ МАШИН
ПОЯСНЮВАЛЬНА ЗАПИСКА
до курсової роботи на тему
ПРОЕКТУВАННЯ КОМП’ЮТЕРА
з дисципліни “Архітектура комп’ютерів”
Варіант № 9
Перевірила
ст. викл. каф. ЕОМ
[ім’я автора приховано]
Львів- 2019
АНОТАЦІЯ
В даній курсові роботі розглядаються принципи проектування комп’ютерів з архітектурою CISC. Здійснено аналіз основних принципів побудови комп’ютерів з використанням вище згаданої архітектури. Здійснено аналіз розроблених інструкцій. Подано схеми спроектованих комп’ютерів, а також опис створених форматів команд. Розглянуто алгоритм роботи розробленого симулятора та асемблера.
ЗМІСТ
ВИХІДНІ ДАНІ НА ПРОЕКТ УВАННЯ
Варіант №9
Дані на проектування:
1. АНАЛІТИЧНИЙ РОЗДІЛ
1.1 Основні принципи побудови комп’ютерів архітектури CISC
Архітектурні принципи, які запропонував Джон фон Нейман:
1. Інформація кодується в двійковому представленні.
2. Інформація в комп’ютері ділиться на команди і дані.
3. Різнотипні за змістом слова розрізняються за способом застосування, а не
по способу кодування.
4. Слова інформації розміщаються в комірках пам’яті та ідентифікуються
номерами комірок – адресами слів.
5. Пам’ять є лінійною.
6. Пам’ять має довільну адресацію.
7. Команди і дані зберігаються в одній пам’яті.
8. Алгоритми представляються у вигляді послідовності керуючих слів, які
називаються командами. Команда визначається найменуванням операції
та слів інформації, які в ній приймають участь. Алгоритм записаний у
вигляді послідовності команд, називається програмою.
9. Весь набір виконуваних комп’ютером команд називається системою
команд комп’ютера.
10. Виконання обчислень, які визначені алгоритмом, являють собою
послідовне виконання команд в порядку, визначеному програмою.
Для виконання задачі на комп’ютері необхідно:
− забезпечити вибірку команди програми із його пам’яті в заданій
послідовності, організувати звернення до неї за відповідними адресами;
− забезпечити розпізнавання типів виконуваних операцій;
− організувати звернення до пам’яті за відповідними адресами для вибірки
необхідні для виконання кожної команди даних;
− організувати виконання над даними операцій відповідно до вказівок
команд;
− запам’ятати результат обчислень.
Для процесора комп'ютера із складною системою команд характерні наступні особливості:
− виконання команди за багато тактів, оскільки для цього потрібно здійснити
багаторазові операції звернення до основної пам'яті та до програмно
доступних регістрів процесора;
− орієнтація АЛП на виконання великої кількості операцій, що пов'язано з
розширеним складом системи команд;
− складна система розпізнавання команди, що пов'язано з великою кількістю
методів адресації та великою кількістю форматів команд різної
розрядності;
− програмне дешифрування команд з метою зменшення затрат обладнання;
− складна організація конвеєризації виконання команд, що пов'язано, в
першу чергу, з різнотипністю їх виконання;
− орієнтація структури на виконання команд типу регістр-пам'ять та пам'ять
пам'ять.
Основні елементи процесора - арифметико-логічний пристрій, пристрій керування і регістрова пам'ять або, як її ще називають, надоперативний запам'ятовуючий пристрій. До складу регістрової пам'яті, в свою чергу, входять наступні вузли - програмний лічильник, регістри: адреси, команди, даних, слова стану програми, а також регістровий файл, який складається з програмно доступних регістрів.
Різноманітність типів даних, форм представлення та опрацювання, необхідні дії для обробки та керування ходом виконання обчислень призводить до необхідності використання різноманітних команд – набору команд.
Кожен процесор має власний набір команд, який називається системою команд процесора.
Система команд характеризується трьома аспектами:
− формат;
− способи адресації;
− система операцій.
Форматом команди – є довжина команди, кількість, розмір, положення,
призначення та спосіб кодування полів. Команди мають включати наступні види інформації:
− тип операції, яку необхідно реалізувати в даній команді (поле
команди операції - КОП)
− місце в пам’яті звідки треба взяти перший операнд (А1);
− місце в пам’яті звідки треба взяти другий операнд (А2);
− місце в пам’яті куди треба помістити результат (А3).
КОП займає k розрядів. Ним може бути закодовано до N = 2k
різних операцій. Кількість двійкових розрядів, які відводяться під код операції, вибирається таким чином, щоб ними можна було закодувати всі виконувані в даному комп'ютері операції. Якщо деякий комп'ютер може виконувати Nc різних операцій, то мінімальна розрядність поля коду операції k визначається наступним чином: k = [log2 Nc], де вираз в дужках означає
заокруглення до більшого цілого. В нашій машині реалізовано 19 команд тобто поле КОП має займати k=[log219]=5.
Поле адреси (адресна частина) займає m розрядів. В ньому знаходяться адреси операндів. Кожна адреса займає mi розрядів, де і - номер адреси (і=1,2,...1), 1 - кількість адресних полів. Кожною адресою можна адресувати пам'ять ємністю 2 слів.
В комп’ютерах з архітектурою CISC використовується багато різноманітних способів адресації серед них:
пряма;
безпосередня;
непряма;
відносна;
безадресна
2. ПРОЕКТНИЙ РОЗДІЛ
2.1. Алгоритми роботи розробленого асемблера та стимулятора
Алгоритми роботи розробленого асемблера
Рис. 2.1.0 Схема роботи асемблера
Загальна схема роботи асемблера (рис. 1) складається з 2 проходів. На першому проході асемблер перевіряє коректність синтаксису команд. На другому виконується генерування відповідних машинних команд, тобто числового представлення асемблерної команди.
Функція readAndParse виконує зчитування рядку асемблерної програми і декодування на відповідні поля: мітка, код операції, операнди. Отримана таким чином і декодована інструкція перевіряється на коректність: існування команди, відповідна кількість аргументів, існування міток та т. п.
Функція testRegArg перевіряє коректність використання назви регістра.
Функція testAddrArg перевіряє коректність використання адреси.
Функція labelArray перетворює відповідну мітку у адресу.
Program.as та program.mc – відповідно вхідний та вихідний файли.
Алгоритми роботи розробленого симулятора
Рис. 2.1.1. Схема роботи симулятора
Симулятор починає свою роботу ініціалізацією пам’яті та регістрів 0 значеннями (рис. 2.). Наступним кроком відбувається заванаження програми у машинних кодах в пам’ять. Далі відбувається покрокове виконання інструкцій та вивід стану на зовнішній пристрій (чи на екран консолі чи у файл).
У stateStruct зберігається стан машини – значення регістрів, пам’яті та програмний лічільник. stateStruct
Функція Run виконує обробку інструкцій з пам’яті, функція printState виводить поточний стан машини, а функція convertNum виконує перетворення числа у доповняльний код.
2.2. Функціональна схема комп’ютера до модифікації
Рис. 2.2. Функціональна схема спрощеного комп’ютера.
В спрощеному комп’ютері (СК) в пам’яті зберігаються, як дані так і інструкції. Кожна інструкція закодована числом. Це число складається з декількох полів: поле назви команди чи код операції (КОП) та полів операндів. В СК є два види пам’яті: загальна пам’ять(MEMORY), та регістрова пам’ять (REGISTER FILE). В загальній пам’яті зберігаються інструкції програми та дані над якими оперують інструкції. В регістровий пам’яті зберігаються дані над якими виконуються інструкції. У реальних комп’ютерах регістрова пам’ять є малою за розмірами та швидкою, працює на швидкості ядра процесора, загальна пам’ять є великою за розміром, але набагато повільніша за ядро процесора. Регістрова пам’ять підтримує лише пряму адресацію, загальна пам’ять підтримує декілька типів адресації. У СК є 8 регістрів по 24 розряди, пам’ять складається з 4096 слів по 32 розряди. У СК є спеціальний регістр – лічильник команд (programCounter), в якому зберігається адреса інструкції. За прийнятою домовленістю 0вий регістр завжди містить 0.
В регістрі instReg міститься код інструкції, яка виконується. При роботі з пам’яттю (MEMORY) використовуються два регістри – в memAddress зберігається адреса пам’яті, а в memData значення, яке читається/записується в пам’ять.
Комп’ютер містить арифметико-логічний пристрій (ALU), який може виконувати 6 дій (подані у таблиці нижче). ALU керується трьохрозрядним сигналом alu_op:
Таблиця 1. Операції АЛП
ALU працює з двома операндами, один з них завжди є в регістрі aluOperand, інший береться з головної шини (main bus), результат зберігається в регістрі aluReult.
instReg – зберігає команду після її зчитування з основної пам’яті.
RegisterFile – зберігає значення регістрів.
Memory – пам’ять.
memAddress – зберігає адресу комірки пам’яті.
memData – зберігає значення для читання/запису в пам’ять.
ProgramCounter – підраховує команди та зберігає адресу поточної команди.
2.3. Функціональна схема комп’ютера після модифікації
Рис. 2.3. Функціональна схема спрощеного комп’ютера після модифікації.
Згідно завдання було додано стек. Його глибина = 32 слова по 32 розярди, а також флажок нуля zf, що встановлюється в 1 при умові якщо 2 опернди на АЛП рівні між собою. Для стеку відведено свою пам’ять що можна буде побачити у репорті, він наповнюється і вивантажується за принципом FILO (як і всі стандартні стеки). Дані з шини, що беруться з пам’яті поступають у stackData і за операцією push заносяться до стеку на його вершину, а при операції pop, значення що лежить на вершині вивантажується з нього у stackData і потім поступає на шину даних.Всі коментарі та принципи роботи описані в коментарі до рисунку 4 залишаються незмінними.
Було додано:
zf – регістр ознаки нуля
stack – стек.
stackData – зберігає значення для запису в стек чи значення що зчитується з стеку при команді pop
Сигнали push і pop служать для запису чи зчитування даних з стеку.
2.4. Множина інструкцій комп’ютера.
Таблиця.
2.5. Опис форматів інструкцій розробленого комп’ютера
Розроблена машина підтримує наступні формати:
Інструкції R-типу (add, nand, idiv, shl, xand, or)
Інструкції I-типу (lw, sw, beq, jma, jmne)
Інструкції J-типу (jalr, bsr, cmp,neg)
Інструкції O-типу (halt, noop)
Окремо слід відзначити інструкцію JE – її не можна віднести до існуючих типів інструкції, оскільки вона містить інформацію тільки про код операції та зміщення
І інструкцію DEC її не можна віднести до існуючих типів інструкції, оскільки вона містить інформацію тільки про код операції та 1 регістр
А Також інструкції PUSH i POP – їx також не можна віднести до існуючих типів інструкції, вони містять інформацію про код операції та один регістр.
2.6. Опис виконання кожного типу інструкцій в по тактовому режимі
R-тип:
memAddress = programCounter;
memData = MEMORY[memAddress];
instReg = memData;
programCounter++;
aluOperand = REGISTER FILE[arg1]; Decoding(instReg) -> operation, arg1, arg2, arg3;
aluResult = ALU(aluOperand, REGISTER FILE[arg2], alu_op)
REGISTER FILE[arg3] = aluResult;
I-тип
memAddress = programCounter;
memData = MEMORY[memAddress];
instReg = memData;
programCounter++;
aluOperand = REGISTER FILE[arg1]; Decoding(instReg) -> operation, arg1, arg2, address;
aluResult = ALU (REGISTER FILE[arg2], aluOperand, alu_op);
MEMORY[address] = aluResult; (sw)(Або 8. programCounter += address, при виконаній умові; (jma, jmne)Або 8. REGISTER FILE[aluResult] = MEMORY[address]; (lw)
J-тип
memAddress = programCounter;
memData = MEMORY[memAddress];
instReg = memData;
programCounter++;
aluOperand = REGISTER FILE[arg1]; Decoding(instReg) -> operation, arg1, arg2;
6.a zf = ALU (REGISTER FILE[arg2], aluOperand, alu_op) (cmp)
6.b aluResult = ALU (REGISTER FILE[arg2], aluOperand, alu_op);
7.b zf = у відповідний стан до відповіді чи знайшло «1» ;
REGISTER FILE[arg2] = aluResult; (bsr)
6.c aluResult = ALU (zf, alu_op);
7.c programCounter = aluResult ; (je)
O-тип
memAddress = programCounter;
memData = MEMORY[memAddress];
instReg = memData;
programCounter++; Decoding(instReg) -> operation;
Exit(0) – вихід з програми (halt)
S-тип
memAddress = programCounter;
memData = MEMORY[memAddress];
instReg = memData;
programCounter++;
5.a memAddress = instReg[address]
6.a memData = MEMORY[memAddress];
7.a stackData = memData;
8.a stack.top = stackData; stack.top++; (push)
5.b memAddress = instReg[address];
6.b stackData = stack.top; stack.top - -;
7.b memData = stackData;
8.b MEMORY[memAddress] = memData; (pop)
Команда je
memAddress = programCounter;
memData = MEMORY[memAddress];
instReg = memData;
programCounter++; Decoding(instReg) -> operation, address;
programCounter ?= offset (в залежності від zf)
Команда dec
memAddress = programCounter;
memData = MEMORY[memAddress];
instReg = memData;
programCounter++; Decoding(instReg) -> operation, arg1;
aluOperand = REGISTER FILE[arg1];
aluResult = ALU (aluOperand, alu_op);
REGISTER FILE[arg1] = aluResult
3. ТЕСТОВИЙ РОЗДІЛ
Див. додатки (тести програм)
ВИСНОВОК
Отже, для процесора комп’ютера із складною системою команд характерні наступні особливості:
виконання команд за багато тактів;
орієнтація АЛП на виконання великої кількості операцій, що пов’язано з розширеним складом системи команд.
Складна конвеєризація виконання команд, що також пов’язано, в першу чергу, з різнотипністю їх виконання
Орієнтація на команди типу регістр-пам’ять та пам’ять- пам’ять.
Вказані особливості стимулюють побудову високопродуктивних
комп’ютерів.
В ході виконання курсової роботи був розроблений асемблер, який дозволяє переводити всі команди, які були подані, як вхідні дані у машинні інструкції, а також асемблер виконує всі перевірки, які потрібно виконувати згідно з завданням і у випадку помилки видає відповідне повідомлення.
Правильність роботи асемблера була перевірена за допомогою спеціально розроблених тестів, які написані за допомогою мови асемблера. Ці тести перевіряють коректність роботи реалізованих команд і директив.
Також був розроблений симулятор, який дозволяє дешифрувати всі машинні всі машинні інструкції, і згідно з командою виконати операцію, а також створюю звіт потактового виконання програми з виводом стану пам’яті і регістрів. Симулятор відповідає всім поставленим вимогам, робить перевірки на помилки і в разі їх виникнення виводить відповідне повідомлення. Також була розроблена система тестів, яка дозволила провести тестування коректності роботи симулятора. Система тестів була спрямована на виявлення помилок в розшифруванні машинних інструкцій, а також у виконанні команд.
ВИКОРИСТАНА ЛІТЕРАТУРА
Мельник А. О. Архітектура комп’ютера. Наукове видання. – Луцьк:
Волинська обласна друкарня, 2008. – 470 с.
Жмакин А. П. Архитектура ЭВМ. – СПб.: БХВ-Петербург, 2006. — 320 с:ил.
Таненбаум Э. Архитектура компьютера. 5-е изд. (+CD). — СПб.: Питер,
2007. — 844 с: ил.
Patterson D., and Hennessy J. Computer Architecture. A quantitative
Approach. Second Edition. - Morgan Kaufmann Publishers, Inc., San
Francisco, California, 1996. - 760 p.
ДОДАТКИ
Файли стимулятора і асемблера
Asol.cpp
/* Assembler for LC */
#pragma warning(disable:4996)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAXLINELENGTH 1000
#define MAXNUMLABELS 4096
#define MAXLABELLENGTH 7 /* includes the null character termination */
#define HALT 0
#define ADD 1
#define NAND 2
#define LW 3
#define SW 4
#define DEC 5
#define IDIV 6
#define XADD 7
#define SHL 8
#define OR 9
#define NEG 10
#define JMA 11
#define JMNE 12
#define CMP 13
#define BSR 14
#define PUSH 15
#define POP 16
#define SAND 17
#define SADD 18
#define JE 19
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*, int address);
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;
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, "halt") && strcmp(opcode, "dec") &&
strcmp(opcode, "idiv") && strcmp(opcode, "xadd") &&
strcmp(opcode, "shl") && strcmp(opcode, "or") &&
strcmp(opcode, "neg") && strcmp(opcode, "jma") &&
strcmp(opcode, "jmne") && strcmp(opcode, "cmp") &&
strcmp(opcode, "bsr") && strcmp(opcode, "je") &&
strcmp(opcode, "push") && strcmp(opcode, "pop") &&
strcmp(opcode, "sand") && strcmp(opcode, "sadd") &&
strcmp(opcode, ".fill")
)
{
printf("error: unrecognized opcode %s at address %d\n", opcode, address);
exit(1);
}
/* check register fields */
if (strcmp(opcode, "halt") &&
strcmp(opcode, "sadd") &&
strcmp(opcode, "sand") &&
strcmp(opcode, ".fill") &&
strcmp(opcode, "push") &&
strcmp(opcode, "pop") &&
strcmp(opcode, "je"))
{
testRegArg(arg0, address);
if(strcmp(opcode, "dec")){
testRegArg(arg1, address);
if (strcmp(opcode, "jma") &&
strcmp(opcode, "jmne") &&
strcmp(opcode, "bsr") &&
strcmp(opcode, "cmp") &&
strcmp(opcode, "lw") &&
strcmp(opcode, "neg") &&
strcmp(opcode, "sw"))
{
testRegArg(arg2, address);
}
}
}
/* check addressField */
if (!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "jma") || !strcmp(opcode, "jmne"))
{
testAddrArg(arg2);
}
if (!strcmp(opcode, ".fill") || !strcmp(opcode, "je"))
{
testAddrArg(arg0);
}
/* check for enough arguments */
if (
(!strcmp(opcode, "idiv") ||
!strcmp(opcode, "xadd") ||
!strcmp(opcode, "shl") ||
!strcmp(opcode, "or") ||
!strcmp(opcode, "jma") ||
!strcmp(opcode, "jmne") ||
!strcmp(opcode, "add") ||
!strcmp(opcode, "nand") ||
!strcmp(opcode, "sw") ||
!strcmp(opcode, "lw") )&&
arg2[0] == '\0'
||
(!strcmp(opcode, "bsr") || !strcmp(opcode, "cmp") || !strcmp(opcode, "neg")) &&
arg1[0] == '\0'
||
(!strcmp(opcode, "halt") ||
!strcmp(opcode, "dec") ||
!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);
}
if (!strcmp(opcode, "nand"))
{
num = (NAND << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
}
if (!strcmp(opcode, "idiv"))
{
num = (IDIV << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
}
if (!strcmp(opcode, "xadd"))
{
num = (XADD << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
}
if (!strcmp(opcode, "shl"))
{
num = (SHL << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
}
if (!strcmp(opcode, "or"))
{
num = (OR << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| atoi(arg2);
}
if (!strcmp(opcode, "bsr"))
{
num = (BSR << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16);
}
if (!strcmp(opcode, "cmp"))
{
num = (CMP << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16);
}
if (!strcmp(opcode, "neg"))
{
num = (NEG << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16);
}
if (!strcmp(opcode, "dec"))
{
num = (DEC << 22) | (atoi(arg0) << 19);
}
if (!strcmp(opcode, "push"))
{
num = (PUSH << 22);
}
if (!strcmp(opcode, "pop"))
{
num = (POP << 22);
}
if (!strcmp(opcode, "sadd"))
{
num = (SADD << 22);
}
if (!strcmp(opcode, "sand"))
{
num = (SAND << 22);
}
else if (!strcmp(opcode, "halt"))
{
num = (HALT << 22);
}
else if (!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "jma") || !strcmp(opcode, "jmne") ||
!strcmp(opcode, "je"))
{
if (!strcmp(opcode, "lw") || !strcmp(opcode, "sw") ||
!strcmp(opcode, "jma") || !strcmp(opcode, "jmne"))
{
/* 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, "jma") || !strcmp(opcode, "jmne"))
{
addressField = addressField - address - 1;
}
}
else
{
addressField = atoi(arg2);
}
}
if (!strcmp(opcode, "je"))
{
addressField = translateSymbol(labelArray, labelAddress,
numLabels, arg0);
addressField = addressField - address - 1;
}
//if (addressField < -32656 || addressField > 32657)
if (addressField < -2048 || addressField > 2048)
{
printf("error: offset %d out of range\n", addressField);
exit(1);
}
/* truncate the offset field, in case it's negative */
addressField = addressField & 0xFFFF;
if (!strcmp(opcode, "jma"))
{
num = (JMA << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| addressField;
}
if (!strcmp(opcode, "jmne"))
{
num = (JMNE << 22) | (atoi(arg0) << 19) | (atoi(arg1) << 16)
| addressField;
}
if (!strcmp(opcode, "je"))
{
num = (JE << 22) | addressField;
}
if (!strcmp(opcode, "lw"))
{
num = (LW << 22) | (atoi(arg0) << 19) |
(atoi(arg1) << 16) | addressField;
}
if (!strcmp(opcode, "sw"))
{
num = (SW << 22) | (atoi(arg0) << 19) |
(atoi(arg1) << 16) | addressField;
}
}
else if (!strcmp(opcode, ".fill"))
{
if (!isNumber(arg0))
{
num = translateSymbol(labelArray, labelAddress, numLabels,
arg0);
}
else
{
if (atol(arg0) <= -8388608 || atol(arg0) >= 8388607)
{
printf("Digit Overflow at line:%d", address);
num = 0;
}
else
{
num = atoi(arg0);
}
}
}
/* printf("(address %d): %d (hex 0x%x)\n", address, num, num); */
fprintf(outFilePtr, "%d\n", num);
}
exit(0);
}
/*
* Read and parse a line of the assembly-language file. Fields are returned
* in label, opcode, arg0, arg1, arg2 (these strings must have memory already
* allocated to them).
*
* Return values:
* 0 if reached end of file
* 1 if all went well
*
* exit(1) if line is too long.
*/
int readAndParse(FILE* inFilePtr, char* label, char* opcode, char* arg0,
char* arg1, char* arg2)
{
char line[MAXLINELENGTH];
char* ptr = line;
/* delete prior values */
label[0] = opcode[0] = arg0[0] = arg1[0] = arg2[0] = '\0';
/* read the line from the assembly-language file */
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);
}
/* is there a label? */
ptr = line;
if (sscanf(ptr, "%[^\t\n ]", label))
{
/* successfully read label; advance pointer over the label */
ptr += strlen(label);
}
/*
* Parse the rest of the line. Would be nice to have real regular
* expressions, but scanf will suffice.
*/
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);
}
int translateSymbol(char labelArray[MAXNUMLABELS][MAXLABELLENGTH],
int labelAddress[MAXNUMLABELS], int numLabels, char* symbol)
{
int i;
/* search through address label table */
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)
{
/* return 1 if string is a number */
int i;
return ((sscanf(string, "%d", &i)) == 1);
}
/*
* Test register argument; make sure it's in range and has no bad characters.
*/
void testRegArg(char* arg, int address)
{
int num;
char c;
if (atoi(arg) < 0 || atoi(arg) > 7)
{
printf("error: register out of range at address:%d\n", address);
exit(2);
}
if (sscanf(arg, "%d%c", &num, &c) != 1)
{
printf("bad character in register argument at address:%d\n", address);
exit(2);
}
}
/*
* Test addressField argument.
*/
void testAddrArg(char* arg)
{
int num;
char c;
/* test numeric addressField */
if (isNumber(arg))
{
if (sscanf(arg, "%d%c", &num, &c) != 1)
{
printf("bad character in addressField\n");
exit(2);
}
}
}
Ssol.cpp
/*
* Instruction-level simulator for the LC
*/
#pragma warning(disable:4996)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stack>
#include <math.h>
#define NUMMEMORY 4096 /* maximum number of words in memory */
#define NUMREGS 8 /* number of machine registers */
#define MAXLINELENGTH 1000
#define HALT 0
#define ADD 1
#define NAND 2
#define LW 3
#define SW 4
#define DEC 5
#define IDIV 6
#define XADD 7
#define SHL 8
#define OR 9
#define NEG 10
#define JMA 11
#define JMNE 12
#define CMP 13
#define BSR 14
#define PUSH 15
#define POP 16
#define SAND 17
#define SADD 18
#define JE 19
#define MY_MIN -8388608
#define MY_MAX 8388607
typedef struct stateStruct
{
int pc;
int mem[NUMMEMORY];
int reg[NUMREGS];
int numMemory;
bool zf;
std::stack<int> stack;
} stateType;
void printState(stateType*);
void run(stateType);
int convertNum(int);
void showStack(std::stack <int> s);
int main(int argc, char* argv[])
{
int i;
char line[MAXLINELENGTH];
stateType state;
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++)
{
state.mem[i] = 0;
}
for (i = 0; i < NUMREGS; i++)
{
state.reg[i] = 0;
}
state.pc = 0;
state.zf = false;
/* read machine-code file into instruction/data memory (starting at
address 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, "%d", state.mem + state.numMemory) != 1)
{
printf("error in reading address %d\n", state.numMemory);
exit(1);
}
printf("memory[%d]=%d\n", state.numMemory, state.mem[state.numMemory]);
}
printf("\n");
/* run never returns */
run(state);
return (0);
}
void run(stateType state)
{
int arg0, arg1, arg2, addressField;
int instructions = 0;
int opcode;
int maxMem = -1; /* highest memory address touched during run */
for (; 1; instructions++)
{
/* infinite loop, exits when it executes halt */
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] >> 22;
arg0 = (state.mem[state.pc] >> 19) & 0x7;
arg1 = (state.mem[state.pc] >> 16) & 0x7;
arg2 = state.mem[state.pc] & 0x7; /* only for add, nand */
addressField = convertNum(state.mem[state.pc] & 0xFFF); /* for beq, lw, sw */
// printf("opcode = %d\n", opcode);
// printf("state.reg[arg0] = %d\n", state.reg[arg0]);
// printf("arg0 = %d\n", arg0);
// printf("arg1 = %d\n", arg1);
// printf("arg2 = %d\n", arg2);
// printf("Adress field = %d\n", addressField);
//
//
state.pc++;
if (opcode == ADD)
{
if(long(state.reg[arg0]) + long(state.reg[arg1]) <= MY_MIN ||
long(state.reg[arg0]) + long(state.reg[arg1]) >= MY_MAX)
{
state.reg[arg2] = 0;
printf("Digit Overflow in pc=%d\n", state.pc);
}
else {
state.reg[arg2] = state.reg[arg0] + state.reg[arg1];
}
}
else if (opcode == NAND)
{
state.reg[arg2] = ~(state.reg[arg0] & state.reg[arg1]);
}
else if (opcode == LW)
{
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];
// printf("opcode = %d\n", opcode);
// printf("state.reg[arg0] = %d\n", state.reg[arg0]);
// printf("arg0 = %d\n", arg0);
// printf("arg1 = %d\n", arg1);
// printf("Adress field = %d\n", addressField);
//printf("state.mem[state.reg[arg0] + addressField] = %d\n", arg0+addressField);
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 == PUSH)
{
if (state.stack.size() > 32)
{
printf("Out of Stack\n");
exit(1);
}
state.stack.push(state.reg[1]);
}
else if (opcode == POP)
{
if(state.stack.empty())
{
printf("Stack is Empty\n");
exit(1);
}
state.reg[1] = state.stack.top();
state.stack.pop();
}
else if (opcode == HALT)
{
printf("machine halted\n");
printf("total of %d instructions executed\n", instructions + 1);
printf("final state of machine:\n");
printState(&state);
exit(0);
}
else if (opcode == IDIV)
{
if (long(state.reg[arg0]) / long(state.reg[arg1]) <= MY_MIN ||
long(state.reg[arg0]) / long(state.reg[arg1]) >= MY_MAX)
{
state.reg[arg2] = abs(state.reg[arg0] / state.reg[arg1]);
}
}
else if (opcode == XADD)
{
state.reg[arg2] = state.reg[arg0] + state.reg[arg1];
const int temp = state.reg[arg0];
state.reg[arg0] = state.reg[arg1];
state.reg[arg1] = temp;
}
else if (opcode == SHL)
{
state.reg[arg2] = state.reg[arg0] << state.reg[arg1];
}
else if (opcode == OR)
{
state.reg[arg2] = state.reg[arg0] | state.reg[arg1];
}
else if (opcode == NEG)
{
state.reg[arg1] = -state.reg[arg0];
}
else if (opcode == DEC)
{
state.reg[arg0] -= 1;
}
else if (opcode == JMA)
{
if (abs(state.reg[arg0]) > abs(state.reg[arg1]))
{
state.pc += addressField;
}
}
else if (opcode == JMNE)
{
if (state.reg[arg0] != state.reg[arg1])
{
state.pc += addressField;
}
}
else if (opcode == BSR)
{
bool exist = false;
for (int k = 23; k >= 0; k--)
{
if ((state.reg[arg0] & (1 << k)) >> k)
{
state.reg[arg1] = k;
exist = true;
break;
}
}
state.zf = exist;
}
else if (opcode == CMP)
{
if (state.reg[arg0] == state.reg[arg1]){
state.zf = true;
} else {
state.zf = false;
}
}
else if (opcode == JE)
{
if (!state.zf)
{
state.pc = addressField;
}
}
else if (opcode == SADD)
{
if (state.stack.size() < 2)
{
printf("Error, Void Stack");
exit(3);
}
int temp = state.stack.top();
state.stack.pop();
if(long(temp) + long(state.stack.top()) <= MY_MIN ||
long(temp) + long(state.stack.top()) >= MY_MAX)
{
temp = 0;
printf("Digit Overflow in pc=%d\n", state.pc);
}
else {
temp += state.stack.top();
}
state.stack.pop();
state.stack.push(temp);
}
else if (opcode == SAND)
{
if (state.stack.size() < 2)
{
printf("Error, Void Stack");
exit(3);
}
int temp = state.stack.top();
state.stack.pop();
temp = (temp & state.stack.top());
state.stack.pop();
state.stack.push(temp);
}
else
{
printf("error: illegal opcode 0x%x\n", opcode);
exit(1);
}
state.reg[0] = 0;
}
}
void printState(stateType* statePtr)
{
int i;
printf("\n@@@\nstate:\n");
printf("\tpc %d\n", statePtr->pc);
printf("\tnext instruction %d\n", statePtr->mem[statePtr->pc] >> 24);
printf("\tmemory:\n");
for (i = 0; i < statePtr->numMemory; i++)
{
printf("\t\tmem[ %d ] %d\n", i, statePtr->mem[i]);
}
printf("\tregisters:\n");
for (i = 0; i < NUMREGS; i++)
{
printf("\t\treg[ %d ] %d\n", i, statePtr->reg[i]);
}
printf("\tstack:\n");
showStack(statePtr->stack);
printf("\tzf %d\n", statePtr->zf);
printf("end state\n");
}
int convertNum(int num)
{
/* convert a 16-bit number into a 32-bit Sun integer */
if (num & (1 << 15))
{
num -= (1 << 16);
}
return (num);
}
void showStack(std::stack <int> s)
{
while (!s.empty())
{
printf("\t\t%d\n", s.top());
s.pop();
}
}
Тести програм
Програма ADD
lw 0 1 res
lw 0 2 arg
start add 1 2 2
halt
arg .fill 25
res .fill 10
-----------------------------
memory[0]=790533
memory[1]=794628
memory[2]=303106
memory[3]=0
memory[4]=25
memory[5]=10
…
machine halted
total of 4 instructions executed
final state of machine:
@@@
state:
pc 4
next instruction 0
memory:
mem[ 0 ] 790533
mem[ 1 ] 794628
mem[ 2 ] 303106
mem[ 3 ] 0
mem[ 4 ] 25
mem[ 5 ] 10
registers:
reg[ 0 ] 0
reg[ 1 ] 10
reg[ 2 ] 35
reg[ 3 ] 0
reg[ 4 ] 0
reg[ 5 ] 0
reg[ 6 ] 0
reg[ 7 ] 0
stack:
zf 0
end state
Програма OR
lw 0 1 a1
lw 0 2 a2
start or 1 2 3
halt
a1 .fill 53 110101
a2 .fill 51 110011
res .fill 0 110111 =55
-----------------------------
machine halted
total of 4 instructions executed
final state of machine:
@@@
state:
pc 4
next instruction 0
memory:
mem[ 0 ] 790532
mem[ 1 ] 794629
mem[ 2 ] 2400259
mem[ 3 ] 0
mem[ 4 ] 53
mem[ 5 ] 51
mem[ 6 ] 0
registers:
reg[ 0 ] 0
reg[ 1 ] 53
reg[ 2 ] 51
reg[ 3 ] 55
reg[ 4 ] 0
reg[ 5 ] 0
reg[ 6 ] 0
reg[ 7 ] 0
stack:
zf 0
end stateПрограма IDIV та SW
lw 0 1 a1
lw 0 2 a2
start idiv 1 2 3
sw 0 3 res
halt
a1 .fill -10
a2 .fill 2
res .fill 0
-----------------------------
memory[0]=790533
memory[1]=794630
memory[2]=1613827
memory[3]=1060871
memory[4]=0
memory[5]=-10
memory[6]=2
memory[7]=0
…
machine halted
total of 5 instructions executed
final state of machine:
@@@
state:
pc 5
next instruction -1
memory:
mem[ 0 ] 790533
mem[ 1 ] 794630
mem[ 2 ] 1613827
mem[ 3 ] 1060871
mem[ 4 ] 0
mem[ 5 ] -10
mem[ 6 ] 2
mem[ 7 ] -5
registers:
reg[ 0 ] 0
reg[ 1 ] -10
reg[ 2 ] 2
reg[ 3 ] -5
reg[ 4 ] 0
reg[ 5 ] 0
reg[ 6 ] 0
reg[ 7 ] 0
stack:
zf 0
end state
Програма XADD
lw 0 1 a1
lw 0 2 a2
start xadd 1 2 3
halt
a1 .fill -10
a2 .fill 2
res .fill 0
-----------------------------
memory[0]=790532
memory[1]=794629
memory[2]=1875971
memory[3]=0
memory[4]=-10
memory[5]=2
memory[6]=0
…
machine halted
total of 4 instructions executed
final state of machine:
@@@
state:
pc 4
next instruction -1
memory:
mem[ 0 ] 790532
mem[ 1 ] 794629
mem[ 2 ] 1875971
mem[ 3 ] 0
mem[ 4 ] -10
mem[ 5 ] 2
mem[ 6 ] 0
registers:
reg[ 0 ] 0
reg[ 1 ] 2
reg[ 2 ] -10
reg[ 3 ] -8
reg[ 4 ] 0
reg[ 5 ] 0
reg[ 6 ] 0
reg[ 7 ] 0
stack:
zf 0
end stateПрограма SHL та NEG
lw 0 1 a1
lw 0 2 a2
start shl 1 2 3
neg 3 4
halt
a1 .fill 10
a2 .fill 2
res .fill 0
-----------------------------
memory[0]=790533
memory[1]=794630
memory[2]=2138115
memory[3]=2736128
memory[4]=0
memory[5]=10
memory[6]=2
memory[7]=0
…
machine halted
total of 5 instructions executed
final state of machine:
@@@
state:
pc 5
next instruction 0
memory:
mem[ 0 ] 790533
mem[ 1 ] 794630
mem[ 2 ] 2138115
mem[ 3 ] 2736128
mem[ 4 ] 0
mem[ 5 ] 10
mem[ 6 ] 2
mem[ 7 ] 0
registers:
reg[ 0 ] 0
reg[ 1 ] 10
reg[ 2 ] 2
reg[ 3 ] 40
reg[ 4 ] -40
reg[ 5 ] 0
reg[ 6 ] 0
reg[ 7 ] 0
stack:
zf 0
end stateПрограма стекових операцій
push a1
push a2
sand
push a2
sadd
pop res
halt
a1 .fill 22 10110
a2 .fill 5 00101
res .fill 0 00100
-----------------------------
memory[0]=3932167
memory[1]=3932168
memory[2]=4456448
memory[3]=3932168
memory[4]=4718592
memory[5]=4194313
memory[6]=0
memory[7]=22
memory[8]=5
memory[9]=0
…
machine halted
total of 7 instructions executed
final state of machine:
@@@
state:
pc 7
next instruction 0
memory:
mem[ 0 ] 3932167
mem[ 1 ] 3932168
mem[ 2 ] 4456448
mem[ 3 ] 3932168
mem[ 4 ] 4718592
mem[ 5 ] 4194313
mem[ 6 ] 0
mem[ 7 ] 22
mem[ 8 ] 5
mem[ 9 ] 0
registers:
reg[ 0 ] 0
reg[ 1 ] 9
reg[ 2 ] 0
reg[ 3 ] 0
reg[ 4 ] 0
reg[ 5 ] 0
reg[ 6 ] 0
reg[ 7 ] 0
stack:
zf 0
end state Програма BSR
lw 0 1 a1
start bsr 1 2
halt
a1 .fill 64039
-----------------------------
machine halted
total of 3 instructions executed
final state of machine:
@@@
state:
pc 3
next instruction 0
memory:
mem[ 0 ] 790531
mem[ 1 ] 3710976
mem[ 2 ] 0
mem[ 3 ] 64039
registers:
reg[ 0 ] 0
reg[ 1 ] 64039
reg[ 2 ] 15
reg[ 3 ] 0
reg[ 4 ] 0
reg[ 5 ] 0
reg[ 6 ] 0
reg[ 7 ] 0
stack:
zf 1
end stateПрограма zf та je (ітераційний цикл)
lw 0 1 i
lw 0 2 end
lw 0 3 res
start add 3 1 3
cmp 1 2
dec 1
je start
halt
i .fill 5
end .fill 0
res .fill 0
-----------------------------
machine halted
total of 28 instructions executed
final state of machine:
@@@
state:
pc 8
next instruction 0
memory:
mem[ 0 ] 790536
mem[ 1 ] 794633
mem[ 2 ] 798730
mem[ 3 ] 364547
mem[ 4 ] 3448832
mem[ 5 ] 1343488
mem[ 6 ] 4980739
mem[ 7 ] 0
mem[ 8 ] 5
mem[ 9 ] 0
mem[ 10 ] 0
registers:
reg[ 0 ] 0
reg[ 1 ] -1
reg[ 2 ] 0
reg[ 3 ] 15
reg[ 4 ] 0
reg[ 5 ] 0
reg[ 6 ] 0
reg[ 7 ] 0
stack:
zf 1
end state