タイトーメモリーズで遊ぼう会2(ヨコハマ)

タイトーメモリーズで遊ぶイベントです。

ハッシュタグ(2025/4/19)

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

いぬふとさんの拡張カートリッジに対応するために学研TV BOYエミュレータを更新しました。 http://inufuto.web.fc2.com/8bit/hardware/

  • 市街戦200X年(ふつうに遊べる!)
  • ミスターボム(ふつうに遊べる!)
  • エキサイトインベーダー(ふつうに遊べる!)
  • ロボタンウォーズ(ふつうに遊べる!)
  • 地対空大作戦(サウンドが鳴らない)
  • フロッガー(タイトルでフリーズ)

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

takeda-toshiya.my.coocan.jp

/*
    GAKKEN TV BOY Emulator 'yaTVBOY'

    Author : tanam
    Date   : 2025.01.28

    [ memory ]
*/

#ifndef _MEMORY_H_
#define _MEMORY_H_

#include "../vm.h"
#include "../../emu.h"
#include "../device.h"
#include "../mc6800.h"
#include "../mc6847.h"
// #include "../pcm1bit.h"

#define SIG_MEMORY_PORT_1   0

class MEMORY : public DEVICE
{
private:
    MC6800 *d_cpu;
    MC6847* d_vdp;

    uint8_t rom[0x8000]; // ext rom
    uint8_t ram[0x8000]; // ext ram 
    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);
    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
/*
    GAKKEN TV BOY Emulator 'yaTVBOY'

    Author : tanam
    Date   : 2025.01.28

    [ 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(0x4000, 0x5fff, ram+0x4000, ram + 0x4000);
    SET_BANK(0x6000, 0x7fff, wdmy, rdmy);
    SET_BANK(0x8000, 0xffff, wdmy, rom );
    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 & 0x01) == 1) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x04);
    }
    if (shot1 == 1 && (d_cpu->port[0].wreg & 0x01) == 1) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x02);
    }
    if (down == 1 && (d_cpu->port[0].wreg & 0x01) == 0) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x04);
    }
    if (up == 1 && (d_cpu->port[0].wreg & 0x01) == 0) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x02);
    }
    if (left == 1 && (d_cpu->port[0].wreg & 0x01) == 0) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x08);
    }
    if (right == 1 && (d_cpu->port[0].wreg & 0x01) == 0) {
        d_cpu->write_signal(SIG_MC6801_PORT_2, 0x00, 0x10);
    }
}

void MEMORY::key_down(int code)
{
    if (code==VK_C) { // POWKIDDY A20
        shot1 =1;
    }
    if (code==VK_D) { // POWKIDDY A20
        shot2 =1;
    }
    if (code==VK_LEFT) {
        left =1;
    }
    if (code==VK_UP) {
        up =1;
    }
    if (code==VK_RIGHT) {
        right =1;
    }
    if (code==VK_DOWN) {
        down =1;
    }
}

void MEMORY::key_up(int code)
{
    if (code==VK_C) { // POWKIDDY A20
        shot1 =0;
    }
    if (code==VK_D) { // POWKIDDY A20
        shot2 =0;
    }
    if (code==VK_LEFT) {
        left =0;
    }
    if (code==VK_UP) {
        up =0;
    }
    if (code==VK_RIGHT) {
        right =0;
    }
    if (code==VK_DOWN) {
        down =0;
    }
}

void MEMORY::open_cart(const _TCHAR* file_path)
{
    FILEIO* fio = new FILEIO();
    if(fio->Fopen(file_path, FILEIO_READ_BINARY)) {
        // GAKKEN ROMS
        if (0x1000 == fio->Fread(rom, 1, sizeof(rom))) {
            // ROBOTAN WARS
            if (rom[0x06f0]==0x96 && rom[0x06f1]==0x19) {
                rom[0x06f0]=0x86;
                rom[0x06f1]=0x10;
            }
            memcpy(rom + 0x7000, rom, 0x1000);
        }
        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   : 2025.01.28

    [ 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);

    pcm = new PCM1BIT(this, emu);

    memory = new MEMORY(this, emu);
    memory->set_context_cpu(cpu);
    memory->set_context_vdp(vdp);

    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);
    vdp->set_context_vsync(cpu, SIG_CPU_IRQ, 0x01);
    vdp->set_context_hsync(cpu, SIG_MC6801_PORT_1, 0x80);

    // cpu bus
    cpu->set_context_mem(memory);
#ifdef USE_DEBUGGER
    cpu->set_context_debugger(new DEBUGGER(this, emu));
#endif

    // 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);
//   POWKIDDY A20
//   if (code==VK_L) this->open_cart(0,"/sdcard/Download/emulator/tvboyROM/CART/chitaikuu.bin");
//    if (code==VK_X) this->open_cart(0,"/sdcard/Download/emulator/tvboyROM/CART/robotan.bin");
//    if (code==VK_Y) this->open_cart(0,"/sdcard/Download/emulator/tvboyROM/CART/einvader.bin");
//    if (code==VK_RETURN) this->open_cart(0,"/sdcard/Download/emulator/tvboyROM/CART/mrbomb.bin");
//    if (code==VK_B) this->open_cart(0,"/sdcard/Download/emulator/tvboyROM/CART/shigaisen.bin");
//    if (code==VK_R) this->open_cart(0,"/sdcard/Download/emulator/tvboyROM/CART/frogger.bin");
//    if (code==VK_Z) this->~VM();
}

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;
}

AMBERNIC RG351V

ゲームボーイが好きです。Linuxで動くRG351Vでアルカディアを動かす方法をメモします。

https://jp.anbernic.com/products/anbernic-rg351v

まずはAMBERELECというCFWを導入します。

https://amberelec.org/

AMBERELECでアルカディアを動かす方法を伝授しましょう。アルカディアのHASHファイルが必要になります。

github.com

<software name="doraemon">
    <description>Doraemon</description>
    <year>1983</year>
    <publisher>Bandai</publisher>
    <part name="cart" interface="arcadia_cart">
        <dataarea name="rom" size="8192">
            <rom name="doraemon.bin" size="8192" crc="62c45881" sha1="a16fda96d00c8b08bb362f2573a45939aa6be051" offset="0000" />
        </dataarea>
    </part>
</software>

アルカディアのROMファイル名およびHASH値はarcadia.xmlの記載に合わせる必要があります。

/roms/bios/mame/hash/arcadia.xml

ROMファイルを置く場所も決まっています。

/roms/mame/arcadia/

電子ゲームで遊ぼう会(ヨコハマ)

電子ゲームで遊ぶイベントです。

  • FLゲーム
  • LSIゲーム
  • LCDゲーム

ハッシュタグ(2025/2/22)