世にも珍しいVCSじゃない方のTV BOYエミュレータです。
www.youtube.com
ソースコード差分は以下になります。
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;
}