Эмуляторы sega

Эмулятор — это небольшая и лёгкая в использовании программка, при помощи которой на компьютере можно запускать другие программы, изначально сделанные для другой платформы (в данном случае платформы под названием Sega Mega Drive).

Gens Emulator:
На сайте вы сможете скачать эмулятор Gens версии 2.14 для воспроизведения игр Сега на своём компьютере. Gens является абсолютно бесплатной и свободной для распространения программой. Впервые данный эмулятор Sega появился в 1999 году и был признан лучшим в своём классе. На нём воспроизводится около 95% игр, 4% могут работать с небольшими проблемами, и всего 1% игр не работает на данном эмуляторе.

Возможности Сега эмулятора:
— Сохранение на любом моменте
— Игра по сети, автоматический поиск серверов
— Графические фильтры для увеличения качества изображения
— Различные настройки звука, поддержка стерео
— Поддержка дополнительных устройств: джойстик, геймпад
— Выход в полноэкранный режим

Ссылка для скачивания:
скачать эмулятор

Вопросы по настройке Gens:
1) Как запустить игру?
В архиве с эмулятором находим ярлык Gens.exe и запускаем его. Затем в программе выбираем «Файл»—>»Открыть ROM» и далее открываем нужную игру.
2) Как настроить управление?
В меню программы выбираем «Настройки»—>»Джойстики», после чего откроется окошко «Controllers Settings». Затем напротив графы «Player 1» нажимаем «Redefine Keys», у вас должно появиться окно «Dialog» в котором будет находиться схема джойстика Sega. Теперь нажимаем на каждую кнопку джойстика в этом окошке и задаём соответствующие клавиши на клавиатуре. Так к примеру для кнопки «start» можно задать на клавиатуре клавишу «Enter» и т.д. После выбора оптимальных настроек нажимаем «Ок». Вы так же можете подключать к компьютеру другие игровые устройства, настраивая их аналогичным способом.
3) Как улучшить качество изображения?
Когда вы развернете программу Gens на весь экран, то заметите немного некачественное изображение, связано это с тем, что расширение приставки Sega было гораздо меньшего формата, нежели нынешние расширения экранов компьютеров. Но это легко можно исправить, зайдите в меню эмулятора Gens и выберите пункт «Графика»—>»Обработчик»—>»2xSAI» после чего картинка на экране примет гораздо более приятный вид.
4) Как сохранить игру?
Есть два способа сохранения, быстрое и обычное. Быстрое даст вам возможность моментально сохранить и так же быстро загрузить игру. При обычном, вам нужно будет указать название файла и место будущего сейва. Для того чтобы записать игру откройте в Gens пункт «Файл» и затем выберите «Сохранить в файл» или «Быстро сохранить».

Доброго времени суток.

Довольно давно имелось желание написать эмулятор какого-нибудь процессора.
А что может быть лучше, чем изобрести велосипед?

Имя велосипеду — V16, от склеивания слова Virtual и, собственно, разрядности.

С чего начать?

А начать нужно, разумеется, с описания процессора.

В самом начале, я планировал написать эмулятор DCPU-16, но таких чудес на просторах Интернета хватает с лихвой, поэтому я решил остановиться только на «слизывании» самого основного с DCPU-16 1.1.

Архитектура

Память и порты

  • V16 адресует 128Kb (65536 слов) оперативной памяти, которая также может использоваться как буферы устройств и стек.
  • Стек начинается с адреса FFFF, следовательно, RSP имеет стандартное значение 0xFFFF
  • Портов ввода-вывода V16 имеет 256, все они имеют длину в 16 бит. Чтение и запись из них осуществляется через инструкции IN b, a И OUT b, a.

Регистры

V16 имеет два набора регистров общего назначения: основной и альтернативный.
Работать процессор может только с одним набором, поэтому между наборами можно переключаться при помощи инструкции XCR.

Инструкции

Все инструкции имеют максимальную длину в три слова и полностью определяются первым
Первое слово делится на три значения: младший байт — опкод, старший байт в виде двух 4-битных значений — описание операндов.

Прерывания

Прерывания здесь — не более чем таблица с адресами, на которые процессор дублирует инструкцию CALL. Если значение адреса равно нулю, то прерывание не делает ничего, просто обнуляет флаг HF.

Диапазон значений Описание
0x0…0x3 Регистр как значение
0x4…0x7 Регистр как значение по адресу
0x8…0xB Регистр + константа как значение по адресу
0xC Константа как значение по адресу
0xD Константа как значение
0xE Регистр RIP как значение только для чтения
0xF Регистр RSP как значение

Пример псевдокода и слов, в которые все это должно странслироваться:

MOV RAX, 0xABCD ; 350D ABCDMOV , 0x1234 ; 354D 1234

Cycles (Такты)

V16 может выполнять одну инструкцию за 1, 2 или 3 такта. Каждое обращение к оперативной памяти это один отдельный такт. Инструкция это не такт!

Начнем писать!

Реализация основных структур процессора

  1. Набор регистров. Регистров всего четыре, но ситуацию улучшает то, что таких наборов в процессоре целых два. Переключение происходит при помощи инструкции XCR.

    typedef struct Regs {uint16_t rax, rbx; //Primary Accumulator, Base Registeruint16_t rcx, rdx; //Counter Register, Data Register} regs_t;

  2. Флаги. В отличии от DCPU-16, V16 имеет условные переходы, вызовы подпрограмм и возвраты оттуда же. На данный момент процессор имеет 8 флагов, 5 из которых — флаги условий.

    //Чтобы было красиво, нужно включить заголовок stdbool.htypedef struct Flags {bool IF, IR, HF;bool CF, ZF;bool EF, GF, LF;} flags_t;

  3. Собственно, сам процессор. Здесь также описана таблица адресов прерываний, что вполне можно назвать дескрипторами и найти ещё одну отсылку на x86.

    typedef struct CPU {//CPU Valuesuint16_t ram; //Random Access Memoryuint16_t iop; //Input-Output Portsuint16_t idt; //Interrupt vectors table (Interrupt Description Table)flags_t flags; //Flagsregs_t reg_m, reg_a; //Main and Alt register filesregs_t * reg_current; //Current register fileuint16_t rip, rsp, rex; //Internal Registers: Instruction Pointer, Stack Pointer, EXtended Accumulator//Emulator valuesbool reg_swapped; //Is current register file altbool running; //Is cpu runninguint32_t cycles; //RAM access counter} cpu_t;

  4. Операнд. При получении значений, нам необходимо сначала прочитать, затем изменить, а затем записать значение туда, откуда мы его взяли.

    typedef struct Opd {uint8_t code : 4;uint16_t value;uint16_t nextw;} opd_t;

Функции для работы со структурами

Когда все структуры описаны, всплывает необходимость в функциях, которые наделят эти структуры магической силой угашенного кода.

cpu_t * cpu_create(void); //Создаем экземпляр процессораvoid cpu_delete(cpu_t *); //Удаляем экземпляр процессораvoid cpu_load(cpu_t *, const char *); //Загружаем ROM в памятьvoid cpu_rswap(cpu_t *); //Меняем наборы регистровuint16_t cpu_nextw(cpu_t *); //RAM. Nuff saidvoid cpu_getop(cpu_t *, opd_t *, uint8_t); //Читаем операндvoid cpu_setop(cpu_t *, opd_t *, uint16_t); //Пишем операндvoid cpu_tick(cpu_t *); //Выполняем одну инструкциюvoid cpu_loop(cpu_t *); //Выполняем инструкции, пока процессор работает

Также я не упомянул большое перечисление с кодами операций, но это необязательно и необходимо только для понимания, что происходит во всей этой каше.

Функция tick()

Также здесь присутствуют вызовы static-функций, предназначенных только для вызова из tick().

void cpu_tick(cpu_t *cpu){ //Если была выполнена инструкция HLT, то функция ничего не сделает if(cpu->flags.HF) { //Если к тому же обнулен флаг прерываний, то паузу снимать уже нечему if(!cpu->flags.IF) { cpu->running = false; } return; } //Получаем следующее слово и декодируем как инструкцию uint16_t nw = cpu_nextw(cpu); uint8_t op = ((nw >> 8) & 0xFF); uint8_t ob = ((nw >> 4) & 0x0F); uint8_t oa = ((nw >> 0) & 0x0F); //А потому что дизайн кода //Создаем структуры операндов opd_t opdB = { 0 }; opd_t opdA = { 0 }; //И читаем их значения cpu_getop(cpu, &opdB, ob); cpu_getop(cpu, &opdA, oa); //Дальше для сокращения и улучшения читабельности кода делаем переменные-значения операндов uint16_t B = opdB.value; uint16_t A = opdA.value; uint32_t R = 0xFFFFFFFF; //Один очень интересный костыль bool clearf = true; //Будут ли флаги условий чиститься после выполнения инструкции? //И начинаем творить магию! switch(op) { //Здесь мы проходим все возможные опкоды. Те, которые пишут результаты, меняют значение переменной R } //Чистим флаги условий if(clearf) { cpu->flags.EF = false; cpu->flags.GF = false; cpu->flags.LF = false; } //Очень интересный костыль, максимальное 32-битное значение при 16-битных операциях // равно 0xFFFF0000, то есть 0xFFFF << 16 // А поэтому очень удобно для результата использовать 32-битное число if(R != 0xFFFFFFFF) { cpu_setop(cpu, &opdB, (R & 0xFFFF)); cpu->rex = ((R >> 16) & 0xFFFF); cpu->flags.CF = (cpu->rex != 0); cpu->flags.ZF = (R == 0); } return;}

Что делать дальше?

В попытках найти ответ на сей вопрос, я раз пять переписал эмулятор с C на C++, и обратно.

Однако главные цели можно выделить уже сейчас:

  • Прикрутить нормальные прерывания (Вместо простого вызова функции и запрета на прием других прерываний сделать вызов функции и добавление новых прерываний в очередь).
  • Прикрутить устройства, а также способы общения с ними, благо опкодов может быть 256.
  • Научить себя не писать всякую ересь на хабр процессор работать с определенной тактовой частотой в 200 МГц.

Надеюсь, что кому-нибудь эта «статья» станет полезной, кого то подтолкнет на написание чего-то похожего.

Мои куличики можно посмотреть на github.

Также, о ужас, у меня есть ассемблер для старой версии этого эмулятора (Нет, даже не пытайтесь, эмулятор как минимум пожалуется на неправильный формат ROM)

Оставить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *