Программная модель вычислительной системы. Эмулятор

  • Вид работы:
    Курсовая работа (т)
  • Предмет:
    Информационное обеспечение, программирование
  • Язык:
    Русский
    ,
    Формат файла:
    MS Word
    129,43 Кб
  • Опубликовано:
    2013-08-07
Вы можете узнать стоимость помощи в написании студенческой работы.
Помощь в написании работы, которую точно примут!

Программная модель вычислительной системы. Эмулятор

Министерство Образования и Науки Украины

Севастопольский Национальный Технический Университет

Кафедра кибернетики и вычислительной техники








Пояснительная записка

к курсовому проекту

«Программная модель вычислительной системы. Эмулятор»

по дисциплине «Программирование»

Выполнил: студент группы М-22д

Маслов В.О.

Проверил: Смагина А.О.







Севастополь 2010

Содержание

Введение

1.  Постановка задачи

2.      Описание входных и выходных данных

.        Разработка алгоритма

.        Разработка эмулятора

.        Отладка и тестирование

Заключение

Библиографический список

Приложение

Введение

В настоящее время в связи с бурным развитием компьютерных технологий, все большее предпочтение среди программистов получают языки высокого уровня; однако, во многих случаях, программы, написанные на таких языках, теряют в быстродействии по сравнению с машинно-ориентированными языками (ассемблерами), а размер программы, написанной на языке высокого уровня почти всегда превышает (причем значительно) размер программы, написанной на ассемблере. Таким образом, для написания быстродействующих компактных программ (например, драйверов, компьютерных вирусов, резидентных программ и т. п.) следует использовать языки низкого уровня. Несмотря на развитие языков программирования высокого уровня, ассемблер нельзя назвать мертвым, изжившим себя языком, так как каким бы хорошим ни был оптимизирующий компилятор, он не заменит хорошего программиста на ассемблере; более того, ассемблер часто используется именно там, где необходимо обеспечить максимальное быстродействие или работу на «машинном» уровне.

Эмулятор - это программа, позволяющая запускать и отлаживать программы, созданные для оборудования, отличного от стандартного ПК. Отладчик содержит в своём составе процедуры моделирования операций, выполняемых в ходе функционирования.

1.     
Постановка задачи

Целью разрабатываемого курсового проекта, является изучение внутреннего устройства ЭВМ с точки зрения программиста. В процессе выполнения проекта необходимо создать кросс-эмулятор - программную модель ЭВМ с заданной архитектурой.

Задана структура микропроцессорной системы (№3):


Объем оперативной памяти - 4096 байт.

Команды имеют длину один или два байта. Биты 0-3 первого байта содержат код команды в соответствии с таблицей команд. Биты 4-7 и второй байт используются для здания адреса памяти, где находится операнд или константа.

Арифметические и логические операции выполняются над операндом, находящемся в памяти и верхним элементом стека. Результат выполнения операции в память.

Список команд для структуры №3 :


В соответствии с заданием, длина объектного кода команды составляет один или два байта, формат команды схематически изображен на следующем рисунке:

0

1

2

3

4

5

6

7


0

1

2

3

4

5

6

7

Код операции

Адрес


Адрес

Байт № 1


Байт № 2

Рисунок 1 - изображение формата команды

2.      Описание входных и выходных данных

Входные данные

Входными данными для эмулятора является объектный файл, сгенерированный ассемблером. Объектный файл содержит программу и указания эмулятору, как необходимо ее загружать. Формат объектного файла «признак-байт».

Программа на ассемблере помимо команд процессора содержит также две специальные директивы: ORG - указание на адрес в памяти, куда необходимо загружать последующие команды, и DATA - задание константы. Чтобы эмулятор мог отличить их друг от друга, каждая команда имеет свой байт-признак, указывающий ее тип:

- адрес; ADDRESS

- команда; COMMAND

- данные; DATA

Например: 01 10 02 E5 02 23 01 20 03 D2 означает, что начиная с адреса 10 идут 2 команды E5 23, а затем с адреса 20 идут данные D2.

Выходные данные

Выходными данными является вывод в окно терминала текущего состояния элементов стека(R0, R1, R2, R3, R4, R5, R6, R7), PC(счетчик адреса команд), rgCOM(регистр команд), значение флагов(ноль, отрицательный, переполнение) и текущее состояния ячеек оперативной памяти.

3.      Разработка алгоритма

1.       Открываем поток чтения из объектного файла, сгенерированного транслятором.

2.      В цикле читаем из файла по два байта. В зависимости от первого байта, который является либо признаком адреса, либо признаком данных, второй байт используем по назначению: если первый байт является признаком адреса, то начинаем с запоминания этого адреса, а если первый байт является признаком данных, то помещаем в текущую ячейку памяти.

.        Выбираем из памяти команду, адрес которой хранится в счетчике адреса команд.

.        Анализируем код команды и определяем количество байтов в команде. Читаем из памяти операнды - исходные данные.

.        Выполняем операцию и при необходимости формируем признаки результата, фиксируя их в регистре флагов.

.        Изменяем значение в счетчике адреса команд, готовясь к выполнению следующей команды.

.        Выводим на экран результат работы программы.

4.      Разработка эмулятора

микропроцессорный память программа эмулятор

Создаем пять классов: Main, Processor, ALU, Memory и Stack.

Рассмотрим подробно каждый класс.

·        Класс Processor включает в себя:

int PC - СЧАК: указывает на адрес следующей команды. rgCOM - регистр команд: хранит код текущей команды.

Флаги результата int zero(ноль), int overflow(переполнение), int negative

(отрицательный).

Stack - структура данных с методом доступа к элементам LIFO(последним пришел- первым вышел), стек состоит из восьми элементов(R0, R1, R2, R3, R4, R5, R6, R7).- переменная которая сообщает об ошибке(если такая имеется).

Эта переменная может принимать такие значения:

·        0 - ошибок нет

·        1 - переполнение стека

·        2 - неизвестная команда

·        3 - неизвестный байт-признак

·        4 - данные в начале файла

·        5 - после данных команда или адрес

·        6 - невозможно загрузить файл

Методы класса Processor:

public Processor(Memory ram) - связывает процессор с памятью.

private void clear() - сбрасывает все значения элементов в процессоре и памяти.void execute() - метод выполняет всю программу: пока не достигнут конец программы.void executeOneCom() - выполняет одну команду.

private boolean twoBytes(int c) - определяет сколько байт в команде.

public boolean loadObjectFile(DataInput f) - считывает указанный объектный файл, загружает программу в память и устанавливает адрес последней команды (это необходимо сделать, т. к. иначе нельзя определить во время выполнения программы когда достигнут ее конец). В случае успешной загрузки он возвращает true и в служебную информацию вписывает сообщение об этом и адрес последней команды. Иначе возвращает false и устанавливает сообщение с указанным номером ошибки.String toString() - метод выводящий в терминал состояние стека, регистра команд, СЧАКа и значение флагов - после выполнения каждой команды.

Алгоритм загрузки программы в память:

flag - предыдущий признак, i - индекс следующей ячейки памяти, m - признак байта, b - байт

1.      Прочитать 2 байта программы если они есть, иначе конец. m = признак байта, b = байт.

2.      Если m = адрес, то:

a.       если это начало программы (flag = 0), то установить счетчик команд в b

b.      если flag = данные, вернуть сообщение об ошибке, конец

c.       flag ← адрес, i ← b.     переход к п. 1

3.      Если m = команда, то:

4.      если flag - данные, вернуть сообщение об ошибке, конец

a.       flag ← команда, конец программы ← i+1

b.      переход к п.6

5.      Если m = данные, то:

a.       если начало программы, то вернуть ошибку

b.      flag ← данные.    переход к п.6

6.      m неизвестно, вернуть сообщение об ошибке

7.      записать в ячейку i байт b, i ← i+1, переход к п.1

Загрузка следующей команды происходит следующим образом: в регистр команды загружается команда из памяти по адресу, указанному в счетчике адреса команды, который затем увеличивается на единицу. В зависимости от кода команды принимается решение о том, следует ли загружать второй байт из памяти и помещать его в регистр операнда АЛУ.

·        Класс ALU включает в себя:

АЛУ реализовано как отдельный класс. В АЛУ содержатся два метода:

public int execute(int com) - определяет что за команда(по коду операции), и выполняет её.

private void setFlags(int r) - вызывает метод проверки флагов результата:

переполнение: если r < 0 или r > 255 то 1(переполнение есть) иначе 0 (нет):

отрицательный: если r < 128 то 1(отрицательный результат) иначе 0 (нет);

ноль: если r == 128 то 1(результат ноль) иначе 0 (нет).

Рассмотрим подробнее представление чисел в эмуляторе. По условию, ячейка памяти и регистры могут хранить байт информации - 8 бит. То есть можно представить числа от -128 до 127 (т. к. в списке команд есть операция вычитания). Старший бит - знаковый. Для хранения чисел используется тип int.

Реализация команд микропроцессора:

t - результат, reg - регистр АЛУ, proc.overflow - переполнение

Мнемокод

Код

Описание

Реализация

ADD

Адрес

0000

память[адрес]=стек[0]+ память[адрес]

proc.stack.peek()+ram.read(reg); setFlags(t); ram.write(t & 0xFF, reg);

ADC

Адрес

0001

память[адрес]=стек[0]+ память[адрес]+ флаг переполнения

proc.stack.peek()+ram.read(reg)+proc.overflow; setFlags(t); ram.write(t & 0xFF, reg);

SUB

Адрес

память[адрес]=стек[0]-память[адрес]

proc.stack.peek()-ram.read(reg); setFlags(t); ram.write(t & 0xFF, reg);

SUB

Адрес

0011

память[адрес]=стек[0]- память[адрес]- флаг переполнения

proc.stack.peek()-ram.read(reg)-proc.overflow; setFlags(t); ram.write(t & 0xFF, reg);

AND

Адрес

0100

память[адрес]=стек[0]& память[адрес]

proc.stack.peek() & ram.read(reg); setFlags(t); ram.write(t & 0xFF, reg);

OR

Адрес

0101

память[адрес]=стек[0] | память[адрес]

proc.stack.peek() | ram.read(reg); setFlags(t); ram.write(t & 0xFF, reg);

NOT


0110

инверсия бит стек[0]

proc.stack.pop(); t = (~t) & 0xFF; setFlags(t); proc.stack.push(t);

PUSH

Адрес

0111

сдвиг стека; стек[0]=память[адрес]

if(!proc.stack.push(ram.read(reg))) { return 1;

POP

Адрес

1000

сдвиг стека; память[адрес]= стек[0]

ram.write(proc.stack.pop(), reg);

INC

регистр

1001

стек[0]=стек[0]+1

proc.stack.pop(); t++; setFlags(t); proc.stack.push(t);

DEC

регистр

1010

стек[0]=стек[0]-1

proc.stack.pop(); t--; setFlags(t); proc.stack.push(t);

JMP

Адрес

1011

безусловный переход

proc.PC = reg;

JNZ

Адрес

1101

переход, если не 0

if(proc.zero == 0) proc.PC = reg;

САLL

Адрес

1110

переход к подпрограмме; адрес возврата сохраняется в регистре;

proc.PC-2; proc.PC = reg; if(!(proc.stack.push(t & 0xFF) && proc.stack.push(t &0xF00))) { return 1;

RET


1111

возврат из подпрограммы; адрес возврата находится в регистре;

int t1 = proc.stack.pop(); int t2 = proc.stack.pop(); t = t1 & t2; proc.PC = t;

Таблица 1. Команды

·        Класс Stack включает в себя методы для работы со стеком, а именно:

public Stack(int n) - по умолчанию все ячейки стека заполняются значением равным -1(не определено).int peek() - метод позволяющий узнать значение верхнего(нулевого) элемента стека.

public boolean push(int x) - позволяет добавить элемент в стек: для этого необходимо все элементы стека передвинуть - i-ый элемент передвигается в право на i+1, а на место нулевого добавляется нужный элемент.int pop() - позволяет вытащить(удалить) нулевой элемент стека, при этом все оставшиеся элементы сдвигаются на одну ячейку в лево.

·        Класс Memory включает в себя:

Память представлена в виде массива целых: индекс - это адрес ячейки(addr), элемент - ее содержимое(data).

Метод public Memory() - метод создающий массив целых 4096 элементов.int size() - возвращает размер памяти.

public int read(int addr) - чтение адреса памяти.

public void write(int data, int addr) - запись данных в адрес памяти.void clear() - обнуляет значение ячеек памяти.String toString() - представление памяти как строк.

Память представлена в виде массива целых: индекс - это адрес ячейки(addr), элемент - ее содержимое(data).

Для записи программы в бинарный файл используется метод public static void writeprog() .

5. Отладка и тестирование

Для тестирования программы написан пример программы, в которой используются команды, работающие со стеком, памятью, а также специальные команды ORG и DATA.

Тестовый пример:2

PUSH 100

ADD 101

PUSH 100128 101

ORG 2

DATA 170160

Содержимое объектного файла программы из листинга тестового примера:

02 02 70 02 64 02 00 02 65 02 70 02 65 02 80 02 80 02 00 02 64 01 64 03 AA 03 A0

Выполним программу вручную:

Начиная с адреса 2 в ячейки памяти будут заносится такие адреса: 70 64 0 65

Приложение А:

Листинг класса Processor:

import java.io.*;

import java.util.Arrays;

public class Processor {

ALU alu;

Memory ram;stack;

public int PC; // program counter указывает на адрес след. команды

public int rgCOM; // command register хранит код текущей командыend; // конец программыzero;overflow;

int negative;

/*

* 0 - ошибок нет

* 1 - переполнение стека

* 2 - неизвестная команда

* 3 - неизвестный байт-признак

* 4 - данные в начале файла

* 5 - после данных команда или адрес

* 6 - невозможно загрузить файл

*/error;Processor(Memory ram) {

this.ram = ram;();

}

/*

* сброс процессора и памяти

*/void clear() {= new Stack(8);= 0;= 0;= 0;= 0;= 0;= 0;.clear();= new ALU(ram, this);

}void execute() {(PC != end) executeOneCom();

}void executeOneCom() {(error == 0) {first = ram.read(PC);= first >> 4;++;(twoBytes(rgCOM)) {

// 0xF00 = 1111 0000 0000 (2 система).reg = (((first << 8) & 0xF00) | ram.read(PC));++;

}= alu.execute(rgCOM);(error != 0) {.out.println("Возникла ошибка: код ошибки " + error);

} else {.out.println(this);

}

}

}boolean twoBytes(int c) {(c == 6 || c == 15 || c == 9 || c == 10) {false;

} else {true;

}

}boolean loadObjectFile(DataInput f) {();int ADDRESS = 1;int COMMAND = 2;int DATA = 3;i = 0; // индекс следующей ячейки памятиflag = 0;

int m; // признак байтаb; // байт

while(true){= f.readInt();= f.readInt();(m == ADDRESS) {(flag == 0) PC = b;(flag == DATA) {= 5;false;

}= ADDRESS;= b;

} else {(m == COMMAND) {(flag == DATA) {= 5;false;

}= COMMAND;= i+1;

} else if(m == DATA) {(flag == 0) {= 4;false;

}= DATA;

} else {= 3;false;

}.write(b, i);++;

}

} catch(EOFException e) {

return true;

} catch(IOException e) {= 6;false;

}

}String toString() {s = new StringBuilder();.append(String.format("stack:%s\n", Arrays.toString(this.stack.s)));.append(String.format("PC:%x\n", this.PC));.append(String.format("rgCOM:%x\n", this.rgCOM));

s.append(String.format("ноль отрицательный переполнение:%d %d %d\n",

this.zero,.negative,.overflow));

return s.toString();

}

}

Листинг класса ALU:

public class ALU {reg;ram;proc;ALU(Memory ram, Processor proc) {.ram = ram;.proc = proc;

/*

* 0 - все хорошо

* 1 - переполнение стека

* 2 - неизвестная команда

*/int execute(int com) {t;(com) {

//ADD0:= proc.stack.peek()+ram.read(reg);(t);.write(t & 0xFF, reg);;

//ADC1:= proc.stack.peek()+ram.read(reg)+proc.overflow;(t);.write(t & 0xFF, reg);;

//SUB2:= proc.stack.peek()-ram.read(reg);(t);.write(t & 0xFF, reg);;

//SUB3:= proc.stack.peek()-ram.read(reg)-proc.overflow;(t);.write(t & 0xFF, reg);;

//AND4:= proc.stack.peek() & ram.read(reg);(t);.write(t & 0xFF, reg);;

//OR5:= proc.stack.peek() | ram.read(reg);(t);.write(t & 0xFF, reg);;

//NOT6:= proc.stack.pop();= (~t) & 0xFF;(t);.stack.push(t);;

//PUSH7:(!proc.stack.push(ram.read(reg))) {1;

};

//POP8:.write(proc.stack.pop(), reg);;

//INC9:= proc.stack.pop();++;(t);.stack.push(t);;

//DEC10:= proc.stack.pop();-;(t);.stack.push(t);;

//JMP11:.PC = reg;;

//JNZ13:(proc.zero == 0) proc.PC = reg;

break;

//CALL

case 14:= proc.PC-2;.PC = reg;(!(proc.stack.push(t & 0xFF) &&.stack.push(t & 0xF00))) {1;

};

//RET15:t1 = proc.stack.pop();t2 = proc.stack.pop();= t1 & t2;.PC = t;;:2;

}0;

}void setFlags(int r) {.overflow = (r < 0 || r > 255) ? 1 : 0;.negative = (r < 128) ? 1 : 0;.zero = (r == 128) ? 1 : 0;

}

}

Листинг класса Stack:

public class Stack {[] s;max;size;Stack(int n) {= new int[n];(int i = 0; i < n; ++i) {[i] = -1;= n;= 0;

}

}

/*

* значение верхнего элемента

*/int peek() {s[0];

}

/*

* добавить элемент в стек

*/boolean push(int x) {(size+1 > max) return false; // переполнение(int i = size; i > 0; i--) {[i] = s[i-1];

}[0] = x;++;

return true;

}

/*

* вытащить верхний элемент

*/

public int pop() {t = s[0];(int i = 1; i < max; i++) {[i-1] = s[i];

}-;t;

}

}

Листинг класса Memory:

class Memory {int[] ram;Memory() {= new int[4096];

}int size() {ram.length;

}int read(int addr) {ram[addr];

}void write(int data, int addr) {[addr] = data;

}void clear() {(int i = 0; i < ram.length; ++i) {[i] = 0;

}

}String toString() {b = new StringBuilder();c = 128;(int i = 0, k = 0; i < c; i++) {j = 0;((k = i + j*c) < ram.length) {.append(String.format("%2h", k));.append(":");.append(String.format("%2h", ram[k]));.append(" ");++;

}.append("\n");

}b.toString();

}

}

Похожие работы на - Программная модель вычислительной системы. Эмулятор

 

Не нашли материал для своей работы?
Поможем написать уникальную работу
Без плагиата!