2020年8月1日土曜日

自前Z80エミュレータのテスト完了

昨年作成したきり放置気味だった自作Z80エミュレータ(C++)でシステムテストを実施して完成させました。

システムテストは、別途ゲームギアのエミュレータを作成して実施しました。(なお、作成したゲームギアについては、現在進行系でゲームギア関連ビジネスが動いているようなので、念のため非公開にしておきます...既に完成度が高いエミュレータなら沢山ありますし)

Z80を搭載したゲーム機なんて沢山あるんじゃないかと思いきや、意外と少なくて、シングルコアで純正Z80を搭載しているのは、調べた限りではSG-1000、Sega MARK III、Sega Master System、Game Gearぐらいのようです。(ゲームボーイがZ80カスタムなので若干惜しい)

そして、その中で私がハードとソフトを唯一持っているのがGGだったので、GGエミュレータを作ることにしました。なお、GGのハード仕様については、以下の資料を読んで把握。
セガのハードは中々面白くて、初代SG-1000の中身はだいたいMSXと同じ構造です。
そして、SM3、SMS、GGはVDP(Video Display Processor)をゲーム向きの専用設計(モード4)に切り替えていますが、ハード構造的にはSG-1000と(ついでに、オセロマルチビジョンとも?)互換性を持たせられる設計になっているようです。

モード4のVDPはかなりシンプルな構造です。
16KBのRAM(VRAM)の中にパターンテーブル、ネームテーブル、OAM(Sprite Attribute)がある形。
ファミコンのハード仕様を理解していれば、容易に理解できると思います。
ファミコンと違ってネームテーブルがかなり扱いやすくなっていて、キャラクタパターン(1byte)と属性データ(1byte)のセット(2byte)で1セルを表現する形になっています。(ファミコンでゲームを作った事がある人なら分かると思いますが、かなりプログラムし易いメモリレイアウトになっています)

GGの場合、MSX相当(SG-1000相当)のモード実装は不要っぽかったので、2週間ぐらいで作れました。(途中4連休もあったので楽勝)
GG Dumperで手持ちのカセットからROMを吸い出して動作検証したところ、手持ちソフトの範囲では10割正常に動きました。(そんなに沢山持ってないのが悔やまれる...)

【動作確認したソフト】
・ベアナックル2
・GG忍
・ソニック・ザ・ヘッジホッグ2
・ドラゴンクリスタル
・デビリッシュ
・魔導物語1(PSG/PCMでちゃんと喋ります)
・魔導物語A
本体が通電不可能なので、今度ハードオフで見つけたら調達予定...
(ついでに検証ソフトも増やしたい)

私の自前Z80の場合、ほぼ実機相当のマシンサイクル単位(実は厳密には少し違うけど...)で同期実装が可能なので、エミュレータ開発者の誰もが苦労する同期実装周りで苦労することはありませんでした。GGの場合、LCDディスプレイ領域は160x144ピクセルですが、内部的には1フレーム342x262ピクセル(NTSC)で処理していて、4Hzにつき3ピクセル描画する形で同期すれば完璧な同期を実現できます。(CPUパワーはかなり消費しますが)

このテスト過程で発見したZ80側のバグがこのPull Requestでfixされています。

先日、このZ80エミュレータにゲームボーイのCPU(LR35902)のコンパチブルモードも実装したので、ゲームボーイのエミュレータも作ろうと思えば作れますね。(作るとは言っていない)

2020年7月30日木曜日

ブログ のデザイン変更

ブログのデザイン変更をかなり大幅に変更しました。

ダークテーマに慣れすぎたせいで、微妙に明るくて見にくかったのでダークテーマのデザインに変更しつつ、そろそろ細かい字を見るのがキツイお年頃なのでフォントサイズなどを大きめにしてみました。

過去記事で色が見難い箇所があるかもしれません。
色は基本的にデフォルトから変えるべきではないですね。(今後は気をつけよう)

2020年7月29日水曜日

ゲームボーイのCPUについて

ゲームボーイのCPUについて、誤った技術情報が検索トップの方に表示されるので、私が把握する限りでZ80との仕様差を書いておきます。

ゲームボーイのCPUとは?
☓ 8080
☓ Z80
○ 8080カスタム or Z80カスタム(正確にはSHARPのLR35902)

Z80との仕様差:
  1. 1マシンサイクルが全て4Hz(なので全ての命令のTサイクルは4で割り切れる)
  2. 一部フラグ(S, P/V, X, Y)が存在しない(※X, Yはundocumented)
  3. レジスタ IX, IY が存在しない
  4. プレフィクスDD(IX命令)、FD(IY命令)が存在しない
  5. 裏レジスタが存在しない(ので、EX/EXX命令も存在しない)
  6. IN/OUT命令が存在しない(I/Oは$FF00〜$FFFFにメモリマップド)
  7. プレフィクスED命令(結構便利な命令群)が存在しない
    1. $ED $4D (RETI命令) については $D9 (EXX命令) へ移動
    2. Rレジスタに実質アクセスできない(Rレジスタ=DRAMへ通電するためのレジスタで命令実行毎に0〜127の範囲でインクリメントされ続けるもの。この特徴を利用してゲームでは乱数ジェネレータとして利用されることがあるが、ED命令削除の影響でゲームボーイでは利用できないので、乱数を使いたい場合は自前の乱数テーブル等を作る必要がある)
  8. プレフィクスCBのSLL命令(undocumented)がSWAP命令に置き換わっている(SWAPはレジスタ上位4bitと下位4bitを入れ替えるLR35902固有機能)
  9. DJNZ命令がSTOP命令に置き換わっている(STOPはHALTよりも省電力な割込待機機能)
  10. LDI/LDD命令をシンプル化(Bレジスタのデクリメントを行わず、HLのインクリメント・デクリメントのみ行う)
    1. LDIR/LDDR(OTIR/OTDR)相当の命令が無いのでBレジスタのデクリメントが要らない(これらの命令は結構便利だが、継続条件時に5Hzの無駄クロックが発生する関係で、性能が求められる箇所では使用せずにOUTI, OUTI, OUTI...などといった形でRじゃない方の命令を並べて書くのが普通...つまり、ゲーム用CPUなら無くても良いよね という判断だと思います)
  11. LDH命令を追加($FF00〜$FFFFへのアクセス命令のサイクル数を少なくしたもの=I/O命令の代替用)
  12. スタックポインタを便利に使えるようにする命令群(LD (addr),SP / ADD SP,d / LDHL SP,d)を追加(※LDHLはADD SP,dの結果をHLに代入する命令)
  13. LD (addr),A と LD A,(addr) の命令コードを変更
  14. LD HL, (addr) と LD (addr), HL を廃止
私の認識はだいたいこんな感じ。

これで本当に正しいか少し自信がないので、自前Z80エミュレータにLR35902コンパチモードを追加して検証してみようと思っているところです。
https://github.com/suzukiplan/z80/pull/17

(以下、余談)
ゲームボーイのCPUが8080派生なのかZ80派生なのかという議論を目にしますが、機能的には8080とZ80のどちらにも近いと思います。レジスタセットは8080に近いし、豊富なシフト命令(CB prefix instruction set)はZ80に近い。また、IN/OUT命令をメモリマップで代用しているから6502っぽさも感じられます。(Z80をよりゲーム用に洗練させた感じなので、GBZ80という通称が一番しっくりくる)
敢えて、8080派生 or Z80派生のどちらが正しいかを「CPUの設計過程でどちらをベースCPUにしていたか」という観点で推測すると、Z80派生が正しいと思います。というのも8080をLR35902化するのと、Z80をLR35902化するのなら後者の方が設計コストが圧倒的に低いので。(8080派生だとするとCBプレフィクス命令の追加設計が物量的に重そうだし、bit/set/res命令辺りは普通にand/orで代用できてand/or比で性能的なメリットも無いから、ゲーム用の機能としてわざわざ新設する意味がないような気がするので)

2020年6月1日月曜日

AWS + Docker + Redmine の簡単な構築手順(pluginを使いたい場合)

AWS/EC2(Amazon Linux 2)でDockerを使ってRedmineの環境構築をした際のメモです。公式のredmineのDockerイメージを使い、尚且プラグインを使いたい場合、多分これが一番簡単だと思います。

(0) スワップ領域確保

EC2の弱いサーバ(t2.micro)で動かす場合、redmineを動かすと割とカジュアルに「メモリ不足」で落ちます。スワップ領域を1GBほど確保すれば、t2.microでも問題なく動きます。(t2.microのお値段は30日あたり$4.176)

dd if=/dev/zero of=/tmp/swap.img bs=1M count=1024

chmod 600 /tmp/swap.img

mkswap /tmp/swap.img

swapon /tmp/swap.img

free


(1) docker / docker-composeをインストール

sudo yum install -y docker
sudo systemctl enable docker
sudo systemctl start docker

sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

(2) redmineのDockerfileを作成

sudo mkdir ~/redmine
sudo mkdir ~/redmine/work
sudo mkdir ~/redmine/redmine
sudo vi ~/redmine/redmine/Dockerfile

以下、Dockerfileの内容:
FROM redmine:4.1-passenger
RUN apt-get update && apt-get install build-essential
WORKDIR /home/ec2-user/redmine/work
RUN bundle install
RUN rake redmine:plugins:migrate RAILS_ENV=production

メモ: 当初docker-compose.ymlだけで「image: redmine:4.1-passenger」を指定して構築したのですが、それだとプラグインをビルド(bundle install)時に「make not found」でビルド失敗してしまいます。公式dockerイメージにはbuild-essentialが入っていないので、公式dockerイメージは(そのままでは)pluginを導入することができません。なので、redmine用のDockerfileを個別に作成して、RUNスクリプトでbuild-essentialをインストールしつつ、ついでにbundle install + DBマイグレーションを実行しておくことが必須です...
今どきredmineを使う所も稀かもしれないけど、その中でもpluginを使わない人なんて居るのだろうか?なので、(Dockerポリスからは怒られるかもしれないが)公式のdockerイメージにbuild-essentialを入れておくべきではなかろうか。

(3) docker-compose.ymlを作成

sudo mkdir /var/lib/redmine
sudo mkdir /var/lib/redmine/config
sudo touch /var/lib/redmine/config/configuration.yml
sudo vi ~/redmine/docker-compose.yml

以下、docker-compose.ymlの内容:
version: '3.5'

services:
  redmine:
    build: ./redmine
    container_name: redmine
    ports:
      - 3000:3000
    environment:
      TZ: Asia/Tokyo
      REDMINE_DB_MYSQL: mysql
      REDMINE_DB_DATABASE: redmine
      REDMINE_DB_USERNAME: redmine
      REDMINE_DB_PASSWORD: redmine
      REDMINE_DB_ENCODING: utf8
    depends_on:
      - mysql
    restart: always
    volumes:
      - /var/lib/redmine/files:/usr/src/redmine/files
      - /var/lib/redmine/log:/usr/src/redmine/log
      - /var/lib/redmine/plugins:/usr/src/redmine/plugins
      - /var/lib/redmine/public/themes:/usr/src/redmine/public/themes
      - /var/lib/redmine/config/configuration.yml:/usr/src/redmine/config/configuration.yml

  mysql:
     image: mysql:5.7
     container_name: mysql
     restart: always
     environment:
       TZ: Asia/Tokyo
       MYSQL_ROOT_PASSWORD: devops
       MYSQL_DATABASE: redmine
       MYSQL_USER: redmine
       MYSQL_PASSWORD: redmine
     volumes:
       - mysql-data:/var/lib/redmine/mysql
     command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci

volumes:
  mysql-data:
    name: mysql-redmine

(4) プラグインの追加方法

  1. /var/lib/redmine/plugins以下にgit cloneなりコピーなりして配置
  2. docker-compose restart
起動が上手く行かない場合、ログを見るなりdocker-compose upとかで起動するなりして失敗原因を確認。多分、何かしらのサードパーティーツールが足りていない筈なので、必要なやつをDockerfileのapt-get installに追加してイメージ再作成すればOK。(build-essentialだけあればほぼ問題無いと思いますが念の為)

(5) メール配信をしたい場合

  1. /var/lib/redmine/config/configuration.yml に必要な設定を追加
  2. docker-compose restart
SESを使う場合はこんな感じ:
production:
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      address: "SMTP settingsに書かれているホスト名"
      port: 587
      domain: "ドメイン(※ドメイン認証は無くても大丈夫かも)"
      user_name: "取得したSMTPクレデンシャルのユーザ名"
      password: "取得したSMTPクレデンシャルのパスワード"

(6) Redmineのバージョンを変更したい場合

DockerfileのFROMを修正した後、

docker-compose stop
docker-compose up -d

とかでイケるはず。

(7) クラウド環境で動かしたりとか

ここまでで記載した内容でとりあえずオンプレミスでは正常に動きます。
クラウド環境というかインターネット上で動かす場合に必要な設定を記載しておきます。
  • Route53やお名前.comなどで独自ドメインを確保
  • ACM(Certification Manager)で、確保したドメイン(ex: example.com) or そのサブドメイン(ex: redmine.example.com)の証明書を作成
  • ELB(ロードバランサ)を作成(リスナーはhttps※ポート番号はウェルノウンポートを避ける、ターゲットは作成したインスタンスのhttpポート3000/tcpへオーバロード & ACMで作成した証明を割り当て)
  • Route53の確保したドメインのホストゾーンにCNAMEレコード追加してELBのホスト名に繋ぐようにする(Elastic IPを割り当てているのであればAレコードでもOKだけどElastic IPは有限なのでCNAMEの方が多分都合が良い筈)
  • Redmine自体の設定でユーザ作成は無効化(管理者が対応)、認証を必須、パスワードログインは基本的にせず自社のGsuiteアカウントを使ってGoogleアカウント認証(それができるプラグインがあり自社ドメインに限定等が出来る)でログインして貰うようにする。
  • adminの無効化方法は分からなかった(私はRedmineにはまだ詳しくない)ので、とりあえずクソ長くて解読しにくいパスワードにして対処
上記のような感じで設定すれば、多分、セキュリティレベルとしては普通のクラウド版Redmineサービス(MyRedmineとか)よりも高い筈。

月額コストはだいたい850円〜900円ぐらい。
※とりあえず、EC2はt2.microでEBSに全部載せという雑な構成で動かす場合
※EC2の無料分に収まれば多分月50円(データ転送やSESのコスト分のみ)ぐらいかな(使用頻度による...)
多分、クラウドサービス版RedmineではMyRedmineが一番安い筈ですが、それでも月8,000円掛かります。Redmine自体割と枯れたシステム(※褒め言葉)なので、社内運用とかであれば保守の人員コストもそんなに掛からないことを勘案すると、自社で構築してしまった方が多分コスパが良いです。((1)〜(7)までの構築に掛かる人件費ですが、AWSに慣れた人なら0.5人日以下でサクッとできてしまいます)
自社で何らかのWebサービスを構築する or 運用しているのであれば、その練習台(インフラエンジニアの新人研修ネタ?)としても丁度良いかも。DBをAurora、EBSに乗せているfilesディレクトリ(添付ファイルデータ)をEFSに変えればAutoScalingを用いてスケールアウトさせることも出来るので、スケーラビリティ設計の練習台にもできそう。

リモートワーク(テレワーク)対応で、今までオンプレで使っていたRedmineをクラウドサービスに移行しようとして、MyRedmineを使う予算承認が降りたのですが、MyRedmineを試食してみたところ「これなら自分で作った方が早いし安い」と思い自前構築した時のメモでした。(今更Redmine...と思いつつ、こういうケースって多いかも?)

実のところ、構築するのが面倒だった(0.5人日で済むとはいえ一応工数が掛かる)のでMyRedmineで良いじゃん(安いし)と思っていたのですが、MyRedmineの場合、契約書に決済者(※私は決済者ではない)の手書きサイン or 会社名が入った社印が必要とのことだった(※要するにWeb申し込みで完結しない契約タイプだった)ので、いわゆる「ハンコ打ちに会社行かなければいけないヤツ」だったことが自前構築の決断要因でした。
会社に行くと往復2時間(0.25人日)無駄にするし、決済者を捕まえるためのスケジュール調整にも無駄に工数が掛かるので、0.5人日掛けて自前構築した方がお得な筈。

2020年5月14日木曜日

電子マネー

政府からの特別給付の手続きで、せっかく電子証明書付きのマイナンバーカードを持っているものの、スマホ(旧型iPhoneSE)がNFC非対応だったので新型iPhoneSEに買い替えました。

申請自体それで滞りなく行えたので良かったのですが(...ただし、現状のマイナンバー制度は色々と不備が多く市区町村の行政サイドが色々混乱しているらしいので、実は郵送申請にした方が良かったのかもしれませんが)、それよりもApplePayが予想以上に便利だったので端末を買い替えて良かったなと。

今まで電子マネーは主にVIEW Suicaを使っていて、チャージ手段はオートチャージでした。ところが、2月末からリモートワークになり改札を通る機会が激減。電車に乗る機会は、月に1回あれば良い方(3月=0回、4月=1回、5月=今の所0回)になり、その結果オートチャージができず、仕方なく券売機で現金チャージをするという「電子マネーとは?」という状態でした。

現金は、調達するのにATM手数料が掛かるので極力使いたくないです。
なので私は、現金しか使えない店は原則使いません。
ついでに今なら、現金(特に硬貨)は衛生面の関係で触ることが憚れますし。

新型iPhoneSEを導入したことでApplePayが使えるようになったので、メイン電子マネーをモバイルSuicaに切り替え、VIEW Suica(クレジットカード)からチャージする運用に変更することにしました。

ApplePayなら、何処で幾ら使ったがひと目で分かるので、支出管理も大分楽になります。
また、スリープ状態でも決済できるのでQR決済(スマホ決済)よりも使い勝手が良いです。

QR決済については、近所のスーパーが電子マネーはPayPay(QR決済)しか利用できない関係でとりあえずPayPayも使ってますが、やはり決済の都度アプリを起動するのは面倒臭い。

多分、QR決済は(Origamiの実質破綻などがありましたが)NFC対応デバイスが普及するまでの過渡的な存在だと思うので、将来的には(NFC対応デバイスが普及しきったら)無くなると思っています。

恐らく、将来的にはPayPayは生き残るためにやむを得ずQRを捨ててNFCなりFeliCaなりになると思いますが、そうなると私ならSuicaに切り替えてPayPayは捨てますね。
Expressカードはひとつしか選べないので。

2020年5月11日月曜日

CPU dummy read (MOS6502)

GW中にMOS6502のエミュレータを作ったのですが、
https://github.com/suzukiplan/M6502
そのテストをするためにファミコンエミュレータを開発中です。
https://github.com/suzukiplan/OpenNES

NesDev Wikiで公開されているテストコードを全部通すことが当面の目標。
https://wiki.nesdev.com/w/index.php/Emulator_tests

とりあえず、branch_timing_testsは無事全部通りました。



この勢いで、cpu_dummy_readsも通そうとしたのですが、失敗...

CPU Dummy Readsってそもそも何?と思い、スレッドを確認。
https://forums.nesdev.com/viewtopic.php?p=31629

absolute X、indirect Yでページがクロスオーバーするとpenalty clockが発生するのですが、その時、

ldx #$22 lda $20E0,x ; dummy read from $2002 ldx #$22 lda $20E2,x ; dummy read from $2004 ldx #$22 lda $3FE0,x ; dummy read from $3F02

という感じでdummy readが発生するらしい。

という訳で対策
https://github.com/suzukiplan/M6502/commit/ff19b729a7132554ea4be5a714ca894fade489d2

absolute Yの時についてはスレッドで言及されていなかったのですが、一応absolute Yの時にもdummy readする感じにしてみました。(これが正しいかは不明)

2020年4月10日金曜日

Sign in with Apple対応メモ

サードパーティー製のログイン機能を実装しているiOSアプリが、Sign in with Appleに対応していないと審査でリジェクトされるようになった(リジェクトされた)ので、マッハ(1.5日ほど)で対応した時の要点メモを残しておきます。

【要点】
以下、C = クライアントサイド & S = サーバサイド です。

  • C: ASAuthorizationAppleIDButtonでSign in with Appleボタンを設置
  • C: ASAuthorizationControllerで認証UIを呼び出す
  • C: ASAuthorizationController.delegateで認証結果を受け取る
  • C: 認証コード(ASAuthorizationAppleIDCredential.authorizationCode)をサーバへ送信
  • S: apple-authへ受け取った認証コードを渡し、Appleのサーバからsub等を取得(こんな感じ)して自サービスのアカウントの紐付けとかをする

【サーバサイド】
  • サードパーティー製ライブラリを使うほどのものでもない対応内容ですが、急いでいたし、apple-authの説明が分かりやすかったので採用(感謝)
  • ハマり所は特に無し(例によってApple Developerでの諸々の設定が面倒で、provisioning profileの更新周りで小一時間手間取る程度)
【クライアントサイド】
  • 特にUI(導線設計)周りは指示通りにやらないとリジェクト・リスクがあるので注意した(なんやかんやでこれが一番対応コストが掛かる。デザイナとSlackで半日程度やりとりしてザックリと決めて即日実装)
  • 唯一のハマり所は、ASAuthorizationAppleIDCredential.authorizationCodeがNSData型だったので「Base64にするん?」と勝手に想像して実装したことぐらい。単純にこれはUTF8のconst char*型データなので以下のようにすれば良い。
```objective-c
NSString* codeText = [[NSString alloc] initWithData:code encoding:NSUTF8StringEncoding];
```

【雑感】
  • タッチIDで認証できるのは中々快適
  • landscape専用のアプリだが、認証画面がportrait専用(iPhoneの場合)
    • 多分、フェースID絡みの関係で、縦にせざるを得なかったのではないかと想像(やっぱり、タッチIDこそが至高)

合理的ではないものを作りたい

ここ最近、実機版の東方VGSの開発が忙しくて、東方VGSの曲追加が滞っています。 東方VGS(実機版)のデザインを作りながら検討中。基本レトロUIベースですがシークバーはモダンに倣おうかな…とか pic.twitter.com/YOYprlDsYD — SUZUKI PLAN (...