PC-8001の周辺機器と言えば、やはりHAL研究所が発売したPCG8100が筆頭にあげられるかと思います。PSAやGB20といった互換ボードも発表/発売され、最近ではPSA2やOBFという最新のPCGボードが開発され、さらにはHAL研究所自身が発売したパソコンミニPC-8001にも機能としてPCGが搭載されていたりします。
※ パソコンミニは既に発売終了しています。

ところがこのPCGの定義機能はマシン語で定義処理を作成するのが前提となっていました。そこで今回はPCG定義をN-BASICで行ってみました。


  • PCG定義の方法
PCGの定義は3つのポートを使います。
  1.  OUT0,P パターンを定義する
  2.  OUT1,L 下位8bitのキャラジェネアドレスを指定する
  3.  OUT2,H + 16 上位8bitのキャラジェネドレスを書き込みフラグ ON で指定する
  4.  OUT2,H 上位8bitのキャラジェネドレスを書き込みフラグ OFF で指定する
※ 2022/09/16 18:00 更新
コメント欄で指摘されて確認しましたが、書き込みフラグをONにした後で、再度OFFで書き込みを行わないと、定義が動作しないようです。拙作Newシティヒーローでもそのような動作になっていました。本記事内の全て当該コードを修正しました。大変失礼いたしました。

処理の順番は上記ポート番号通りです。Pパターンを定義して、L下位アドレス、H上位アドレスと設定します。上位アドレス設定時に、書き込みフラグを立てるのが重要です。書き込みフラグは 00010000 = 16(&H10)です。

キャラジェネアドレスと書いてしまいましたが、正しくはキャラクター・ジェネレーション・アドレスです(たぶん)。文字の形を作るために定義されているデータの格納場所という意味です。このアドレスは PCG8100 内部の &H0000 仮想アドレスから始まっています。

具体的に説明していきます。例えば、任意のキャラクタコードに以下のパターンを定義したいとします。

...o.... ooooooo. ..o..... .ooooo.. ......o. ......o. oooooo.. ........
1キャラクタは縦横8ドットです。横8ドットは1バイトのビットパターンで表現できるので、1キャラクタの定義は8行ですから、文字の再定義には全部で8バイトが必要となります。PCG8100 では、キャラクタコード &H80 からの 128個のキャラを定義できます。スタートが &H80 なので、キャラジェネアドレスは、キャラクタコードから &H80 を引いた値を8倍すると得られます。以下の例では CH にキャラクタコードが格納されています。

DT%=(CH-&H80)*8
続いて最初の定義パターン情報を設定します。最初のパターンは ...o.... です。これを 2進数で表すと 00010000 なので、数値としては &H10 となります。この値をポート 0 に送出します。

V%=&H10:OUT0,V%

続いて、定義パターンをどのアドレスに設定するか指定します。DT% はアドレスなので、それを下位と上位に分離します。整数計算じゃないと小数が出来てしまうので、変数は整数型の % を指定してます。下位は MOD で取得します。上位は 256 で割れば良いのですが、その時同時に書き込みフラグ 16(&H10)を加算してします。

OUT1,DT%MOD256 OUT2,(DT%/256)+16
OUT2,(DT%/256)
本来は論理和で 16(&H10)を加算すべきですが、DT%は &H1000 より小さい事が保証されていますので、今回は単に加算で済ませています。この定義を DT% を +1 しながら8回繰り返すと、1文字、PCG が定義できるようになります。


  • パターンが見える状態で定義する
16進数でデータを用意すると大変見づらいので、パターンとして目に見える形で実装テストしてみます。まず、データは . と o で構成された文字列データで用意します。

5000 DATA "...o...." 5010 DATA "ooooooo." 5020 DATA "..o....." 5030 DATA ".ooooo.." 5040 DATA "......o." 5050 DATA "......o." 5060 DATA "oooooo.." 5070 DATA "........"
なんとなく「ち」というひらがなが見えますよね。この文字列データを 16進数の値に変換しながら、上記で説明した処理を行うサブルーチンがこちら。

499 ' Define PCG1 500 DT%=(CH-&H80)*8:FORJ=1TO8:READP$ 510 V%=0:FORI=1TO8:IFMID$(P$,I,1)="o"THENV=V%+1 520 V%=V%*2:NEXT:V%=V%/2 530 OUT0,V%:OUT1,DT%MOD256:OUT2,(DT%/256)+16:OUT2,(DT%/256) 540 DT%=DT%+1:NEXT:RETURN
サブルーチンを呼び出す前提は、データの位置に RESTORE しておく事と、定義したいキャラクタ番号を CH に入れておく事です。例えば、CH = &HA0 に行番号 5000 からのデータを定義したい場合はこんな感じで呼び出します。

100 CH=&HA0:RESTORE5000:GOSUB500:REM PRINTCHR$(CH)
最後の REM は正しい定義されたかを見るためのチェック用です。REM を外せば定義が表示されます。やっぱりちょっと遅いです…。
※ これは面白いデザイン!食卓が楽しくなりそうですw

  • パターンを16進数の値で定義する
N-BASIC でもなんとか我慢できるレベルにするならば、やっぱり定義データは最初から16進数にしておくのが良いでしょう。パターンから値に変換する処理が必要なくなるので、定義部分はこんなにシンプルになります。

699 ' Define PCG2 600 DT%=(CH-&H80)*8:FORJ=1TO8:READV% 610 OUT(0),V%:OUT(1),DT%MOD256:OUT(2),(DT%/256)+16:OUT2,(DT%/256) 620 DT%=DT%+1:NEXT:RETURN

呼び出し方はパターンが見える状態と全く同じです。書くまでもないかと思いましたが、念のため、定義の呼び出しを提示します。

100 CH=&HA1:RESTORE6000:GOSUB600:REM PRINTCHR$(CH)


  • 少しは定義を高速化する
よく知られていますが、PC-8001 を遅くしている原因が画面表示の DMA です。文字の定義では画面表示が不要という事であれば、画面を消してしまう事で、かなり高速化します。DMAを止めるのは OUT81,0 です。もう一度画面を表示させるには WIDTH 命令を実行します。例えば、以下のようにすると実行速度が向上します。

100 DEFINTA-Z 105 OUT81,0 110 CH=&HA0:RESTORE5000:GOSUB500:PRINTCHR$(CH) 120 CH=&HA1:RESTORE6000:GOSUB600:PRINTCHR$(CH) 125 WDITH80,25 130 END

注意点としては、PCG定義中にプログラムが中断してしまうと、画面が何も見えないままになってしまうと言う事です。このときは、[Ctrl] + [L] を押して画面を(見えないですが)消去した上で、WIDTH80 と入力して実行すると回復します。


  • PCG8200 補足
PC-8001mk2 専用の周辺機器である PCG8200 は 256個全てのキャラクタ文字を定義できるように機能拡張されています。そのため、キャラジェネアドレスが2系統存在しています。PCG8100 と同様に &H80 ~ &HFF まキャラクタ文字を定義する場合は事前に OUT3,8 と実行してください。これは PCG8100 では無視されますので、呪文のように最初に入れても構いません。

PCG8200 で &H00 ~ &H7F を定義する場合は、OUT3,19 とします。また、キャラクタコードから -&H80 している部分は不要となります。PCG8100 との互換性を保つためにこのようなハード仕様になったのだと思われます。
※ OUT3 でキャラジェネを定義する RAM を切り替えています。

上記のプログラムサンプルを cmt としてダウンロードできるようにしておきます。下記からダウンロードしてください。

pcgdef.zip

※ 2022/09/16 第2版更新

.cmt の中のファイル名は "PCGDEF" です。cload"PCGDEF" としてもらえれば、PC-8001 に読み込めると思います。
BASICでPCG定義する
お役に立ったとか面白かったとか、応援する気持ちが頂けるのなら、買わなくても良いのでポチっとAmazonのリンクをクリックしてもらえると嬉しいです。よろしくお願いします。
※ 紛う事なくドラム缶デザインです。どことなく某有名珈琲店のマグカップにもにてますねw