15.5.1 Введение
Под виртуальной машиной (ВМ) будем подразумевать программную реализацию модели
некоторой вычислительной системы. Практически все известные системы
программирования можно рассматривать как виртуальные машины, поскольку часть
операций моделируются, т.е. для них написан программный код. При построении ВМ
необходимо рассмотреть следующие вопросы:
- структура ВМ;
- множество элементарных данных;
- множество элементарных операций;
- управление последовательностью действий;
- управление данными;
- управление памяти .
Структура ВМ определяется наличием интерпретатора команд и специальных блоков
памяти, где хранятся данные, программы и вспомогательная информация. Множество
элементарных данных и операций определяется из анализа состава функций и задач
некоторой предметной области. Управление последовательностью действий включает
два аспекта: управление последовательностью операций и управление вызова
подпрограмм. Вопросы управления данными подразумевает рассмотрение способов
хранения и передачи данных между подпрограммами.
При создании вычислительного процесса данные могут накапливаться, изменяться и
удаляться, поэтому необходимо рассмотреть также вопросы управления памятью.
При построении ВМ для данной предметной области необходимо иметь состав функций,
который может быть выявлен в ходе проведения системного анализа с целью
построения модели предметной области. Если этот состав конечен и полон,
15.5.3 Реализация простой виртуальной машины
#define SIZEMEMORY
256 //размер памяти под данные
#define DEBUG_SVM//работа
программы в отладочном режиме
int Mem[SIZEMEMORY];
//память по данные
Входные параметры :
prog - массив, содержащий программу (последовательность команд).
Mem - массив, содержащий данные.
Основные переменные:
com - код команды;
Addr, Addr2, Addr3 - вспомогательные переменные для хранения операндов текущей
команды.
flag - переменная, хранящая флаг перехода.
pc - счетчик команд.
int SimpleVirtualMachine(unsigned char
*prog,int Mem[])
{
int com,
//код команды
Addr,Addr2,Addr3,
//вспомогательные ячейки хранения адресов
flag=0, // флаг перехода
pc=0;
//счетчик команд
(program counter)
while(1) //цикл выполнения программы
{
#ifdef DEBUG_SVM //для отладки
PrintMemory();
PrintCommand(&prog[pc]);
if(getch()==27) exit(0);
//если нажата ESC завершить программу
#endif
com=prog[pc]; //выделяем код операции
switch (com)
{
case IN: //добавляем
1, т.к.
вначале
pc
//
указывает на
IN, а нам необходимо само число
Addr=prog[pc+1]; //ввод числа
printf("svm_input>");
scanf("%d",&Mem[Addr]);
pc=pc+2; //смещаем счетчик команд
break;
case OUT: //вывод числа
Addr=prog[pc+1];
printf("svm_ouput>%d\n",Mem[Addr]);
pc=pc+2; //смещаем счетчик команд
break;
case ADD: //сложить два числа
Addr=prog[pc+1];
Addr2=prog[pc+2];
//производим сложение и результат записываем по адресу первого элемента
Mem[Addr]=Mem[Addr]+Mem[Addr2];
pc=pc+3;
break;
case SUB: //вычитание
Addr=prog[pc+1];
Addr2=prog[pc+2];
Mem[Addr]=Mem[Addr]-Mem[Addr2];
pc=pc+3;
break;
case MULT: //умножение
Addr=prog[pc+1];
Addr2=prog[pc+2];
Mem[Addr]=Mem[Addr]*Mem[Addr2];
pc=pc+3;
break;
case DIV: //деление
Addr=prog[pc+1];
Addr2=prog[pc+2];
Mem[Addr]=Mem[Addr]/Mem[Addr2];
pc=pc+3;
break;
case JUMP: // безусловный переход
pc=prog[pc+1];
break;
case TEST: //проверка на ноль и установка флага
Addr=prog[pc+1];
if (Mem[Addr]==0) flag=1;
else flag=0;
pc=pc+2;
break;
case GRT: //проверка
a>b
Addr=prog[pc+1];
Addr2=prog[pc+2];
if (Mem[Addr]>Mem[Addr2])
flag=1; //установка флага
else flag=0; //сброс флага
pc=pc+3;
break;
case LST: //проверка
a < b
Addr=prog[pc+1];
Addr2=prog[pc+2];
if (Mem[Addr] <
Mem[Addr2])
flag=1;
else
flag=0;
pc=pc+3;
break;
case BRA: //условный переход
Addr=prog[pc+1];
if(flag) pc=Addr;
else pc=pc+2;
break;
case INC: //Инкримент
Addr=prog[pc+1];
Mem[Addr]++;
pc=pc+2;
break;
case DEC: //Декримент
Addr=prog[pc+1];
Mem[Addr]++;
pc=pc+2;
break;
case MOV: //переслать данные
Addr=prog[pc+1];
Addr2=prog[pc+2];
Mem[Addr]=Mem[Addr2];
pc=pc+3;
break;
case ILOAD: //косвенная загрузка данных
Addr=prog[pc+1];
Addr2=prog[pc+2];
Addr3=prog[pc+3];
Mem[Addr]=Mem[Addr2+Mem[Addr3]];
pc=pc+4;
break;
case ISAVE: //косвенное сохранение данных
Addr=prog[pc+1];
Addr2=prog[pc+2];
Addr3=prog[pc+3];
Mem[Addr2+Mem[Addr3]]=Mem[Addr];
pc=pc+4;
break;
case STOP:
return 1;
default:
printf("Error command\n");
return - 1;
}
}
}
//печать первых
10 ячеек памяти
void
PrintMemory()
{
printf("dump memory \n");
for(int
i=0; i<10; i++)
printf("%2d %5d\n",i,Mem[i]);
}
//вывод команды на терминал
int
PrintCommand(unsigned char *prog)
{
int Addr,Addr2,Addr3;
int pc=0;
switch (*prog)
{
case IN:
Addr=prog[pc+1]; //ввод числа
printf(" IN %3d\n",Addr);
break;
case OUT: //вывод числа
Addr=prog[pc+1]
printf("OUT %3d\n",Addr);
break;
case ADD: //сложить два числа
Addr=prog[pc+1];
Addr2=prog[pc+2];
printf("ADD %3d, %3d\n",Addr,Addr2);
break;
case SUB: //вычитание
Addr=prog[pc+1];
Addr2=prog[pc+2];
printf("SUB %3d, %3d\n",Addr,Addr2);
break;
case MULT: //умножение
Addr=prog[pc+1];
Addr2=prog[pc+2];
printf("MULT %3d, %3d\n",Addr,Addr2);
break;
case DIV: //деление
Addr=prog[pc+1];
Addr2=prog[pc+2];
printf("DIV %3d, %3d\n",Addr,Addr2);
break;
case JUMP: //безусловный переход
Addr=prog[pc+1]; //ввод числа
printf("JUMP %3d\n",Addr);
break;
case TEST: //проверка на ноль и установка флага
Addr=prog[pc+1]; //ввод числа
printf("TEST %3d\n",Addr);
break;
case GRT: //проверка
a>b
Addr=prog[pc+1];
Addr2=prog[pc+2];
printf("GRT %3d, %3d\n",Addr,Addr2);
break;
case LST: //проверка
a < b
Addr=prog[pc+1];
Addr2=prog[pc+2];
printf("LST %3d, %3d\n",Addr,Addr2);
break;
case BRA: //условный переход
Addr=prog[pc+1];
printf("BRA %3d\n",Addr);
break;
case INC: //Инкремент
Addr=prog[pc+1];
printf("INC %3d\n",Addr);
break;
case DEC: //Декремент
Addr=prog[pc+1];
printf("DEC %3d\n",Addr);
break;
case MOV: //переслать данные
Addr=prog[pc+1];
Addr2=prog[pc+2];
printf("MOV %3d, %3d\n",Addr,Addr2);
break;
case ILOAD: //косвенная загрузка данных
Addr=prog[pc+1];
Addr2=prog[pc+2];
Addr3=prog[pc+3];
printf("ILOAD %3d, %3d, %3d\n",Addr,Addr2,Addr3);
break;
case ISAVE: //косвенное сохранение данных
Addr=prog[pc+1];
Addr2=prog[pc+2];
Addr3=prog[pc+3];
printf("ILOAD %3d, %3d, %3d\n",Addr,Addr2,Addr3);
break;
case STOP:
printf("STOP\n");
return 1;
default:
printf("Error command\n");
return -1;
}
}
//Пример1
//Распределение памяти
#define
X 0
#define Y 1
#define SUM 2
//программа для виртуальной машины
(SVM)
//ввод
x, y; вычисдение
sum=x+y, вывод
sum
unsigned
char prog1[]= {
IN,
X, //ввести значение для
X
IN,
Y, //ввести
y
MOV,
SUM, X, //переслать значение
X в
SUM
ADD,
SUM, Y, //найти сумму
OUT,
SUM, //вывести сумму
STOP
//остановить
};
//Пример2
//распределение памяти
#define
COUNT 0
#define NMAS 1
#define SMAS 2
#define CURE 3
#define MAS 4
#define BEG 0
//программа2
для виртуальной машины
(SVM)
//нахождение суммы элементов массива
//
для(i=0;
i<n; i++) sum=sum+mas[i]; вывод(sum)
unsigned
char prog2[]= {
ILOAD, CURE,MAS,COUNT, //cure=mas[count]
ADD,
SMAS,CURE, //smas=smas+cure
INC,
COUNT, //count=cur+1
LST,
COUNT,NMAS, //count<nmas?
BRA,
BEG, //переход по
BEG
OUT,
SMAS, //вывод суммы(smas)
STOP
//останов
};
void SetMemory()//начальная установка памяти
{
Mem[COUNT]=0; //установка счетчика элементов массива
Mem[NMAS]=50; //количество элементов в массиве
Mem[SMAS]=0; //начальное значение суммы
for(int
i=0; i<50; i++) Mem[MAS+i]=i+1; //начальные значения для массива
}
void main()
{
SimpleVirtualMachine(prog1,Mem); //выполение первой программы
SetMemory();
SimpleVirtualMachine(prog2,Mem); //выполение второй программы
getch();
}