学研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
Q-bert (Japan) (Othello Multivision)-decrypted.SG

今度はSDカード上のバイナリを.NET Coreを使って直接復号化してみます。SecretKeyは個体毎に異なる(DNAから生成されます)のでこのままでは使えないはずです。

Program.cs

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

class Program
{
	static void Main(string[] args)
	{
		if (args.Length<1 || args.Length>1) {
			Console.WriteLine("\nUsage: AES128Decrypt [file path]");
			return;
		}

		FileStream fs = new FileStream(args[0], FileMode.Open, FileAccess.Read);
		byte[] bs = new byte[fs.Length];
		fs.Read(bs, 0, bs.Length);
		fs.Close();

		byte[] be = AES128Decryptor.Decrypt(bs);

		string bas = System.IO.Path.GetFileNameWithoutExtension(args[0]);
		string ext = System.IO.Path.GetExtension(args[0]);
		FileStream fe = new FileStream(bas+"_decrypted"+ext, FileMode.Create, FileAccess.Write);
		fe.Write(be, 16, be.Length-16);
		fe.Close();

		Environment.Exit(0);
	}

	private static readonly byte[] SecretKey = new byte[] {0x59,0xB7,0xAD,0x89,0x7D,0xD4,0x41,0x2C,0xAB,0xA3,0xBB,0x85,0xE2,0xC8,0x3C,0xC8 };

	public class AES128Decryptor
	{
		public static byte[] Decrypt(byte[] encryptedValue)
		{
			var iv = new byte[16];
			var byteEncryptedValue = new byte[encryptedValue.Length - 512+16];
			Buffer.BlockCopy(encryptedValue, 16, iv, 0, iv.Length);
			Buffer.BlockCopy(encryptedValue, 512-16, byteEncryptedValue, 0, byteEncryptedValue.Length);
			using (var aes = creteAesManaged(iv))
			{
				var decryptedValue = aes.CreateDecryptor().TransformFinalBlock(byteEncryptedValue, 0, byteEncryptedValue.Length);
				return decryptedValue;
			}
		}

		private static AesManaged creteAesManaged(byte[] iv)
		{
			var aes = new AesManaged();
			aes.KeySize = 128;
			aes.BlockSize = 128;
			aes.Mode = CipherMode.CBC;
			aes.IV = iv;
			aes.Key = SecretKey;
			aes.Padding = PaddingMode.None;
			return aes;
		}
	}
}

RETRON5でゲームギアをDUMPする

RETRON5改に3IN1アダプターをつないで、ゲームギアカートリッジをDUMPしてみます。

www.amazon.com

まずはゲームギアで遊べるまで本体をアップデートします。つぎに以下のCFWを適用する事で、コントローラケーブルをPCにつなぐとADB接続でrootが有効になります。

www.retro5.net

ゲームギアカートリッジで遊ぶと/mnt/ramにバイナリデータが生成されます。

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

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

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

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)とします。