Yet Another NEC PC-6001 Emulator その3

Androidジョイスティックですが、USBホスト機能でキー入力として拾えることに気付きました。

http://d.hatena.ne.jp/naoya2k/20120212/p1

であれば、Java API側でSTRIG(0)を[RETURN]に変換してしまえば良いですね。これで、SD6031イメージファイル選択プログラムもジョイスティックで使えます。

f:id:tanam:20130804235537p:image:w360

SPACE FIGHTERもジョイスティックでプレイ!

f:id:tanam:20130804235534p:image:w360

qt_input.cpp

/*
	Skelton for retropc emulator

	Qt Version : tanam
	Date   : 2013.05.18 -
*/

#include "emu.h"
#include "vm/vm.h"
#include "fifo.h"
#include "fileio.h"
#ifdef Q_OS_WIN32
#include <mmsystem.h>
#endif

#define STICK0_SPACE 0x80
#define STICK0_LEFT  0x20
#define STICK0_RIGHT 0x10
#define STICK0_DOWN  0x08
#define STICK0_UP    0x04
#define STICK0_STOP  0x02
#define STICK0_SHIFT 0x01

#define KEY_KEEP_FRAMES 3
#define COUNTOF(arr) (sizeof(arr) / sizeof((arr)[0]))

// Qtキーコード  -> 仮想キーコード 変換テーブル
std::map<int, int> VKTable;
static const struct {
int InKey;       // Qtのキーコード
int VKey;        // 仮想キーコード
} VKeyDef[] = {
{ Qt::Key_unknown,      0xff },
{ Qt::Key_1,            '1' }, // 1 !
{ Qt::Key_Exclam,       '1' }, // 1 !
{ Qt::Key_2,            '2' }, // 2 "
{ Qt::Key_QuoteDbl,     '2' }, // 2 "
{ Qt::Key_3,            '3' }, // 3 #
{ Qt::Key_NumberSign,   '3' }, // 3 #
{ Qt::Key_4,            '4' }, // 4 $
{ Qt::Key_Dollar,       '4' }, // 4 $
{ Qt::Key_5,            '5' }, // 5 %
{ Qt::Key_Percent,      '5' }, // 5 %
{ Qt::Key_6,            '6' }, // 6 &
{ Qt::Key_Ampersand,    '6' }, // 6 &
{ Qt::Key_7,            '7' }, // 7 '
{ Qt::Key_Apostrophe,   '7' }, // 7 '
{ Qt::Key_8,            '8' }, // 8 (
{ Qt::Key_ParenLeft,    '8' }, // 8 (
{ Qt::Key_9,            '9' }, // 9 )
{ Qt::Key_ParenRight,   '9' }, // 9 )
{ Qt::Key_0,            '0' }, // 0
{ Qt::Key_Minus,       0xBD }, // - =
{ Qt::Key_Equal,       0xBD }, // - =
{ Qt::Key_AsciiCircum, 0xDE }, // ^ ~
{ Qt::Key_AsciiTilde,  0xDE }, // ^ ~
{ Qt::Key_Backspace,   0x2E }, // Delete
{ Qt::Key_Insert,      0x2D }, // Insert
{ Qt::Key_Escape,      0x1B }, // ESC
{ Qt::Key_Q,            'Q' }, // q Q
{ Qt::Key_W,            'W' }, // w W
{ Qt::Key_E,            'E' }, // e E
{ Qt::Key_R,            'R' }, // r R
{ Qt::Key_T,            'T' }, // t T
{ Qt::Key_Y,            'Y' }, // y Y
{ Qt::Key_U,            'U' }, // u U
{ Qt::Key_I,            'I' }, // i I
{ Qt::Key_O,            'O' }, // o O
{ Qt::Key_P,            'P' }, // p P
{ Qt::Key_At,          0xC0 }, // @ `
{ Qt::Key_QuoteLeft,   0xC0 }, // @ `
{ Qt::Key_BracketLeft, 0xDB }, // [ {
{ Qt::Key_BraceLeft,   0xDB }, // [ {
{ Qt::Key_Return,      0x0D }, // CR
{ Qt::Key_Up,          0x26 }, // ↑
{ Qt::Key_Down,        0x28 }, // ↓
{ Qt::Key_Left,        0x25 }, // ←
{ Qt::Key_Right,       0x27 }, // →
{ Qt::Key_Control,     0x11 }, // L-Ctrl
{ Qt::Key_A,            'A' }, // a A
{ Qt::Key_S,            'S' }, // s S
{ Qt::Key_D,            'D' }, // d D
{ Qt::Key_F,            'F' }, // f F
{ Qt::Key_G,            'G' }, // g G
{ Qt::Key_H,            'H' }, // h H
{ Qt::Key_J,            'J' }, // j J
{ Qt::Key_K,            'K' }, // k K
{ Qt::Key_L,            'L' }, // l L
{ Qt::Key_Semicolon,   0xBB }, // ; +
{ Qt::Key_Plus,        0xBB }, // ; +
{ Qt::Key_Colon,       0xBA }, // : *
{ Qt::Key_Asterisk,    0xBA }, // : *
{ Qt::Key_BracketRight,0xDD }, // ] }
{ Qt::Key_BraceRight,  0xDD }, // ] }
{ Qt::Key_Shift,       0x10 }, // L-Shift
{ Qt::Key_Z,            'Z' }, // z Z
{ Qt::Key_X,            'X' }, // x X
{ Qt::Key_C,            'C' }, // c C
{ Qt::Key_V,            'V' }, // v V
{ Qt::Key_B,            'B' }, // b B
{ Qt::Key_N,            'N' }, // n N
{ Qt::Key_M,            'M' }, // m M
{ Qt::Key_Comma,       0xBC }, // , <
{ Qt::Key_Less,        0xBC }, // , <
{ Qt::Key_Period,      0xBE }, // . >
{ Qt::Key_Greater,     0xBE }, // . >
{ Qt::Key_Slash,       0xBF }, // / ?
{ Qt::Key_Question,    0xBF }, // / ?
{ Qt::Key_End,         0xDC }, // 円
{ Qt::Key_F1,          0x70 }, // F1
{ Qt::Key_F2,          0x71 }, // F2
{ Qt::Key_F3,          0x72 }, // F3
{ Qt::Key_F4,          0x73 }, // F4
{ Qt::Key_F5,          0x74 }, // F5
{ Qt::Key_F6,          0x75 }, // かな
{ Qt::Key_F7,          0x76 }, // カナ
{ Qt::Key_F8,          0x77 }, // GRAPH
{ Qt::Key_F9,          0x78 }, // STOP
{ Qt::Key_Space,       0x20 }, // Space
{ Qt::Key_Home,        0x24 }, // Home
{ Qt::Key_Delete,      0x2E }, // Delete
{ Qt::Key_PageUp,      0x21 }, // PAGE
{ Qt::Key_PageDown,    0x22 }, // PAGE
{ Qt::Key_Tab,         0x09 }, // TAB
{ Qt::Key_Backslash,   0xE2 }, // ろ
{ Qt::Key_Menu,        0    }, // ESCAPE
{ Qt::Key_Alt,         0xff }, // ALT
{ 0,                   0x1b }, // MENU
};

int OSD_ConvertKeyCode( int scode )
{
    if(VKTable.count(scode) == 0){
        qDebug("keycode %x unknown\n", scode);
        return 0xff;
    }
    return VKTable[scode];
}

void EMU::keyPressEvent(QKeyEvent *event)
{
	bool keep_frames = false;
	int code = OSD_ConvertKeyCode(event->key());
	qDebug("p:keycode %x\n", code);
	if (code==VK_LEFT) joy_status[2] |= STICK0_LEFT;
	else if (code==VK_RIGHT) joy_status[2] |= STICK0_RIGHT;
	else if (code==VK_DOWN) joy_status[2] |= STICK0_DOWN;
	else if (code==VK_UP) joy_status[2] |= STICK0_UP;
	else if (code==VK_F9) joy_status[2] |= STICK0_STOP;
	else if (code==VK_SHIFT) joy_status[2] |= STICK0_SHIFT;
	else joy_status[2] |= STICK0_SPACE;

    key_status[code] = keep_frames ? KEY_KEEP_FRAMES : 0x80;
    if (code == 0) ShowPopup();
    return;
}

void EMU::keyReleaseEvent(QKeyEvent *event)
{
	int code = OSD_ConvertKeyCode(event->key());
	qDebug("r:keycode %x\n", code);
	bool keep_frames = false;
	if (code==VK_LEFT) joy_status[2] &= ~STICK0_LEFT;
	else if (code==VK_RIGHT) joy_status[2] &= ~STICK0_RIGHT;
	else if (code==VK_DOWN) joy_status[2] &= ~STICK0_DOWN;
	else if (code==VK_UP) joy_status[2] &= ~STICK0_UP;
	else if (code==VK_F9) joy_status[2] &= ~STICK0_STOP;
	else if (code==VK_SHIFT) joy_status[2] &= ~STICK0_SHIFT;
	else joy_status[2] &= ~STICK0_SPACE;

	if (key_status[code]) key_status[code] &= 0x7f;
    return;
}

void EMU::mousePressEvent(QMouseEvent *event)
{
    if (event->button() == Qt::RightButton) ShowPopup();
    return;
}

void EMU::initialize_input()
{
	// initialize status
	memset(key_status, 0, sizeof(key_status));
	memset(joy_status, 0, sizeof(joy_status));
#ifdef Q_OS_WIN32
	// initialize joysticks
	joy_num = joyGetNumDevs();
	for(int i = 0; i < joy_num && i < 2; i++) {
		JOYCAPS joycaps;
		if(joyGetDevCaps(i, &joycaps, sizeof(joycaps)) == JOYERR_NOERROR) {
			joy_mask[i] = (1 << joycaps.wNumButtons) - 1;
		} else {
			joy_mask[i] = 0x0f; // 4buttons
		}
	}
#endif
	// initialize keycode convert table
	// Qtキーコード  -> 仮想キーコード 変換テーブル初期化
	for( int i=0; i < COUNTOF(VKeyDef); i++ )
		VKTable[VKeyDef[i].InKey] = VKeyDef[i].VKey;
}

void EMU::release_input()
{
    ;
}

void EMU::update_input()
{
#ifdef Q_OS_WIN32
		// update joystick status
		memset(joy_status, 0, 8);
		for(int i = 0; i < joy_num && i < 2; i++) {
			JOYINFOEX joyinfo;
			joyinfo.dwSize = sizeof(JOYINFOEX);
			joyinfo.dwFlags = JOY_RETURNALL;
			if(joyGetPosEx(i, &joyinfo) == JOYERR_NOERROR) {
				if(joyinfo.dwYpos < 0x3fff) joy_status[i] |= 0x01;    // up
				if(joyinfo.dwYpos > 0xbfff) joy_status[i] |= 0x02;    // down
				if(joyinfo.dwXpos < 0x3fff) joy_status[i] |= 0x04;    // left
				if(joyinfo.dwXpos > 0xbfff) joy_status[i] |= 0x08;    // right
				joy_status[i] |= ((joyinfo.dwButtons & joy_mask[i]) << 4);
			}
		}
#endif
}

QtActivity.java


(省略)

//@ANDROID-11
//QtCreator import android.app.Fragment;
//QtCreator import android.view.ActionMode;
//QtCreator import android.view.ActionMode.Callback;
//@ANDROID-11
import android.media.*;

(省略)

    // Audio
    private static Thread mAudioThread;
    private static AudioTrack mAudioTrack;
    public static native void nativeRunAudioThread();

    // Audio
    private static Object buf;
    
    public static Object audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
        
        Log.v("SDL", "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + ((float)sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
        
        // Let the user pick a larger buffer if they really want -- but ye
        // gods they probably shouldn't, the minimums are horrifyingly high
        // latency already
        desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
        
        mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
                channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
        
        audioStartThread();
        
        Log.v("SDL", "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + ((float)mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
        
        if (is16Bit) {
            buf = new short[desiredFrames * (isStereo ? 2 : 1)];
        } else {
            buf = new byte[desiredFrames * (isStereo ? 2 : 1)]; 
        }
        return buf;
    }
    
    public static void audioStartThread() {
        mAudioThread = new Thread(new Runnable() {
            public void run() {
                mAudioTrack.play();
                nativeRunAudioThread();
            }
        });
        
        // I'd take REALTIME if I could get it!
        mAudioThread.setPriority(Thread.MAX_PRIORITY);
        mAudioThread.start();
    }
    
    public static void audioWriteShortBuffer(short[] buffer) {
        for (int i = 0; i < buffer.length; ) {
            int result = mAudioTrack.write(buffer, i, buffer.length - i);
            if (result > 0) {
                i += result;
            } else if (result == 0) {
                try {
                    Thread.sleep(1);
                } catch(InterruptedException e) {
                    // Nom nom
                }
            } else {
                Log.w("SDL", "SDL audio: error return from write(short)");
                return;
            }
        }
    }
    
    public static void audioWriteByteBuffer(byte[] buffer) {
        for (int i = 0; i < buffer.length; ) {
            int result = mAudioTrack.write(buffer, i, buffer.length - i);
            if (result > 0) {
                i += result;
            } else if (result == 0) {
                try {
                    Thread.sleep(1);
                } catch(InterruptedException e) {
                    // Nom nom
                }
            } else {
                Log.w("SDL", "SDL audio: error return from write(short)");
                return;
            }
        }
    }

    public static void audioQuit() {
        if (mAudioThread != null) {
            try {
                mAudioThread.join();
            } catch(Exception e) {
                Log.v("SDL", "Problem stopping audio thread: " + e);
            }
            mAudioThread = null;

            //Log.v("SDL", "Finished waiting for audio thread");
        }

        if (mAudioTrack != null) {
            mAudioTrack.stop();
            mAudioTrack = null;
        }
    }

(省略)

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event)
    {
        if (keyCode==4) keyCode=0x100000;
        if (keyCode==188) keyCode=0x100000;
        if (keyCode==189) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==190) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==191) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==192) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==193) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==194) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==195) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==196) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==197) keyCode=KeyEvent.KEYCODE_ENTER;
        if (QtApplication.m_delegateObject != null && QtApplication.onKeyDown != null)
            return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyDown, keyCode, event);
        else
            return super.onKeyDown(keyCode, event);
    }

(省略)

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event)
    {
        if (keyCode==4) keyCode=0x100000;
        if (keyCode==188) keyCode=0x100000;
        if (keyCode==189) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==190) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==191) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==192) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==193) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==194) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==195) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==196) keyCode=KeyEvent.KEYCODE_ENTER;
        if (keyCode==197) keyCode=KeyEvent.KEYCODE_ENTER;
        if (QtApplication.m_delegateObject != null  && QtApplication.onKeyDown != null)
            return (Boolean) QtApplication.invokeDelegateMethod(QtApplication.onKeyUp, keyCode, event);
        else
            return super.onKeyUp(keyCode, event);
    }

(省略)