プチコンで遊ぼう! (はてなブログ版)

任天堂3DSのプチコンで遊ぼう! [twitter:@eida_s]

はてなダイアリーから移行しました。 はてなダイアリーのURLを開いても自動的にこちらにリダイレクトされますのでご了承ください。

Petit-Z80ちょっとだけ進捗

Petit-Z80がちょっとだけ進捗。

■ツール類を作ってみた

デバッグ作業を楽にするため、いくつかツール類を作りはじめた。

メモリの中を確認するZ80_Mem_Viewを作った。

まだ基本的な機能しかないが、これでメモリの確認が少しだけ楽に。

また、テキストファイルから16進データをメモリにロード/セーブする、
Z80_Mem_Load、Z80_Mem_Saveを作った。
これはまだテストもしていない。


仮想マシンの対象を検討した

こちらが本題。
最初の仮想マシンはテスト用に適当に作ったもので、
テキストRAMに書きこまれたデータを、一定期間ごとに
全画面に書きだすだけのものだった。

現実のパソコンを実時間でエミュレートするつもりはないのだけど、
実際のプログラムを使わないとテストがしづらいのと、
それなりのものが動かないと達成感がないので、
現実のパソコンの仕様をすごく簡略化した仮想マシンを作って、
既存の実際のプログラムを動かしてみたいと考えた。

この「現実のパソコン」にはいくつか条件がある。
(1)既存のプログラムが入手可能である
 これが目的なので、入手不可能な場合は意味がない。
 プログラムとしては、リアルタイム性がなく、それなりの画面を備えたゲーム、というのがよさそうなところだろう。

(2)非力すぎない
 作る意味がわからなくなるレベルものはやめておこう。

(3)プチコン3号で実現できる範囲
 これは(2)とは逆になるが、解像度が高くて物理的仕様が1対1対応できないとか、
 自分の実力で実現が難しいものとかはやめる。

この3つの条件で、かつ知名度や自分の勝手なイメージから、
「現実のパソコン」を3つに絞った。
PC-6001
MZ-700
MSX


さて、上記の条件に加えて、さらに重要な前提条件がもう一つある。

(4)「現実のパソコン」の仕様を自分が理解可能なレベルで解説された資料を入手できる
 上記の機種は、いずれも所有したことがなく、プログラミングの経験もない。
 はじめてプログラミングするのにやさしい(資料が揃っている)機種はどれなのか?

この(4)の条件を満たすのがどれかを探すのが、今回のお題である。

PC-6001

調べた資料

自分がブックマークしたURLは以下。
http://homepage2.nifty.com/akikawa/basic.html
http://p6ers.net/hashi/index.html
http://www.tiny-yarou.com/asmdev/asmdev.html
http://homepage2.nifty.com/akikawa/sword/index.html
http://papicom.net/p6v/index.html
http://sbeach.seesaa.net/
http://www.geocities.jp/submarine600/html/p6/tate.html
しかし、私のレベルでは基礎知識が不足しているのか、PC-6001の仕様を理解できた気がしない。
スクリーンモードそのものの仕様もわからなかった。

結果

残念ながら、自分の理解レベルでPC-6001のサブセットとなる仮想マシンを作るのは無理そう。

MZ-700

調べた資料

MZ-700マシン語プログラミング入門
http://www.maroon.dti.ne.jp/youkan/mz700/pg/index.html
I/Oマップ
http://www.maroon.dti.ne.jp/youkan/mz700/mzioframe.html
に極めて簡潔に記載されており、必要にして十分な情報量。

結果

マシンのもともとの仕様がシンプルなことも幸いしてか、非常に分かりやすい資料をすぐに見つけることができた。
また、マシンの仕様そのものも、今回の実装には都合がよさそうである。

MSX

調べた資料

テクハンwiki化計画
http://ngs.no.coocan.jp/doc/wiki.cgi/TechHan
MSX2だが、紙のテクニカルハンドブックをそのままwiki化しようとしたものらしく、極めて貴重な資料。
図が非常に多いので、理解がしやすい。
画面の仕様は4部のあたり。

結果

MSXにはスクリーンモードがたくさんあって把握が大変だが、詳細に書かれたテクニカルハンドブックをそのまま参照することができたので、問題なく理解できた。
仕様はわかったが、MSX自体の仕様はスロット等があったりしてイメージとちがって結構ややこしい。

▼結論

MZ-700とする。
すぐにわかりやすい資料を見つけることができたのと、想像以上にマシンの仕様がシンプルなので、今回の目的には最も合う。
サンプルプログラムの入手性はMSXに劣るが、大きな問題はなさそう。
ビットマップが使えないのは少し残念だが。

次点はMSX
なんといってもテクニカルハンドブックをそのまんま(?)wiki化してるので、文化の継承の観点からいってもこれ以上はないだろう。(権利関係は心配だが)
MSXならば50年後でもプログラミング入門者を作り出せそう。
プログラムも、紙媒体・Webともに、軽いものから大作まで入手可能で他の2機種を大きく引き離している。
ただし、自分がイメージしたよりも仕様が複雑であり、今回はパスする。

最後はPC-6001
残念ながらスクリーンモードすら把握することができなかった。
がんばって互換モニタのソース(アセンブラ)を読むのにトライするも、Z80初心者かつスクリーンモードの内容すらわかっていない自分には難しかった。
PC-6001はウィザード達の領域になってしまっていて、それがゆえに基礎的な資料が少ないようだ。
これからPC-6001プログラミングに入門するには結構ハードルが高い。

ビットマップを使いたかったので、本当はMSXPC-6001がよかったのだが、自分の能力から考えると結果的にMZ-700のシンプルさが良さそうだ。

3号プログラミング覚え書き(5) 画面編

・解像度:上画面は400x240ドット、下画面は320x240ドット

・コンソール:上画面50x30文字、下画面40x30文字

・画面モードの切り替えはXSCREENで行う
XSCREEN 4を指定した時は、上下がつながって320x480(上240、下240)ドットになる(上画面の左右40ドットずつは使えない)。

・上画面・下画面から描画したい先を選ぶ時はDISPLAYを使う
上画面 DISPLAY 0
下画面 DISPLAY 1
を指定してから、描画命令(グラフィック、スプライト、BG、コンソール)を実行する。
(XSCREEN 4の時はDISPLAY 0のみ)

GRPリソースのサイズは512x512ドット
GRPリソースは、単なる512x512ドットに対応したメモリリソースで、画面上ではその一部分が見えているだけだと思えばよい(見えない部分も存在していて、グラフィック描画命令で実際に描くことができる)。

GRPは0〜3がグラフィック、4がスプライト用、5がBG用
GRP4はスプライト用、GRP5はBG用の画像元データの置き場として使われる。
GRP0〜3はグラフィック用で、デフォルトの状態では上画面にGRP0、下画面にGRP1が割り当てられている。

・表示・操作したいGRPを変更するにはGPAGEを使う

GPAGE 表示ページ,操作ページ

のように指定する。

・表示ページは表示画面とGRPリソースの関連付けを表す
表示ページは、GRPリソース(単なるメモリリソース)と画面の関連付けを行う。
つまり、下画面にGRP3を表示したい時は以下のようにする。

DISPLAY 1
GPAGE 3,3

・操作ページはグラフィック描画命令の適用対象を表す
操作ページは、グラフィック描画命令(LINE、PAINTなど)が適用される対象のGRPリソースを指定する。

このようにプチコン3号では、表示ページと操作ページは全く異なる概念となっている。

加速度センサーを使ったサンプル

昨日調べた加速度センサーの動作にもとづいて、本体を傾けるとボールが移動するサンプルを作ってみた。

ファイル名: ACCELBALL

公開キー: BK7NV3AJ

公開日: 2015/09/22

バージョン: 1.00

動画
https://vine.co/v/ePnXD9gEF2X


なお、本体の傾きはGYROAでよさそうな気がするが、実際に使ってみると、しばらくすると本来の傾きとずれてくる。
(「3号プログラミング覚え書き(4) センサー編」に書いたとおり)

プチコン3号公式でも本体の傾きをとるにはACCELを使うとよい、と書かれているのでACCELを使う。
http://smileboom.com/special/ptcm3/reference/#input

3号プログラミング覚え書き(4) センサー編

ジャイロセンサーで本体の向きを取ろうとしても実際の向きとは合わないことがある

GYROA命令で本体の向きを取ることができるが、しばらくすると実際の向きと合わなくなる。
おそらくGYROVでとれる回転角速度の累積値を、GYROAの傾き角度にしていると思われるが、誤差の累積で実際の向きと合わなくなる。

本体の傾きをとるよりよい方法は、ACCEL命令を使うこと。
ACCEL OUT X,Y,Z
とする。
水平な場所に置いた状態では、X,Y,Zの値は、概ね0.00、0.00、-1.00である。
本体を右に90度傾けるとXの値はおよそ1.00、左に90度傾けるとXの値はおよそ-1.00となる。
同様に、本体を奥に90度傾けるとYの値はおよそ-1.20、手前に90度傾けるとYの値はおよそ0.80となる。(Yについては個体による違いがあるかもしれない)

角度については、45度くらいに傾けた時に0.50程度になるので、傾き角度に比例すると考えてよさそう。

Pebble Time 覚え書き5 加速度センサーをエミュレータで動かす

エミュレータで加速度センサーとかどうすんのかな?と思ってたら、ものすごく簡単にできました。
ものすごすぎて色々興奮w

1.エミュレータを起動
まずはエミュレータを起動します。
エミュレータの歯車ボタンをタップしてSETTINGSを開いたら、「SENSORS」をタップします。

2.サイトを開く
すると、「cpbl.io」というURLをスマホに入力して、下の数字を入力しろ、との指示が出ます。

cpbl.io」というURLをスマホに入力して、数字を入力します。


3.スマホを動かすとエミュレータに加速度情報が転送される
すると、スマホを動かすとエミュレータの加速度センサーが連動して動く!

あまりの簡単さにビビりましたw

Pebble Time 覚え書き4 実機で動かす

せっかくウォッチフェイスを自分で作ってみたので、実機で動かしてみる。

まずPebble Timeと連動しているスマホの設定。(画面はAndroid)
右上のメニューから「Settings」を選びます。
項目の中から「Developer Connection」をタップします。
次にConnectedを「ON」にします。

次にCloudPebbleです。
左メニューの「COMPILATION」で、通常は「EMULATOR」になっているところを「PHONE」に変更し、「INSTALL AND RUN」をクリックします。

これだけ! かんたん!
実機のウォッチフェイスが変わりました。

インストールしたウォッチフェイスは、普通に追加したウォッチフェイスと同じ扱いでそのまま動いているので、いらなくなったら通常の方法でウォッチフェイスを切り替えます。

Pebble Time 覚え書き3 ウォッチフェイスの改良(ちょっとだけ)

ひとつ前のエントリ(http://d.hatena.ne.jp/eidaht/20150920/1442732674)で、ウォッチフェイスを作ってみるチュートリアルを終えた。

こっから少しずつ勝手に改良してみる。

時分だけじゃなくて秒も表示してみる。
まずソースを眺めると、TickTimerServiceへの登録がMINUTE_UNITとなっているので、ここをSECOND_UNITにしたら1秒ごとに更新できそう。
以下のように直してみる。

  tick_timer_service_subscribe(SECOND_UNIT, tick_handler);

SECOND_UNITも定数として認識されたのでこれでよさそう。

次にupdate_time()の中の記述を直す。

  // Create a long-lived buffer
  static char buffer[] = "00:00:00";

  // Write the current hours and minutes into the buffer
  if(clock_is_24h_style() == true) {
    //Use 2h hour format
    strftime(buffer, sizeof("00:00:00"), "%H:%M:%S", tick_time);
  } else {
    //Use 12 hour format
    strftime(buffer, sizeof("00:00:00"), "%I:%M:%S", tick_time);
  }

%Sでいいのかわからないけどこれでいいんじゃね?

と実行してみると...、実行できたけどフォントがでかすぎて横がはみ出る。

フォントの設定はmain_window_load()の中に記述がある。

  text_layer_set_font(s_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));

この42がフォントのポイントっぽいので、30とかに変更したらよさそう。
でビルドすると、ビルドエラーになる。

../src/main.c:39:59: error: 'FONT_KEY_BITHAM_30_BOLD' undeclared (first use in this function)

宣言されてないって。
もとの定数はpebble.hで定義されてるんだろうけど、CloudPebbleからだとpebble.hの中身見えないんだけどw

チュートリアルの次の章はカスタムフォントを使うことになってるんだけど、カスタムフォント使うとかちょっとめんどい。

いくつか数値を試してみるも通らずw

観念して「pebble system fonts」でググるとありました。
USING PEBBLE SYSTEM FONTS

ていうことで、Bitham 30 Blackにしてみます。

  text_layer_set_font(s_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_30_BLACK));

ビルドできました。

実行すると、プロポーショナルフォントなので秒が変わるたびにガタガタしちゃうのがあんまりカッコよくない。
この辺にこだわるとカスタムフォントしかないのかな...。

カスタムフォントはまた次回。

Pebble Time 覚え書き2 最初のウォッチフェイス

C SDKでウォッチフェイスを作ってみる。
http://developer.getpebble.com/tutorials/watchface-tutorial/part1


1.CloudPebbleからログイン
https://cloudpebble.net/ide/
からCloudPebbleにログインする。

2.Projectの作成
PROJECTSから「CREATE」。
「CREATE NEW PROJECT」と出たら、
PROJECT NAME : プロジェクト名を入力する
PROJECT TYPE : Pebble C SDK
SDK VERSION : SDK 3
TEMPLATE : Empty project
を入力して「CREATE」。
要は、プロジェクト名だけ入力して「CREATE」。

3.Projectの設定
左にあるメニューの「SETTINGS」をクリック。
「APP KIND」という項目を探して、「Watchface」に変更する。

4.ソースの追加
左メニューの「SOURCE FILES」の横にある「ADD NEW」をクリック。
「CREATE NEW FILE」と出たら、
FILE TYPE : C file
FILENAME : ファイル名を入力する。ここでは「main.c」と入力
TARGET : App / Watchface
CREATE HEADER : チェックしない
を入力して「CREATE」。
要は、ファイル名だけ入力して「CREATE」。

5.ソースを入力 (空のウィンドウを表示)
main.cには以下の1行だけが最初から入力されている。

#include 

続いて以下をソースに入力する。

static Window *s_main_window;

static void main_window_load(Window *window) {

}

static void main_window_unload(Window *window) {

}

static void init() {
  // Create main Window element and assign to pointer
  s_main_window = window_create();

  // Set handlers to manage the elements inside the Window
  window_set_window_handlers(s_main_window, (WindowHandlers) {
    .load = main_window_load,
    .unload = main_window_unload
  });

  // Show the Window on the watch, with animated=true
  window_stack_push(s_main_window, true);
}

static void deinit() {
   // Destroy Window
  window_destroy(s_main_window);
}

int main(void) {
  init();
  app_event_loop();
  deinit();
}

Pebble SDKにおける標準的な記述方法はこのようにする。
init()でメモリの確保、deinit()でメモリの開放を行う処理を記述する。
また、main()の中にイベントループとしてapp_event_loop()の呼び出しを記述する。

画面を表示するにはWindowを作成しなければならないので、init()の中の2行目の処理でWindowを作成している。
その後で、Windowのloadとunload時のイベントで呼ばれる関数を関数ポインタとしてWindowHandlersに渡している。
window_stack_pushでWindowを表示している。

これで、何も表示しない、というか一面白色になるウィンドウを表示するウォッチフェイスが作成できた。

6.実行 (空のウィンドウを表示)
画面右上のプレイボタンみたいなボタン(ポイントすると「Save, build, install and run」と表示されるボタン。以後、実行ボタンと呼ぶ)をクリックすると、ビルドが始まり、自動的にエミュレータが起動してウォッチフェイスがインストールされ、ウォッチフェイスが起動するところまで行われる。
画面左上にエミュレータが表示され、白い画面が表示されたら成功。


7.ソースの修正 (何かテキストを表示させる)
先ほどのmain.cを再び開く。
文字を表示するにはTextLayerを使って描画する。
先ほど空だった、main_windows_loadの中に、TextLayerを作成して、テキストを描画する処理を追加する。
また、main_windows_unloadの中にTextLayerの開放処理を追加する。

static void main_window_load(Window *window) {
  // Create time TextLayer
  s_time_layer = text_layer_create(GRect(0, 55, 144, 50));
  text_layer_set_background_color(s_time_layer, GColorClear);
  text_layer_set_text_color(s_time_layer, GColorBlack);
  text_layer_set_text(s_time_layer, "00:00");

  // Improve the layout to be more like a watchface
  text_layer_set_font(s_time_layer, fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD));
  text_layer_set_text_alignment(s_time_layer, GTextAlignmentCenter);

  // Add it as a child layer to the Window's root layer
  layer_add_child(window_get_root_layer(window), text_layer_get_layer(s_time_layer));
}

static void main_window_unload(Window *window) {
    // Destroy TextLayer
    text_layer_destroy(s_time_layer);
}

再び実行ボタンで実行。
画面左上のエミュレータに、「00:00」と表示されたら成功。


8.ソースの修正 (時計を表示させる)
再度main.cを開く。
時計を表示するには、イベントループの中で、一定時間ごとに処理が呼び出されるようにする必要がある。
このためには、TickTimerServiceに対して一定時間ごとに呼び出される処理を登録する。
TickTimerServiceから直接呼び出される処理は以下のようになる。

static void tick_handler(struct tm *tick_time, TimeUnits units_changed) {
  update_time();
}

また、init()の最後に、TickTimerServiceに1分ごとにtick_handlerを呼び出すように登録する処理を付け加える。

  // Register with TickTimerService
  tick_timer_service_subscribe(MINUTE_UNIT, tick_handler);

そして、先ほどのtick_handlerから呼び出される、update_time(時計を描画する処理)
を追加する。

static void update_time() {
  // Get a tm structure
  time_t temp = time(NULL); 
  struct tm *tick_time = localtime(&temp);

  // Create a long-lived buffer
  static char buffer[] = "00:00";

  // Write the current hours and minutes into the buffer
  if(clock_is_24h_style() == true) {
    //Use 2h hour format
    strftime(buffer, sizeof("00:00"), "%H:%M", tick_time);
  } else {
    //Use 12 hour format
    strftime(buffer, sizeof("00:00"), "%I:%M", tick_time);
  }

  // Display this time on the TextLayer
  text_layer_set_text(s_time_layer, buffer);
}

また念のため、tick_handlerが最初に呼び出されるのがすぐとは限らないので、00:00を眺め続けることにならないように、main_window_unloadの最後にupdate_timeの呼び出しを追加しておく。

  // Make sure the time is displayed from the start
  update_time();

再び実行ボタンで実行。
画面左上のエミュレータに、現在の時刻が表示され、一分後に時計の表示が変われば成功。

最初の時計はこれで完成!

Pebble Time 覚え書き1

・ソースのサンプルや、コンパイラオプションに出てくる、「APLITE」「BASALT」ってなんのこと?

何のことわりもなしに、「APLITE」「BASALT」って出てくる。
これは何?と思ったら、
http://dvorak55.hatenadiary.jp/entry/2015/07/20/160801
こちらに書かれているとおり、
APLITE = Pebble (旧バージョン)
BASALT = Pebble Time (今の最新バージョン)
ってことのようです。

旧バージョンとアプリも互換性があるのね。


・APP KINDの「Watchapp」「Watchface」のちがいは何?

プロジェクトの「SETTINGS」の「APP KIND」だけど、ウォッチフェイスを作る時は「Watchface」にしておく。

The main difference between the two kinds are that watchfaces are selected using the Up and Down buttons and serve as the default display on the watch. This also means that these buttons are not available for custom behavior (Back and Select are also not available to watchfaces). In contrast, watchapps are launched from the Pebble system menu. These have more capabilities such as button clicks and menu elements, but we will come to those later.

これら2種類の大きな違いは、ウォッチフェイスは上下ボタンを使って選択されることと、時計のデフォルトとして表示される、ということだ。
このことは、これらのボタンはカスタマイズした振る舞いに使うことはできないということを意味している(バックボタンとセレクトボタンもウォッチフェイスでは使うことができない)。
それとは対照的に、ウォッチアップスはPebbleのシステムメニューから起動される。これによって、ボタンクリックとメニュー要素はより自由度を得るが、これについては後で見ることにしよう。

ってことで、ウォッチフェイスにしておくと、時計としてデフォルトで表示されるけど、ボタンに割り当てられる機能には制限がある、それに対してアプリにしておくと、メニューから起動することになる代わりにボタンのカスタマイズ等についてはより自由度がある、ってことのようです。

プチZ80Continueを製作中

mkIIで途中まで作っていたプチZ80
仮想マシンとコードが一体化していたせいで、直していたらうまく動かなくなってしまって途中で放置状態になってました。。。

今はプチコン3号がメインになったので、心機一転、3号でプチZ80を作りなおすことに。
今までのコードはいったん捨てて、ゼロから書き直しています。
ただし設計はmkIIの時と概ね同じ。
ということでmkII版を継ぐものとして、タイトルは「PETIT-Z80 Continue」に。

今回は、使えるメモリが大幅に増えたことに加えて、配列そのものを参照として簡単に引き渡しできるので、メモリやCPUの内部状態(レジスタなど)をすべて配列に格納して、プチZ80の外側にある仮想マシンから渡してもらうようにしました。
ある意味、プチZ80自体は、外側にある仮想マシンから見るとZ80の命令セットを実行するライブラリのように見えます。
これによってメモリなどの環境を簡単に切り替えて使うことができます。
バンク切り替えも、配列を入れ替えるだけで実行できるので余裕です。

例によって完成するかどうか自体があやふやなので、期待しないで待っててください。(笑)
とりあえず、「Hello, World!」を表示するところまでは簡単にできました。