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

MC6847で遊ぼう会(シモキタ)

MC6847搭載機で遊ぶイベントです。

  • NEC PC-6001
  • SANYO PHC-25
  • GAKKEN TV BOY
  • TANDY TRS-80 COCO
  • TANDY TRS-80 MC-10

ハッシュタグ(2021/6/26)

FP技能士3級に挑戦してみた!

老後の資金計画のために、FP技能士2級資格をとろうと思いまずは問題集を買いました。しかしよくよく調べると、最初からFP技能士2級を受験する事はできないそうです。

仕方がないのでYouTubeと過去問で勉強して3級から受験しようと思います。

www.youtube.com

過去問を繰り返しといて、毎回60点とれるようになれば合格すると思います。

www.jafp.or.jp

          学科 実技
2019年5月 56  60
2019年9月 66  70
2020年1月 66  55
2020年9月 56  65
2021年1月 76  45
2021年5月 56  50

www.youtube.com

www.youtube.com

www.youtube.com

www.youtube.com

www.youtube.com

www.youtube.com

NISAに挑戦してみた!

定年退職後の年金の不安からiDeco/NISAを調べてみました。あと15年くらい真面目に働けば65歳から夫婦で毎月20万円くらいはもらえると思います。

でも毎月30万円くらい欲しいですよね。iDecoは年金的なもらい方も出来るようですが、自分としては退職金として一括でもらいたいです。理想は退職金も合わせて現金で2000万円、株式で2000万円、不動産で2000万円でしょうか。

このままローン返済が終われば、定年退職する頃には現金で2000万円、不動産で2000万円は達成できそうです。

そうすると株式投資を始めないといけないという事になります。今まで目を背けていたのは、仕事が忙しくて株などやっていられないという事と、子どもの教育が一番優先すべき投資という考えでした。

しかし50歳も目前となると、仕事は落ち着いており、子どもの進路もだいたい決まり、何より定年退職が視野に入ってきます。

まずはiDecoを調べたのですが、会社で同様の制度に入っており、そちらで十分という結論になりました。

つぎにNISAと積立NISAを調べたのですが、年間120万円の枠が魅力であるNISAで個別株を買っていこうと思いました。

2023年までのNISAとそれ以降の新NISAで、個別株を毎年100万円買っていけば60歳までに株式で1000万円達成できるはずです。

車中泊に挑戦してみた!

仕事が一段落したら長期休暇をとって、西日本を周ってみたいと考えており、安くて運転しやすいバンコンか、高いけど快適なキャブコンか、実際に乗ってみて考えたいと思っています。

まずはバンコンタイプのアルトピアーノを24Hレンタルして自宅の駐車場で車中泊してみました。

www.toyota-mobility-kanagawa.jp

家族3人でのドライブは快適でしたが、車中泊となると1人だったら快適と感じました。またFFヒーターやエアコンがないため季節を選ぶとも思いました。

つぎにキャブコンタイプのレジストロアウルを予約してみました。

www.mystic.ne.jp

こちらはFFヒーターやエアコンもあり、バンクベッドもあることから、2人でも快適に車中泊できそうです。ただし自宅の駐車場には高さが足りなくて入らないため、今回はPAでの車中泊に挑戦してみようと思います。

3DOで遊ぼう会2(ヨコハマ)

次世代ゲーム機3DOで遊ぶイベントです。

ハッシュタグ(2021/4/18)

ナムコミュージアム アンコール

自分の部屋にPS3が戻ってきたので、PS1 ナムコミュージアムをやり直しています。

ひととおり遊んだ評価は、

あくまで自分の好みですが、世界観が好きで遊んでいて楽しいものは○、遊べるものは△、世界観が好きではないものは×としました。