世にも珍しいVCSじゃない方のTV BOYエミュレータです。
ソースコード差分は以下になります。
vm.h
// GAKKEN TV BOY #ifdef _TVBOY #include "tvboy/tvboy.h" #endif
tvboy.h
/* GAKKEN TV BOY Emulator 'yaTVBOY' Author : tanam Date : 2020.06.13 [ virtual machine ] */ #ifndef _TVBOY_H_ #define _TVBOY_H_ #define DEVICE_NAME "GAKKEN TV BOY" #define CONFIG_NAME "tvboy" // device informations for virtual machine #define FRAMES_PER_SEC 60 #define LINES_PER_FRAME 262 #define CPU_CLOCKS 3579545 / 4 #define SCREEN_WIDTH 256 #define SCREEN_HEIGHT 192 #define HAS_MC6801 #define MC6847_VRAM_INV 0x40 // device informations for win32 #define USE_CART 1 #define USE_SOUND_VOLUME 2 #define USE_DEBUGGER #define USE_STATE #include "../../common.h" #include "../../fileio.h" #include "../vm_template.h" #ifdef USE_SOUND_VOLUME static const _TCHAR *sound_device_caption[] = { _T("PCM"), }; #endif class EMU; class DEVICE; class EVENT; class MC6847; class MC6800; class PCM1BIT; class MEMORY; class VM : public VM_TEMPLATE { protected: // EMU* emu; // devices EVENT* event; MC6847* vdp; MC6800* cpu; PCM1BIT* pcm; MEMORY* memory; public: // ---------------------------------------- // initialize // ---------------------------------------- VM(EMU* parent_emu); ~VM(); // ---------------------------------------- // for emulation class // ---------------------------------------- // drive virtual machine void reset(); void run(); double get_frame_rate() { return FRAMES_PER_SEC; } #ifdef USE_DEBUGGER // debugger DEVICE *get_cpu(int index); #endif // draw screen void draw_screen(); // sound generation void initialize_sound(int rate, int samples); uint16_t* create_sound(int* extra_frames); int get_sound_buffer_ptr(); #ifdef USE_SOUND_VOLUME void set_sound_device_volume(int ch, int decibel_l, int decibel_r); #endif // user interface void open_cart(int drv, const _TCHAR* file_path); void close_cart(int drv); bool is_cart_inserted(int drv); // notify key void key_down(int code, bool repeat); void key_up(int code); bool is_frame_skippable(); void update_config(); bool process_state(FILEIO* state_fio, bool loading); // ---------------------------------------- // for each device // ---------------------------------------- // devices DEVICE* get_device(int id); // DEVICE* dummy; // DEVICE* first_device; // DEVICE* last_device; }; #endif
tvboy.cpp
/* GAKKEN TV BOY Emulator 'yaTVBOY' Author : tanam Date : 2020.06.13 [ virtual machine ] */ #include "tvboy.h" #include "../../emu.h" #include "../device.h" #include "../event.h" #include "../mc6847.h" #include "../mc6800.h" #include "../pcm1bit.h" #ifdef USE_DEBUGGER #include "../debugger.h" #endif #include "memory.h" // ---------------------------------------------------------------------------- // initialize // ---------------------------------------------------------------------------- VM::VM(EMU* parent_emu) : VM_TEMPLATE(parent_emu) { // create devices first_device = last_device = NULL; dummy = new DEVICE(this, emu); // must be 1st device event = new EVENT(this, emu); // must be 2nd device vdp = new MC6847(this, emu); cpu = new MC6800(this, emu); memory = new MEMORY(this, emu); memory->set_context_cpu(cpu); memory->set_context_vdp(vdp); pcm = new PCM1BIT(this, emu); cpu->set_context_port1(pcm, SIG_PCM1BIT_SIGNAL, 0x20, 0); cpu->set_context_port1(pcm, SIG_PCM1BIT_SIGNAL, 0x40, 0); cpu->set_context_port1(memory, SIG_MEMORY_PORT_1, 0x0F, 0); // set contexts event->set_context_cpu(cpu); event->set_context_sound(pcm); vdp->set_vram_ptr(memory->get_vram(), 0x800); vdp->set_context_cpu(cpu); // cpu bus cpu->set_context_mem(memory); #ifdef USE_DEBUGGER cpu->set_context_debugger(new DEBUGGER(this, emu)); #endif pcm = new PCM1BIT(this, emu); // initialize all devices for(DEVICE* device = first_device; device; device = device->next_device) { device->initialize(); } } VM::~VM() { // delete all devices for(DEVICE* device = first_device; device;) { DEVICE *next_device = device->next_device; device->release(); delete device; device = next_device; } } DEVICE* VM::get_device(int id) { for(DEVICE* device = first_device; device; device = device->next_device) { if(device->this_device_id == id) { return device; } } return NULL; } // ---------------------------------------------------------------------------- // drive virtual machine // ---------------------------------------------------------------------------- void VM::reset() { // reset all devices for(DEVICE* device = first_device; device; device = device->next_device) { device->reset(); } } void VM::run() { event->drive(); } // ---------------------------------------------------------------------------- // debugger // ---------------------------------------------------------------------------- #ifdef USE_DEBUGGER DEVICE *VM::get_cpu(int index) { if(index == 0) { return cpu; } return NULL; } #endif // ---------------------------------------------------------------------------- // draw screen // ---------------------------------------------------------------------------- void VM::draw_screen() { vdp->draw_screen(); } // ---------------------------------------------------------------------------- // notify key // ---------------------------------------------------------------------------- void VM::key_down(int code, bool repeat) { memory->key_down(code); } void VM::key_up(int code) { memory->key_up(code); } // ---------------------------------------------------------------------------- // soud manager // ---------------------------------------------------------------------------- void VM::initialize_sound(int rate, int samples) { // init sound manager event->initialize_sound(rate, samples); pcm->initialize_sound(rate, 8000); } uint16_t* VM::create_sound(int* extra_frames) { return event->create_sound(extra_frames); } int VM::get_sound_buffer_ptr() { return event->get_sound_buffer_ptr(); } #ifdef USE_SOUND_VOLUME void VM::set_sound_device_volume(int ch, int decibel_l, int decibel_r) { if(ch == 0) { pcm->set_volume(0, decibel_l, decibel_r); } } #endif // ---------------------------------------------------------------------------- // user interface // ---------------------------------------------------------------------------- void VM::open_cart(int drv, const _TCHAR* file_path) { if(drv == 0) { memory->open_cart(file_path); reset(); } } void VM::close_cart(int drv) { if(drv == 0) { memory->close_cart(); reset(); } } bool VM::is_cart_inserted(int drv) { if(drv == 0) { return memory->is_cart_inserted(); } else { return false; } } bool VM::is_frame_skippable() { return event->is_frame_skippable(); } void VM::update_config() { for(DEVICE* device = first_device; device; device = device->next_device) { device->update_config(); } } #define STATE_VERSION 3 bool VM::process_state(FILEIO* state_fio, bool loading) { if(!state_fio->StateCheckUint32(STATE_VERSION)) { return false; } for(DEVICE* device = first_device; device; device = device->next_device) { const _TCHAR *name = char_to_tchar(typeid(*device).name() + 6); // skip "class " int len = (int)_tcslen(name); if(!state_fio->StateCheckInt32(len)) { return false; } if(!state_fio->StateCheckBuffer(name, len, 1)) { return false; } if(!device->process_state(state_fio, loading)) { return false; } } return true; }
memory.h
/* GAKKEN TV BOY Emulator 'yaTVBOY' Author : tanam Date : 2020.06.13 [ memory ] */ #ifndef _MEMORY_H_ #define _MEMORY_H_ #include "../vm.h" #include "../../emu.h" #include "../device.h" #include "../mc6800.h" #include "../mc6847.h" #define SIG_MEMORY_PORT_1 0 class MEMORY : public DEVICE { private: MC6800 *d_cpu; MC6847 *d_vdp; uint8_t rom[0x1000]; uint8_t ram[0x1000]; uint8_t vram[0x1000]; uint8_t wdmy[0x400]; uint8_t rdmy[0x400]; uint8_t* wbank[64]; uint8_t* rbank[64]; int shot1; int shot2; int up; int down; int left; int right; bool event; bool inserted; public: MEMORY(VM_TEMPLATE* parent_vm, EMU* parent_emu) : DEVICE(parent_vm, parent_emu) { set_device_name(_T("Memory Bus")); } ~MEMORY() {} // common functions void initialize(); void reset(); void write_data8(uint32_t addr, uint32_t data); uint32_t read_data8(uint32_t addr); void write_signal(int id, uint32_t data, uint32_t mask); void event_callback(int event_id, int err); bool process_state(FILEIO* state_fio, bool loading); // unique functions void key_down(int code); void key_up(int code); void set_context_cpu(MC6800* device) { d_cpu = device; } void set_context_vdp(MC6847* device) { d_vdp = device; } void open_cart(const _TCHAR* file_path); void close_cart(); bool is_cart_inserted() { return inserted; } uint8_t* get_vram() { return vram; } }; #endif
memory.cpp
/* GAKKEN TV BOY Emulator 'yaTVBOY' Author : tanam Date : 2020.06.13 [ memory ] */ #include "memory.h" #define SET_BANK(s, e, w, r) { \ int sb = (s) >> 10, eb = (e) >> 10; \ for(int i = sb; i <= eb; i++) { \ if((w) == wdmy) { \ wbank[i] = wdmy; \ } else { \ wbank[i] = (w) + 0x400 * (i - sb); \ } \ if((r) == rdmy) { \ rbank[i] = rdmy; \ } else { \ rbank[i] = (r) + 0x400 * (i - sb); \ } \ } \ } void MEMORY::initialize() { memset(rom, 0xff, sizeof(rom)); memset(rdmy, 0xff, sizeof(rdmy)); // set memory map SET_BANK(0x0000, 0x0fff, ram, ram ); SET_BANK(0x1000, 0x1fff, vram, vram); SET_BANK(0x2000, 0xefff, wdmy, rdmy); SET_BANK(0xf000, 0xffff, wdmy, rom ); // register event register_event_by_clock(this, 0, 256, true, NULL); event = false; inserted = false; } void MEMORY::reset() { memset(ram, 0, sizeof(ram)); for (int i=0; i<sizeof(vram); i++) { vram[i]=rand() % 256; } d_vdp->write_signal(SIG_MC6847_AS, 0x00, 0x08); d_vdp->write_signal(SIG_MC6847_AG, 0x10, 0x10); d_vdp->write_signal(SIG_MC6847_CSS, 0x20, 0x20); d_vdp->write_signal(SIG_MC6847_GM, 0x00, 0x02); d_vdp->write_signal(SIG_MC6847_GM, 0x01, 0x01); d_vdp->write_signal(SIG_MC6847_INTEXT, 0x00, 0x04); shot1 = shot2 = up = down = left = right = 0; } void MEMORY::write_data8(uint32_t addr, uint32_t data) { addr &= 0xffff; if(addr >= 0x80 && addr < 0x100) { d_cpu->ram[addr-0x80]=data; } if(addr == 0x2000) { d_vdp->write_signal(SIG_MC6847_AS, data, 0x08); d_vdp->write_signal(SIG_MC6847_AG, data, 0x10); d_vdp->write_signal(SIG_MC6847_CSS, data, 0x20); d_vdp->write_signal(SIG_MC6847_GM, data << 1, 0x02); d_vdp->write_signal(SIG_MC6847_GM, data >> 1, 0x01); d_vdp->write_signal(SIG_MC6847_INTEXT, data, 0x04); return; } wbank[addr >> 10][addr & 0x3ff] = data; } uint32_t MEMORY::read_data8(uint32_t addr) { addr &= 0xffff; if(addr >= 0x80 && addr < 0x100) { return d_cpu->ram[addr-0x80]; } return rbank[addr >> 10][addr & 0x3ff]; } void MEMORY::write_signal(int id, uint32_t data, uint32_t mask) { d_cpu->write_signal(SIG_MC6801_PORT_2, 0x1E, 0x1E); if (shot2==1 && d_cpu->port[0].wreg==1) { d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x04); } if (down==1 && d_cpu->port[0].wreg==2) { d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x04); } if (shot1==1 && d_cpu->port[0].wreg==1) { d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x02); } if (up==1 && d_cpu->port[0].wreg==2) { d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x02); } if (left==1 && d_cpu->port[0].wreg==2) { d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x08); } if (right==1 && d_cpu->port[0].wreg==2) { d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x10); } } void MEMORY::event_callback(int event_id, int err) { if (event) { d_cpu->write_signal(SIG_CPU_IRQ, 1, 1); event = false; } else { d_cpu->write_signal(SIG_CPU_IRQ, 0, 1); event = true; } } void MEMORY::key_down(int code) { if (code==0x20) { shot1 =1; } if (code==0x11) { shot2 =1; } if (code==0x25) { left =1; } if (code==0x26) { up =1; } if (code==0x27) { right =1; } if (code==0x28) { down =1; } } void MEMORY::key_up(int code) { if (code==0x20) { shot1 =0; } if (code==0x11) { shot2 =0; } if (code==0x25) { left =0; } if (code==0x26) { up =0; } if (code==0x27) { right =0; } if (code==0x28) { down =0; } } void MEMORY::open_cart(const _TCHAR* file_path) { FILEIO* fio = new FILEIO(); if(fio->Fopen(file_path, FILEIO_READ_BINARY)) { fio->Fread(rom, sizeof(rom), 1); fio->Fclose(); inserted = true; } delete fio; } void MEMORY::close_cart() { memset(rom, 0xff, sizeof(rom)); inserted = false; } #define STATE_VERSION 1 bool MEMORY::process_state(FILEIO* state_fio, bool loading) { if(!state_fio->StateCheckUint32(STATE_VERSION)) { return false; } if(!state_fio->StateCheckInt32(this_device_id)) { return false; } state_fio->StateArray(ram, sizeof(ram), 1); state_fio->StateArray(vram, sizeof(vram), 1); return true; }