PC-6001VX for IS01 その1

PC6001VX 2.0a2がリリースされました。

http://eighttails.seesaa.net/article/305067428.html#more

SDLが切り離されたことで軽くなったはずなので、今度はIS01Android 1.6)向けにビルドしてみました。Androidに対応したQt5.0.2がリリースされていないため、実績のあるNecessitas Qt 4.8.2 for Androidにバックポートして行きます。

※ Qt 5.1.0 Android for arm(GCC 4.7 Qt 5.1.0)でのビルド手順を反映しました(2013/07/09)

※ Qt 5.1.1 for Androidでも動作確認しました。5.1.0と全く同じでした(2013/09/26)

期待出来るのは以下です。

  • メニュー表示
  • スキャンライン表示
  • ステータスバー表示
  • レジューム対応

逆に以下の制限はあると思います。

  • NecessitasではOpenGLを使うためには、APIレベル7(GLES2.0)が必要(古い機種ではそのままでは動かない)
  • NecessitasではMultimediaは実装されていない(サウンド機能はそのままでは動かない)

http://community.kde.org/Necessitas/FAQ#I_need_Audio_support

以下のオプションで制限を回避します。ジョイスティックもないので、SDLも切り離します。

DEFINES += NOJOYSTICK NOOPENGL

例によってソースファイルを解凍してPC6001VX.proを修正します。

PC6001VX_2.0a2_src.tar.gz

#-------------------------------------------------
#
# Project created by QtCreator 2012-01-01T00:17:09
#
#-------------------------------------------------

QT       += core gui network

TARGET = PC6001VX
TEMPLATE = app

DEFINES += NOJOYSTICK NOOPENGL

INCLUDEPATH += src/Qt src/Qt/qtsingleapplication

SOURCES += \
    src/breakpoint.cpp \
    src/config.cpp \
    src/cpum.cpp \
    src/cpus.cpp \
    src/d88.cpp \
    src/debug.cpp \
    src/device.cpp \
    src/disk.cpp \
    src/error.cpp \
    src/graph.cpp \
    src/ini.cpp \
    src/intr.cpp \
    src/io.cpp \
    src/joystick.cpp \
    src/keyboard.cpp \
    src/memory.cpp \
    src/movie.cpp \
    src/p6t2.cpp \
    src/pio.cpp \
    src/psg.cpp \
    src/replay.cpp \
    src/schedule.cpp \
    src/sound.cpp \
    src/status.cpp \
    src/tape.cpp \
    src/vdg.cpp \
    src/voice.cpp \
    src/vsurface.cpp \
    src/device/ay8910.cpp \
    src/device/mc6847.cpp \
    src/device/pd7752.cpp \
    src/device/pd8255.cpp \
    src/device/z80.cpp \
    src/device/z80-dbg.cpp \
    src/Qt/osdQt.cpp \
    src/Qt/pc6001v.cpp \
    src/Qt/configdialog.cpp \
    src/Qt/colorbutton.cpp \
    src/Qt/aboutdialog.cpp \
    src/Qt/common.cpp \
    src/Qt/console.cpp \
    src/p6el.cpp \
    src/p6vm.cpp \
    src/Qt/qtsingleapplication/qtlocalpeer.cpp \
    src/Qt/qtsingleapplication/qtlockedfile.cpp \
    src/Qt/qtsingleapplication/qtlockedfile_unix.cpp \
    src/Qt/qtsingleapplication/qtlockedfile_win.cpp \
    src/Qt/qtsingleapplication/qtsingleapplication.cpp \
    src/Qt/qtsingleapplication/qtsinglecoreapplication.cpp \
    src/Qt/qtp6vxapplication.cpp \
    src/Qt/emulationadaptor.cpp \
    src/Qt/renderview.cpp \
    src/Qt/qtel6.cpp \
    src/Qt/semaphore.cpp \
    src/Qt/thread.cpp

HEADERS  += \
    src/breakpoint.h \
    src/common.h \
    src/config.h \
    src/console.h \
    src/cpum.h \
    src/cpus.h \
    src/d88.h \
    src/debug.h \
    src/device.h \
    src/disk.h \
    src/error.h \
    src/graph.h \
    src/ini.h \
    src/intr.h \
    src/io.h \
    src/joystick.h \
    src/keyboard.h \
    src/keydef.h \
    src/log.h \
    src/memory.h \
    src/movie.h \
    src/osd.h \
    src/p6device.h \
    src/p6t2.h \
    src/pc6001v.h \
    src/pio.h \
    src/psg.h \
    src/replay.h \
    src/schedule.h \
    src/semaphore.h \
    src/sound.h \
    src/status.h \
    src/tape.h \
    src/thread.h \
    src/typedef.h \
    src/vdg.h \
    src/voice.h \
    src/vsurface.h \
    src/device/ay8910.h \
    src/device/mc6847.h \
    src/device/pd7752.h \
    src/device/pd8255.h \
    src/device/z80.h \
    src/device/z80-cdCB.h \
    src/device/z80-cdED.h \
    src/device/z80-cdXC.h \
    src/device/z80-cdXX.h \
    src/device/z80-code.h \
    src/device/z80-tbl.h \
    src/Qt/configdialog.h \
    src/Qt/colorbutton.h \
    src/Qt/aboutdialog.h \
    src/event.h \
    src/p6el.h \
    src/p6vm.h \
    src/Qt/qtsingleapplication/QtLockedFile \
    src/Qt/qtsingleapplication/QtSingleApplication \
    src/Qt/qtsingleapplication/qtlocalpeer.h \
    src/Qt/qtsingleapplication/qtlockedfile.h \
    src/Qt/qtsingleapplication/qtsingleapplication.h \
    src/Qt/qtsingleapplication/qtsinglecoreapplication.h \
    src/Qt/qtp6vxapplication.h \
    src/Qt/emulationadaptor.h \
    src/Qt/renderview.h \
    src/Qt/qtel6.h

FORMS    += \
    src/Qt/configdialog.ui \
    src/Qt/aboutdialog.ui

OTHER_FILES += \
    TODO.txt \
    src/Qt/res/PC-6001.ico \
    src/Qt/res/PC-6001mk2.ico \
    src/Qt/res/PC-6001mk2SR.ico \
    src/Qt/res/PC-6601.ico \
    src/Qt/res/PC-6601SR.ico \
    src/Qt/res/fontz12.png \
    src/win32/PC6001VX.ico \
    src/win32/PC6001VX.rc \
    COPYING \
    README.txt \
    src/Qt/res/font/fonth12.png \
    src/Qt/res/font/fontz12.png \
    android/AndroidManifest.xml \
    android/res/drawable/icon.png \
    android/res/drawable/logo.png \
    android/res/drawable-hdpi/icon.png \
    android/res/drawable-ldpi/icon.png \
    android/res/drawable-mdpi/icon.png \
    android/res/layout/splash.xml \
    android/res/values/libs.xml \
    android/res/values/strings.xml \
    android/res/values-de/strings.xml \
    android/res/values-el/strings.xml \
    android/res/values-es/strings.xml \
    android/res/values-et/strings.xml \
    android/res/values-fa/strings.xml \
    android/res/values-fr/strings.xml \
    android/res/values-id/strings.xml \
    android/res/values-it/strings.xml \
    android/res/values-ja/strings.xml \
    android/res/values-ms/strings.xml \
    android/res/values-nb/strings.xml \
    android/res/values-nl/strings.xml \
    android/res/values-pl/strings.xml \
    android/res/values-pt-rBR/strings.xml \
    android/res/values-ro/strings.xml \
    android/res/values-rs/strings.xml \
    android/res/values-ru/strings.xml \
    android/res/values-zh-rCN/strings.xml \
    android/res/values-zh-rTW/strings.xml \
    android/src/org/kde/necessitas/ministro/IMinistro.aidl \
    android/src/org/kde/necessitas/ministro/IMinistroCallback.aidl \
    android/src/org/kde/necessitas/origo/QtActivity.java \
    android/src/org/kde/necessitas/origo/QtApplication.java \
    android/version.xml

RESOURCES += \
    src/Qt/pc6001vx.qrc

※ Qt 5.1.0 Androidでも、Multimediaを有効にするとうまく動かなかった。Multimediaはまだ限定的なサポートのようです。

ビルドしてみてエラーを消していきます。QT5関連のエラーは以下の#ifで対処します。

common.cpp、qtel6.cpp

#include <QtCore>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#include <QtGui>
#else
#include <QtWidgets>
#endif

renderview.cpp

#ifndef NOOPENGL
#include <QtOpenGL>
#endif


(省略)

#ifndef NOOPENGL
        if(qApp->property("TILTEnabled").toBool()){
            rotate(qApp->property("TILT").toReal());
        }
#endif

pc6001v.cpp

int main( int argc, char *argv[] )
{
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
    QtP6VXApplication app(argc, argv);

    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
#else
    //X11の場合
    if (QGuiApplication::platformName() == QLatin1String("xcb")){
        QCoreApplication::setAttribute(Qt::AA_X11InitThreads);
    }

    QtP6VXApplication app(argc, argv);
#endif
    setlocale(LC_ALL,"Japanese");

qtp6vxapplication.cpp

#include <QtCore>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#include <QtGui>
#else
#include <QtWidgets>
#endif

(省略)

bool QtP6VXApplication::notify ( QObject * receiver, QEvent * event )
{
    Event ev;
    ev.type = EV_NOEVENT;

    switch(event->type()){
    case QEvent::KeyPress:
    case QEvent::KeyRelease:
    {
        QKeyEvent* ke = dynamic_cast<QKeyEvent*>(event);
        Q_ASSERT(ke);
        int keyCode = ke->key();

        // 「ろ」が入力できない対策
        quint32 nativeKey = ke->nativeScanCode();
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#else
        //X11の場合
        if (QGuiApplication::platformName() == QLatin1String("xcb")){
            if(keyCode == Qt::Key_Backslash){
                keyCode = nativeKey == 97 ? Qt::Key_Underscore : Qt::Key_Backslash;
            }
        }
        //Windowsの場合
        else if (QGuiApplication::platformName() == QLatin1String("windows")){
            if(keyCode == Qt::Key_Backslash){
                keyCode = nativeKey == 115 ? Qt::Key_Underscore : Qt::Key_Backslash;
            }
        }
#endif

osdQt.cpp

#include <QtCore>
#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
#include <QtGui>
#else
#include <QtWidgets>
#endif
#ifndef Q_OS_ANDROID
#include <QtMultimedia>
#endif

(省略)

#ifndef Q_OS_ANDROID
//サウンド関連
QPointer<QIODevice> audioBuffer = NULL;
QPointer<QAudioOutput> audioOutput = NULL;
#endif

(省略)

////////////////////////////////////////////////////////////////
// オーディオデバイスオープン
//
// 引数:	obj			自分自身へのオブジェクトポインタ
//			callback	コールバック関数へのポインタ
//			rate		サンプリングレート
//			sample		バッファサイズ(サンプル数)
// 返値:	bool		true:成功 false:失敗
////////////////////////////////////////////////////////////////
bool OSD_OpenAudio( void *obj, CBF_SND callback, int rate, int samples )
{
#ifndef Q_OS_ANDROID
    QAudioFormat format;
    format.setCodec("audio/pcm");
    format.setChannelCount(1);
    format.setSampleRate(rate);
    format.setSampleSize(16);
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::SignedInt);

    if(audioOutput){
        audioOutput->deleteLater();
    }

    QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
    if (!info.isFormatSupported(format)) {
        qWarning()<<"raw audio format not supported by backend, cannot play audio.";
        return false;
    }

    audioOutput = new QAudioOutput(info, format, qApp);
#endif
    return true;
}


////////////////////////////////////////////////////////////////
// オーディオデバイスクローズ
//
// 引数:	なし
// 返値:	なし
////////////////////////////////////////////////////////////////
void OSD_CloseAudio( void )
{
#ifndef Q_OS_ANDROID
    if(audioOutput){
        audioOutput->stop();
    }
#endif
}


////////////////////////////////////////////////////////////////
// 再生開始
//
// 引数:	なし
// 返値:	なし
////////////////////////////////////////////////////////////////
void OSD_StartAudio( void )
{
#ifndef Q_OS_ANDROID
    if(audioOutput){
        audioBuffer = audioOutput->start();
        //#PENDING これではグローバルボリュームを変えてしまう?
        audioOutput->setVolume(0.3);
    }
#endif
}


////////////////////////////////////////////////////////////////
// 再生停止
//
// 引数:	なし
// 返値:	なし
////////////////////////////////////////////////////////////////
void OSD_StopAudio( void )
{
#ifndef Q_OS_ANDROID
    if(audioOutput){
        audioOutput->suspend();
    }
#endif
}

////////////////////////////////////////////////////////////////
// オーディオストリーム書き込み
//
// 引数:	stream  書き込むデータへのポインタ
//      samples 書き込むバイト数
// 返値:	なし
////////////////////////////////////////////////////////////////
void OSD_WriteAudioStream(BYTE *stream, int samples)
{
#ifndef Q_OS_ANDROID
    if(audioBuffer){
        audioBuffer->write((const char*)stream, samples);
    }
#endif
}


////////////////////////////////////////////////////////////////
// 再生状態取得
//
// 引数:	なし
// 返値:	bool		true:再生中 false:停止中
////////////////////////////////////////////////////////////////
bool OSD_AudioPlaying( void )
{
#ifndef Q_OS_ANDROID
    return audioOutput->state() == QAudio::ActiveState;
#else
    return false;
#endif
}

たった、これだけの変更でビルドが通って実行出来ました。例によって、設定ファイル(/sdcard/pc6001v/pc6001vx.ini)を事前に準備して起動させてみます。

////////////////////////////////////////////////////////////////
// 設定及び諸ファイル(ROMなどの初期位置)パス取得
//
// 引数:	なし
// 返値:	char *		取得した文字列へのポインタ
////////////////////////////////////////////////////////////////
const char *OSD_GetConfigPath( void )
{
    PRINTD( OSD_LOG, "[OSD][OSD_GetModulePath]\n" );
    static char mpath[PATH_MAX] = "";	// モジュールパス取得用

    //Windowsの場合はexe本体と同じ場所。
    //それ以外(UNIX系を想定)は ~/.pc6001vx を返す
#ifdef WIN32
    QString confPath = qApp->applicationDirPath() + QDir::separator();
#else
  #ifdef Q_OS_ANDROID
    QString confPath = QString("/sdcard/pc6001v");
  #else
    QString confPath = QDir::homePath() + QDir::separator() + QString(".pc6001vx");
  #endif
#endif

    sprintf( mpath, "%s", confPath.toUtf8().data() );
    AddDelimiter( mpath );	// 念のため
    UnDelimiter( mpath );
    return mpath;
}

何か画面ちっちゃいですが、ステータスバーが表示されています。なおサウンド機能は、PC6001VX for IS11Tその2を見ながら、VX1.1のソースコードを移植したら鳴りましたが、動作が重くなるのであえて移植しない方針とします。

f:id:tanam:20130514225135p:image:w360