вытягиевает наш процессор из памяти байт, он равен 7
получаем адрес функции из массива p = flist[7]
вызываем эту функцию p();
вытягиевает наш процессор из памяти байт, он равен 3
получаем адрес функции из массива p = flist[3]
вызываем эту функцию p();
вытягиевает наш процессор из памяти байт, он равен 5
получаем адрес функции из массива p = flist[5]
вызываем эту функцию p();
-------
3.
у процессора должны быть регистры(как минимум), будет он выглядеть примерно так:
Код:
//придумаем виртуальный процессор + вспомогательный функционал
class processor
{
public:
//регистры
DWORD reg_A;
DWORD reg_B;
DWORD reg_C;
DWORD reg_D;
DWORD reg_E;
//регистр флагов
DWORD zFlag;
//указатель на инструкцию
DWORD * Ip;
bool execute() //выполнить 1 инструкцию
{
if (*Ip>=100)//навешиваем проверочки, типа не быдлокодеры нифига
{
printf("error1, кривая инструкция\n");
return false;
};
bool (*p)(processor*) = (bool (__cdecl *)(processor *))(flist[*Ip]);//достаем адресок функции из массива
if (p == 0)//проверяем
{
printf("error2, неизвесная инструкция\n");
return false;
};
if( p(this)==false) //вызываем функцию
{
printf("error3, внутренняя ошибка\n");
return false;
};
return true;
};
void run(DWORD * code)
{
Ip = code;
while (execute());//выполнять до любой ошибки
};
void printreg()
{
printf("%p A=%x B=%x C=%x D=%x E=%x\n",Ip,reg_A,reg_B,reg_C,reg_D,reg_E);
};
} vcpu;
4.
пишем код который будет выполнять виртуальные инструкции.
например мы хотим инструкцию которая обнуляет регистр A
Код:
bool fx_zero_regA(processor * p)
{
//переводим указатель Ip на следующую виртуальную инструкцию
p->Ip+=1; //если размер нашей инструкции 5 байт, значит делаем +5
//обнуляем регистр А
p->regA=0;
return true;
};
//по такому же принципу пишем все нужные нам функции
bool f0_ERROR(processor * p);
bool f1_MOVE_regreg(processor * p);
bool f2_MOVE_regdata(processor * p);
bool f3_ADD_regreg(processor * p);
.............
.......
...
5.
теперь начинается веселая часть
Код:
/*
пишем софтину для нашего ололо процессора,
вычисляет площадь прямоугольника
входящие данные в регистрах A и B
результат в регистре С
*/
DWORD testprogram[]=
{
MOVE_regdata, regA, 100, //длинна
MOVE_regdata, regB, 50, //ширина
MOVE_regdata, regC, 0, //результат
MOVE_regreg, regD, regB, //"временная переменная"
//пичалька, проц не поддерживает умножение, тогда нужно сложение в цикле
MOVE_regreg, regE, regIp, //запоминаем куда нам вернутся
ADD_regreg, regC,regA, //добавляем длинну к результату
SUB_regdata, regD,1,
JNZ_reg, regE, //прыгаем "куда запоминали"
ERROR
};
6.
обнуляем все что можно
Код:
memset( &vcpu,0,sizeof(vcpu));
заполняем массив функций
Код:
flist[0]=f0_ERROR; //расставляем функции согласно их "опкодам"
flist[1]=f1_MOVE_regreg;
flist[2]=f2_MOVE_regdata;
......
...
..
командуем виртуальному процессору выполнять виртуальную программу
Вроде бы на втором курсе в институте писали свой виртуальный процессор и ассемблер, компилирующий исходные коды в "бинарные" файлы для исполнения. Процессор должен был поддерживать заданный набор инструкций (в том числе условные переходы), иметь несколько регистров, работать со стеком. Ассемблер поддерживал метки, переменные нескольких типов. А потом под свой "процессор" (у всех были те или иные различия как в наборе инструкций, так и в базовых вещах, например количестве поддерживаемых операндов) писали демонстрационные примеры (например, вычисление факториала). Было интересно
А ближе к последним курсам уже проектировали реальные счетные устройства из элементарных логических блоков и триггеров: на входы подаешь операнды в двоичном коде, код операции (сложение, умножение и т. д.), а на выходах через несколько тактов получаешь результат. Взглянуть бы сейчас на эти схемы (может где на жестком валяется проект в ладе или протеусе).
Последний раз редактировалось Hint, 12.11.2011 в 02:30.
Вообще в самый раз Это ведь не на неделю задание. По-моему даже на целый семестр. И по сути работа состоит из независимых "модулей", которые можно разрабатывать отдельно (парсинг текстовых файлов, класс работы с хеш-таблицами и т. д.). Лично мне было интересно этим заниматься, хотя большинство конечно передирало работы прошлых лет. Во-первых, практика программирования, во-вторых, знакомство с устройством компьютера на всех уровнях (чем отличается двухпроходный ассемблер от однопроходного и т. д.).
Вот дальше было сложнее, когда пришлось разрабатывать язык более высокого уровня =) Различные циклы (for, while), блоки инструкций, условные операторы и т. д. Правда это уже писалось не с нуля, а с использование лексичесих и синтаксических анализаторов (lex, yacc). Есть что вспомнить http://ru.wikipedia.org/wiki/Yacc http://ru.wikipedia.org/wiki/Lex
Кстати, эти инструменты могут пригодиться и в реальных проектах (более приземленных). Например, для парсинга серверных скриптов lineage 2 (именно они и используются в l2server). Регулярками ведь те же npcdata и itemdata не разберешь.
Добавлено через 2 минуты
Цитата:
Сообщение от destructor
Hint, гдето на "радиотехнике" учился?
Официально называется "Вычислительные машины, комплексы, системы и сети" (специальность 230101).
Последний раз редактировалось Hint, 12.11.2011 в 02:57.
Причина: Добавлено сообщение
Тут самое главное прально продумать архитекуру опкодов и написать какоенеть подобие компилятора. (ох ненавижу я весь этот парсинг и проверку синтаксиса)