40年前のマイコン PC-8001 でゲームを作る
1979年に発表されたNECのPC-8001。当時はまだパソコンという言葉がなく、マイコンと呼んでいました。
最近では、マイコンと言えば、RaspberryPi や Arduino、ESP32 などの SBC (Single Board Computer)を意味しますね。
懐古主義? と思われがちですが。。
私の場合は、個人的な思い入れが非常に強いということもありますが、IoTシステムを構築する上で、コンピュータの根本を知るといことが重要になってきます。組み込み開発の分野では当たり前のことですが、一般的な業務システム開発、Webシステム開発の技術者が工場IoTとリンクするときにコンピュータとは何かというところを理解していないと問題が生じてしまいます。
ということで今更ながらPC-8001用のゲームを作ってウォーミングアップ
まだ発表するには時期尚早ですが、途中のものの一部をアップします。
タイトル画面。PC-8001 では、2×4ドット毎に1色しか指定することができません。160×100ドットという低解像度ですが、うまく作成すれば、なんとなく味のあるデザインになます!
当時、よくあったストーリーを表示する画面。Ultima II をイメージして作成してみました。
キャラクターの初期パラメータを乱数で設定。キーを押してストップさせてそれぞれの値を決めていきます。昔の光栄の初期の「信長の野望」「三国志」がこんな感じのキャラ設定でした。多分。当時プログラムがBASICで書かれていたので、ここを最大値になるように改造してチートしてました。。
フルグラフィックにしようかと思いましたが、ちょっとマシン語で書く時間もないので、BASICでもある程度スピードがでるようにキャラ文字オンリーでマップ表示。
N-BASIC 小ネタ
N-BASIC でゲームプログラムをする際のちょっとしたテクニックについて、おさらいします。(いまさらだれが必要としているのか不明ですが。。)
ファンクションキーを設定する
よく利用するコマンドをファンクションキーに割り当てることで、プログラム作成時の時間の節約になります。
KEY1,"WIDTH 80,25"+CHR$(13)
KEY2,CHR$(12)
KEY3,"CONSOLE 0,25,1,1"+CHR$(13)
リアルタイムなキー入力は INP(n) を利用する
キー入力には INKEY$ という命令もありますが、これはキークリックされたかどうか評価するものです。上記画面のパラメータを設定するような場合や、質問にY/N でこたえるような UI の場合は良いですが、ゲームのキャラクタを動かすときなどは、キーを押しっぱなしたときは連続してキャラクタが動いてほしい場面が多いかと思います。
その際は、INP(n) で対象のキーの状態 (押下しているかどうか)を取得することができます。
ポート | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
0 | (0) | (1) | (2) | (3) | (4) | (5) | (6) | (7) |
1 | (8) | (9) | (*) | (+) | (=) | (,) | (.) | RET |
2 | @ | A | B | C | D | E | F | G |
3 | H | I | J | K | L | M | N | O |
4 | P | Q | R | S | T | U | V | W |
5 | X | Y | Z | [ | \ | ] | ^ | – |
6 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
7 | 8 | 9 | * | + | < | > | ? | _ |
8 | HOME | ↑ | → | INS | GRPH | カナ | SHIFT | CTRL |
9 | STOP | F1 | F2 | F3 | F4 | F5 | SPACE | ESC |
Val | 254 | 253 | 251 | 247 | 239 | 223 | 191 | 127 |
()はテンキー側のキーボードを示しています。
何も押されていないときは255となります。
一般的な利用方法 (Zキーが押されていることを確認するとき)
IF INP(5)=251 THEN ....
ゲームの時はコード量を減らす技として、論理演算を利用することが多いです。
例えば [4]で左、[6]で右にキャラクタを移動させるときに、移動量を論理式を使ってシンプルに記述することができます。
I=INP(0):X=X+(I=191)-(I=239)
それと、実は同じボードで同時に押されているキーも検出できます。上記の例では、 251や191などと書いていますがポートの1byte の8つのビットが0か1になっているだけです。
何も押されていなければ、11111111B で255となっているだけです。例えば ポート0 で、テンキーの1が押されていたら11111110B、2が押されていたら 11111101B、同時に押されていたら、 11111100B となるだけのことです。これを10進数表記して条件に入れているだけです。
INSTR で何種類かの入力を受け付けるようにする
例えば、ゲームなどで Y,y,ン,SPACE,RETURN で入力を待つようなときに、キーのモード (大文字、小文字、カナ)を気にしないで対応させる小ネタです。
すなおに記述すると
K$=INPUT$(1):PRINT K$;
IF K$="Y" OR K$="y" OR K$="ン" OR K$=" " OR K$=CHR$(13) THEN.....
となりますが、INSTR$ をうまく使うとすっきり書けます。
K$=INPUT$(1):PRINT K$;
IF INSTR("Yyン "+CHR$(13),K$) <> 0 THEN ....
タイトル画像の作成について
160×100 の低解像度画像とは言っても、手作業だとかなりだるいので、これは現代の力を借りました。
最初に 160 x 100 の画像を PhotoShop で作成。これをコントラストを上げて、さらに減色。これである程度の画像ができあがります。
2×4 ドット毎に1色しか色付けができないこと、1行では20回までしか色を変化されることができないという点を考慮してデザインすることも重要です。PNGで保存。
このあとは、Delphi で作成したツールで画像を読み込んで、16進数に変換して、CSVで書き出しました。
さすが文明の利器! らくちんでした。
まだまだ制作途中です
まだコーディング半ばなので、発表するまでに至りませんが、夜や週末にぼちぼち進めていきます。
MZ-80のエミュレーターも同時に作っているので、中々進まないです。。
何がしたいのか?
何がしたくて、過去のテクノロジーのおさらいをしているのかと申しますと、RaspberyPi などで、LinuxなどのOSなしで直接動かす、いわゆる Bare Metal で超高速なIoT機器を作りたいのです。 Arm系のマシン語をやったことがないので、Z80, 6809, 6502, 8086 でウォーミングアップして助走をつけているのです。。(笑)