エミュレータのコア部分は、必ずしもプラットフォームに依存する必要はなくて、Cの標準ライブラリやC++のSTLだけで実装できるものです。
何処までがプラットフォーム非依存(PIL)で、何処からがプラットフォーム依存(PDL)か。
その線引は、以下のようになります。
【プラットフォーム非依存】(PIL)
・CPUなどのエミュレーション全般
・プラットフォーム依存(PDL)間のブリッジ
【プラットフォーム依存】(PDL)
・映像出力
・音声出力
・入力機器からの入力
コードで書いた方が分かり易いので、LaiNESというファミコンエミュレータの実装をベースに見てみると分かり易いです。
オリジナルのLaiNESは、PIL と PDL が若干入り乱れているので、純粋な PIL だけを切り離した LaiNES を私の方で作ってみました。
https://github.com/suzukiplan/LaiNES
上記のSUZUKI PLANカスタム版LaiNESは純粋なPILのみ実装している キレイなエミュレータ なので、ターミナル上でファミコンを動かすこともできてしまいます。上記リポジトリのtest.cppが実際にターミナル上でファミコンを動かすプログラムの例です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// this is a dummy code for checking the linkage | |
#include <stdio.h> | |
#include "cartridge.hpp" | |
#include "apu.hpp" | |
#include "cpu.hpp" | |
#include "hal.hpp" | |
int main(int argc, char* argv[]) { | |
if (argc < 2) { | |
puts("usage: a.out rom_file"); | |
return 1; | |
} | |
// note: need initialize APU before load rom | |
APU::init(); | |
// load rom file | |
puts("loading"); | |
FILE* f = fopen(argv[1], "rb"); | |
if (!f) { | |
puts("file not found"); | |
return 2; | |
} | |
fseek(f, 0, SEEK_END); | |
int size = ftell(f); | |
fseek(f, 0, SEEK_SET); | |
u8* rom = new u8[size]; | |
fread(rom, size, 1, f); | |
fclose(f); | |
Cartridge::load(rom, size); | |
if (!Cartridge::loaded()) { | |
puts("load error"); | |
return 2; | |
} | |
// execute 60 frame | |
for (int i = 0; i < 60; i++) { | |
puts("execute 1 frame"); | |
CPU::run_frame(); | |
} | |
return 0; | |
} | |
// these function will callbacked by CPU (CPU::run_frame function) | |
namespace HAL { | |
u8 get_joypad_state(int n) { | |
printf("get_joypad_state: %d\n", n); | |
return 0; // return joypad state | |
} | |
void new_frame(u16* pixels) { | |
// set pixels to graphic-module | |
printf("new_frame: %p\n", pixels); | |
} | |
void new_samples(const blip_sample_t* samples, size_t count) { | |
// set samples to sound-module | |
printf("new_samples: %p, %ld\n", samples, count); | |
} | |
} |
ファミコンを動かすといっても、ターミナル上でPDL(の全て)を再現するのは厳しいので、PDL部分(namespace HALの部分の実装)はダミーですが。
それだとあんまりなので、このSUZUKI PLANカスタム版LaiNESを用いてAndroidでPDLを実装してみた例も作ってみました。
https://github.com/suzukiplan/nes-view-android
なお、動作についてはPublic DomainのROMで確認してます。
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。