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

世にも珍しい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;
}

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

まずはSource Code Archive (4/6/2020)をダウンロードします。

http://takeda-toshiya.my.coocan.jp/common/index.html

好きなディレクトリに展開して、以下のプロジェクトを開きます。

source\vc++2008\pc6001.vcproj

追加のインクルードディレクトリとライブラリディレクトリを指定してビルドできるはずです。

C:\Program Files (x86)\Microsoft DirectX 9.0 SDK (December 2004)\include
C:\Program Files (x86)\Microsoft DirectX 9.0 SDK (December 2004)\lib\x86

Common Source Code Projectのpc6001をコピーして編集していきます。

source\vc++2008\pc6001.vcproj
source\src\res\pc6001.rc
              \pc6001.ico
source\src\vm\vm.h
source\src\vm\pc6001\display.cpp
                    display.h
                    floppy.cpp
                    floppy.h
                    joystick.cpp
                    joystick.h
                    memory.cpp
                    memory.h
                    memory_draw.cpp
                    pc6001.cpp
                    pc6001.h
                    psub.cpp
                    psub.h
                    sub.cpp
                    sub.h
                    timer.cpp
                    timer.h

ディレクトリ構成は以下のようになります。

source\vc++2008\tvboy.vcproj
source\src\res\tvboy.rc
                tvboy.ico
source\src\vm\vm.h
source\src\vm\event.cpp
                event.h
                mc6800.cpp
                mc6800.h
                mc6847.cpp
                mc6847.h
                pcm1bit.cpp
                pcm1bit.h
source\src\vm\tvboy\memory.cpp
                    memory.h
                    tvboy.cpp
                    tvboy.h

tvboy.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// Japanese resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN)
#ifdef _WIN32
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
#pragma code_page(932)
#endif //_WIN32

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

#endif    // Japanese resources
/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32

/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//

IDR_ACCELERATOR1 ACCELERATORS DISCARDABLE 
BEGIN
    VK_RETURN,      ID_ACCEL_SCREEN,        VIRTKEY, ALT, NOINVERT
    VK_APPS,        ID_ACCEL_SPEED,         VIRTKEY, NOINVERT
    VK_APPS,        ID_ACCEL_ROMAJI,        VIRTKEY, CONTROL, NOINVERT
END


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_MENU1 MENU DISCARDABLE 
BEGIN
    POPUP "Control"
    BEGIN
        MENUITEM "Reset",                       ID_RESET
        MENUITEM SEPARATOR
        MENUITEM "CPU x1",                      ID_CPU_POWER0
        MENUITEM "CPU x2",                      ID_CPU_POWER1
        MENUITEM "CPU x4",                      ID_CPU_POWER2
        MENUITEM "CPU x8",                      ID_CPU_POWER3
        MENUITEM "CPU x16",                     ID_CPU_POWER4
        MENUITEM "Full Speed",                  ID_FULL_SPEED
        MENUITEM SEPARATOR
        MENUITEM "Paste",                       ID_AUTOKEY_START
        MENUITEM "Stop",                        ID_AUTOKEY_STOP
        MENUITEM "Romaji to Kana",              ID_ROMAJI_TO_KANA
        MENUITEM SEPARATOR
        POPUP "Save State"
        BEGIN
            MENUITEM "State 0",                 ID_SAVE_STATE0
            MENUITEM "State 1",                 ID_SAVE_STATE1
            MENUITEM "State 2",                 ID_SAVE_STATE2
            MENUITEM "State 3",                 ID_SAVE_STATE3
            MENUITEM "State 4",                 ID_SAVE_STATE4
            MENUITEM "State 5",                 ID_SAVE_STATE5
            MENUITEM "State 6",                 ID_SAVE_STATE6
            MENUITEM "State 7",                 ID_SAVE_STATE7
            MENUITEM "State 8",                 ID_SAVE_STATE8
            MENUITEM "State 9",                 ID_SAVE_STATE9
        END
        POPUP "Load State"
        BEGIN
            MENUITEM "State 0",                 ID_LOAD_STATE0
            MENUITEM "State 1",                 ID_LOAD_STATE1
            MENUITEM "State 2",                 ID_LOAD_STATE2
            MENUITEM "State 3",                 ID_LOAD_STATE3
            MENUITEM "State 4",                 ID_LOAD_STATE4
            MENUITEM "State 5",                 ID_LOAD_STATE5
            MENUITEM "State 6",                 ID_LOAD_STATE6
            MENUITEM "State 7",                 ID_LOAD_STATE7
            MENUITEM "State 8",                 ID_LOAD_STATE8
            MENUITEM "State 9",                 ID_LOAD_STATE9
        END
        MENUITEM SEPARATOR
        MENUITEM "Debug Main CPU",              ID_OPEN_DEBUGGER0
        MENUITEM "Close Debugger",              ID_CLOSE_DEBUGGER
        MENUITEM SEPARATOR
        MENUITEM "Exit",                        ID_EXIT
    END
    POPUP "Cart"
    BEGIN
        MENUITEM "Insert",                      ID_OPEN_CART1
        MENUITEM "Eject",                       ID_CLOSE_CART1
        MENUITEM SEPARATOR
        MENUITEM "Recent",                      ID_RECENT_CART1
    END
    POPUP "Host"
    BEGIN
        MENUITEM "Rec Movie 60fps",             ID_HOST_REC_MOVIE_60FPS
        MENUITEM "Rec Movie 30fps",             ID_HOST_REC_MOVIE_30FPS
        MENUITEM "Rec Movie 15fps",             ID_HOST_REC_MOVIE_15FPS
        MENUITEM "Rec Sound",                   ID_HOST_REC_SOUND
        MENUITEM "Stop",                        ID_HOST_REC_STOP
        MENUITEM "Capture Screen",              ID_HOST_CAPTURE_SCREEN
        MENUITEM SEPARATOR
        POPUP "Screen"
        BEGIN
            MENUITEM "Window x1",               ID_SCREEN_WINDOW
            MENUITEM "Fullscreen 640x400",      ID_SCREEN_FULLSCREEN
            MENUITEM SEPARATOR
            MENUITEM "Fullscreen Stretch 1",    ID_SCREEN_FULLSCREEN_DOTBYDOT
            MENUITEM "Fullscreen Stretch 2",    ID_SCREEN_FULLSCREEN_STRETCH
            MENUITEM "Fullscreen Stretch 4",    ID_SCREEN_FULLSCREEN_FILL
            MENUITEM SEPARATOR
            MENUITEM "Rotate 0deg",             ID_SCREEN_ROTATE_0
            MENUITEM "Rotate +90deg",           ID_SCREEN_ROTATE_90
            MENUITEM "Rotate 180deg",           ID_SCREEN_ROTATE_180
            MENUITEM "Rotate -90deg",           ID_SCREEN_ROTATE_270
        END
        POPUP "Sound"
        BEGIN
            MENUITEM "2000Hz",                  ID_SOUND_FREQ0
            MENUITEM "4000Hz",                  ID_SOUND_FREQ1
            MENUITEM "8000Hz",                  ID_SOUND_FREQ2
            MENUITEM "11025Hz",                 ID_SOUND_FREQ3
            MENUITEM "22050Hz",                 ID_SOUND_FREQ4
            MENUITEM "44100Hz",                 ID_SOUND_FREQ5
            MENUITEM "48000Hz",                 ID_SOUND_FREQ6
            MENUITEM "96000Hz",                 ID_SOUND_FREQ7
            MENUITEM SEPARATOR
            MENUITEM "50msec",                  ID_SOUND_LATE0
            MENUITEM "100msec",                 ID_SOUND_LATE1
            MENUITEM "200msec",                 ID_SOUND_LATE2
            MENUITEM "300msec",                 ID_SOUND_LATE3
            MENUITEM "400msec",                 ID_SOUND_LATE4
            MENUITEM SEPARATOR
            MENUITEM "Realtime Mix",            ID_SOUND_STRICT_RENDER
            MENUITEM "Light Weight Mix",        ID_SOUND_LIGHT_RENDER
            MENUITEM SEPARATOR
            MENUITEM "Volume",                  ID_SOUND_VOLUME
        END
        MENUITEM SEPARATOR
        MENUITEM "Use Direct3D9",               ID_HOST_USE_D3D9
        MENUITEM "Wait Vsync",                  ID_HOST_WAIT_VSYNC
        MENUITEM "Use DirectInput",             ID_HOST_USE_DINPUT
        MENUITEM "Disable Windows 8 DWM",       ID_HOST_DISABLE_DWM
        MENUITEM "Show Status Bar",             ID_HOST_SHOW_STATUS_BAR
    END
END


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1               ICON    DISCARDABLE     "phc20.ico"
#endif    // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_VOLUME DIALOG DISCARDABLE  0, 0, 250, 90
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Volume"
FONT 9, "MS PGothic"
BEGIN
    LTEXT           "Sound Device #1",IDC_VOLUME_CAPTION0,6,6,60,8
    CONTROL         "",IDC_VOLUME_PARAM_L0,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,70,6,175,12
    CONTROL         "",IDC_VOLUME_PARAM_R0,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,70,21,175,12
    LTEXT           "Sound Device #2",IDC_VOLUME_CAPTION1,6,36,60,8
    CONTROL         "",IDC_VOLUME_PARAM_L1,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,70,36,175,12
    CONTROL         "",IDC_VOLUME_PARAM_R1,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,70,51,175,12
    DEFPUSHBUTTON   "OK",IDOK,70,70,50,14
    DEFPUSHBUTTON   "Reset",IDC_VOLUME_RESET,130,70,50,14
END



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

第二種電気工事士の資格に挑戦してみる その1

きっかけは以下の記事です。

https://qiita.com/rukihena/items/a30f07f93ca1718dff8e

 

2003年に新築した一戸建ても色々がたが来て、コンセントなど自分でできるものは取り換えたい!と思い立ちました。

 

まずは引き返せないように申し込みます。インターネット申し込みだと9300円かかりました。

https://uketsuke.shiken.or.jp/mohshi/FIC1010.do

 

工具は自分で用意する必要があるため以下を購入しました。11595円かかりました。

https://www.amazon.co.jp/dp/B079JFJHSM/

 

部材セットはDVDつきの第二種電気工事士技能試験「準備万端試験対策セット」をメルカリで5000円で購入。2019年の対策セットですが、2020年も試験範囲が同じなので問題ないと思っています。

 

筆記試験と技能試験があるのですが、まずは工具が届くまでは、技能試験の動画を見てイメージトレーニングをしています。HOZAN最高!

電工試験の虎_ホーザン - YouTube

 

動画を見て、合格クリップ(P-926 629円)、合格マルチツール(DK-200 667円)、プレートはずし器(WV-8400 420円)などを追加購入しました。足りない部材は都度ホームセンターなどで調達予定。

RETRONFREAK5でHello, World.

Android NDKをつかってRETRONFREAK5でHello, Worldしてみます。

circuit-board.de

まずはAndroid NDK(android-ndk-r10e-windows-x86_64.zip)をダウンロードします。

developer.android.com

サンプルプログラムを書き換えます。

>cd \android-ndk-r10e\samples\hello-jni
>..\..\ndk-build

RETRONFREAK5に転送して実行します。

>adb push \android-ndk-r10e\samples\hello-jni\libs\armeabi\hello-jni /hello-jni
>adb shell
#busybox chmod +x /hello-jni
#/hello-jni
Hello, World.

Application.mk

APP_ABI := armeabi

Android.mk

# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

#include $(BUILD_SHARED_LIBRARY)
include $(BUILD_EXECUTABLE)

hello-jni.c

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include <string.h>
#include <jni.h>

/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java
 */

jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
//#if defined(__arm__)
//  #if defined(__ARM_ARCH_7A__)
//    #if defined(__ARM_NEON__)
//      #if defined(__ARM_PCS_VFP)
//        #define ABI "armeabi-v7a/NEON (hard-float)"
//      #else
//        #define ABI "armeabi-v7a/NEON"
//      #endif
//    #else
//      #if defined(__ARM_PCS_VFP)
//        #define ABI "armeabi-v7a (hard-float)"
//      #else
//        #define ABI "armeabi-v7a"
//      #endif
//    #endifexit
//  #else
   #define ABI "armeabi"
//  #endif
//#elif defined(__i386__)
//   #define ABI "x86"
//#elif defined(__x86_64__)
//   #define ABI "x86_64"
//#elif defined(__mips64)  /* mips64el-* toolchain defines __mips__ too */
//   #define ABI "mips64"
//#elif defined(__mips__)
//   #define ABI "mips"
//#elif defined(__aarch64__)
//   #define ABI "arm64-v8a"
//#else
//   #define ABI "unknown"
//#endif

    return (*env)->NewStringUTF(env, "Hello from JNI !  Compiled with ABI " ABI ".");
}

int main(void)
{
    printf("Hello, World.\n");
    return 0;
}

RETRONFREAK5のSERIAL(DNA)を取得します。

/*
 * RETROFREAK GETDNA
 */

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#define RKNAND_SYS_STORGAE_DATA_LEN 512
#define SN_SECTOR_OP_TAG            0x41444E53 // SNDA
#define RKNAND_GET_SN_SECTOR       _IOW('d', 3, unsigned int)

typedef struct tagRKNAND_SYS_STORGAE
{
    unsigned long tag;
    unsigned long len;
    unsigned char data[RKNAND_SYS_STORGAE_DATA_LEN];
}RKNAND_SYS_STORGAE;

int main(void)
{
    int i;
    RKNAND_SYS_STORGAE sysData;
    for (i=0; i<512; i++) sysData.data[i]=0xff;
    int sys_fd = open("/dev/rknand_sys_storage",O_RDWR,0);
    if(sys_fd < 0){
        printf("rknand_sys_storage open fail\n");
        return -1;
    }
    //sn
    sysData.tag = SN_SECTOR_OP_TAG;
    sysData.len = RKNAND_SYS_STORGAE_DATA_LEN;
    int ret = ioctl(sys_fd, RKNAND_GET_SN_SECTOR, &sysData);
    if(ret) {
        printf("get sn fail\n");
        return -1;
    }
    for (i=480; i<496; i++) printf("%02x", sysData.data[i]);
    printf("\n");

    return 0;
}

RETRON5をRETROFREAKにする

RETRON5をCFWでRETROFREAKにしてみます。

circuit-board.de

上記CFW(RETRONFREAK5)で本体をアップデートします。コントローラケーブルをPCにつなぐとADB接続でrootが有効になります。

カートリッジを読み込むと/mnt/ramに元のバイナリデータが生成されます。

>adb pull /mnt/ram
>cd ram
>dir
dump.NES

 

カセットビジョンで遊ぼう会(ヨコハマ)

エポック社の電子ゲーム、ゲームポケコンカセットビジョンスーパーカセットビジョンなどで遊ぶイベントです。

MC6800でHello, World

Absolute assembler for MC6800/MC6801を使ってMC-10で、MC6847を表示するプログラムを開発して行きます。

f:id:tanam:20191125123044p:plain

VRAM($4000-$57FF)、ROM($E000-$FFFF)の想定で書いていきます。

	ORG	$E000
STACK	EQU	$5000
VRAM	EQU	$4000
START	LDS	#STACK
	LDX	#VRAM
	STX	$82
	LDX	#HELLO
LOOP	LDAA	0,X
	BEQ	QUIT
	INX
	STX	$80
	LDX	$82
	STAA	0,X
	INX
	STX	$82
	LDX	$80
	BRA	LOOP
QUIT	BRA	QUIT
HELLO	FCC	'HELLO,WORLD'
	FCB	$00
	ORG	$FFFE
	FDB	START
	END

TVボーイで動かす場合は、VRAM($1000-$17FF)、ROM($F000-$FFFF)とします。