学研TV BOYエミュレータをつくる その5

世にも珍しいVCSじゃない方のTV BOYエミュレータを久しぶりに更新しました。


www.youtube.com

ソースコード差分は以下になります。

/*
    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)
{
    static uint8_t temp=0;
    addr &= 0xffff;
    uint32_t pc=d_cpu->get_pc();
    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 & 0x0f)==1) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x04);
    }
    if (down==1 && (d_cpu->port[0].wreg & 0x0f)==2) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x04);
    }
    if (shot1==1 && (d_cpu->port[0].wreg & 0x0f)==1) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x02);
    }
    if (up==1 && (d_cpu->port[0].wreg & 0x0f)==2) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x02);
    }
    if (left==1 && (d_cpu->port[0].wreg & 0x0f)==2) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x08);
    }
    if (right==1 && (d_cpu->port[0].wreg & 0x0f)==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;
}
/*
    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 char *name = typeid(*device).name() + 6; // skip "class "
        int len = (int)strlen(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;
}