PC-6001mkII/6601 CP/M導入メモ・その2

導入メモその1に続き、今度はCP/M BIOSを自分で書いてみました。既にI/O掲載版があるわけで車輪の再発明もいいところですが、機能が全く同じでもないですし、複数の実装があってもいいんではないかと。また、引き続きいくつかのCP/Mアプリケーションを6601上で動かしてみたので、その紹介もしたいと思います。

(※2013.1.27追記:SR専用版も作ってみました)


目次


特徴

Rogueの画像
スクリーン4版でRogue。ドットが細かいせいか録画したら色が変になってしまったのでエミュレータ画面で。
ビデオ出力+ブラウン管テレビだと流石に文字の判別は無理でした。


ディスクイメージの作成

  1. 配布されているソースから、CP/Mのシステムイメージを作成します。手順は導入メモその1にある「CP/Mのアセンブル」の項目を見てください。ただし、CPM22.Z80の14行目の変更は、以下のようにします。
  2. 拙作のBIOS(2012.11.24版)をダウンロードして展開します。
  3. 展開ディレクトリにcpm.binを置き、以下のディスクイメージ作成プログラムを実行します。(Windowsの場合)。Windows以外の場合は、srcディレクトリにあるソースをコンパイルして使ってください。
  4. cpm.d88という174768バイトのファイルができます。
  5. NDitt等でディスクに書き込めば実機でCP/Mが起動します。

アプリケーション導入例 〜リアルタイムゲーム編〜

導入メモその1ではZorkにRogueと思考タイプのゲームを動かしたので、今回はリアルタイムゲームを試してみます。以下三つのゲームはすべて80桁用ですが、残念ながらROM/RAMカートリッジ無しのスクリーン4版では一つ目のゲームしか動きませんでした。スクリーン1版では、width 80としてから、ファイル名 (qtitris等) を入力すると起動します。F1キーでオートフォーカスを切った方が遊びやすいでしょう。それと、三つともゲーム前にレベル (スピード) を選べるので、ある程度スピードを上げた方が面白いです。

ファイルのコピー方法

さて、ゲームの入ったディスクイメージを用意するために、前回はエミュレータのモニタモードでアプリケーションをメモリに読み込み、CP/M上のSAVEコマンドでディスクイメージに書き込むという手順を踏みました。今回は、CP/M用D88ファイルに直接アクセスしてファイルを追加するプログラムを書いてみたので、その使い方を説明します。配布BIOSファイルを展開した中にある、cpmadd.exe (Windows用バイナリ) というファイルを使います。Windows以外の場合は、src/cpmadd.cをコンパイルして使ってください。コマンドプロンプト上で、

cpmadd input.d88 input1.bin [input2.bin ...] output.d88

のようにすると、元のCP/M用D88イメージに対して、新たにファイルが追加された状態のD88ファイルができます。ディスクイメージの作成の項で用意したcpm.d88を元ファイルとして使うと良いでしょう。

既に存在するファイルを出力D88ファイルとして指定することもできますが、問答無用で上書きするので注意してください。(出力ファイルの指定を忘れると、最後の入力ファイルが破壊されます!) 入力と出力のD88ファイル名を同じにすることもできます。ひたすら追記するだけのプログラムなので、ディスクが一杯になったら別のイメージを用意するか、CP/M上でERAコマンドを使って要らないファイルを消去するなどしてください。ちなみに、空いているブロックを先頭から順に使用するので、消したり追加したりを繰り返すと断片化が進むかもしれません。CP/M用のデフラグツールってあるんでしょうか?また、テキストファイルを追加する場合は、改行コードがCR+LF(0Dh+0Ah)になっていること、最後がCtrl-Z(1Ah)で終わっていることを確認してください。

前回はZork Iのファイルをコピーするのに一旦分割してPIPコマンドで結合しましたが、これを使うと

cpmadd cpm.d88 zork1.* zork1.d88

のようにしてZork Iの入ったディスクイメージを作成することができます。


Quatris

Quatrisの画像
PC-6601でQuatris

テトリス的ゲームです。Retrocomputing Archiveの/cpm/cdrom/ENTERPRS/CPM/GAME/よりQTITRIS.COMをダウンロードし、コマンドプロンプトで

cpmadd cpm.d88 qtitris.com quatris.d88

とすると、QTITRIS.COMが書き込まれたquatris.d88というファイルが生成されます。ゲーム名とコマンド名が違って紛らわしいので、名前を変えておいた方がいいかもしれませんね。導入メモその1でやったように、

P6VW>loadmem qtitris.com 0x100
a>save 57 qtitris.com

のようにしてもOKです。操作は13で左右移動、2で回転。スクリーン1版だと次のブロック表示が画面内に収まらないので、スクリーン4版でやった方がいいでしょう。


CatChum

CatChumの画像
PC-6601でCatChum。スクリーン4版で動かすにはROM/RAMカートリッジが必要です。

ばぐごん的ゲームです。Retrocomputing ArchiveのMiscellaneous Softwareより、CATCHUM.ZIPをダウンロードして展開し、中のファイルCATCHUM.COMとCATCHUM.DATの両方をディスクイメージに書き込む必要があります。

cpmadd cpm.d88 catchum.* catchum.d88

でOKです。自キャラがちゃんとパクパクしているように見えるのがいいですね。残念ながら、ROM/RAMカートリッジ無しのスクリーン4版では動きませんでした。


Ladder

Ladderの画像
PC-6601でLadder。スクリーン4版で動かすにはROM/RAMカートリッジが必要です。

スパイパニックや沙悟浄的ゲームです。Javaで移植されていたり、結構人気があるみたいですね。Retrocomputing ArchiveのMiscellaneous Softwareより、LADDER.ZIPをダウンロードして展開します。あとはCatChumと同様に、

cpmadd cpm.d88 ladder.* ladder.d88

として必要なファイルをコピーします。面のバリエーションがあってなかなか面白いです。残念ながら、ROM/RAMカートリッジ無しのスクリーン4版では動きませんでした。


アプリケーション導入例 〜プログラミング言語編〜

ZASM

ZASMの画像
PC-6601でZASM

ここまでゲームばかり動かしてきましたが、CP/Mと言えば充実した開発環境なので、次は各種言語が動くかどうか試してみました。まずはアセンブラから。でも使い方を覚えるの面倒だなあ……というわけで、個人的にDOS版を使い慣れているZASMのCP/M版を。作者のサイトから、zasm164c.lzhをダウンロードして展開し、zasm.comをディスクにコピーします。当然かもしれませんが、DOS版と同じソースリストが同じようにアセンブルできるようです。BIOSのソースの最後にCtrl-Zだけ加えてコピーし、ZASMにかけてみたら、ちゃんと通りました。素晴らしい。


BDS-C

BDS-Cの画像
PC-6601でBDS-C。ファイル表示するプログラムをコンパイル後実行して、自分のソースを表示してみたところ

次はC言語に挑戦してみます。BDS-Cというのを試してみました。The Unofficial CP/M Web SiteのCP/M Programming Languages and Developer's Toolsより、bdsc-all.zipをダウンロードして展開し、以下のファイルをディスクにコピーします。BDSCIO.Hはファイル入出力なんかの時に要るようです。stdio.hみたいなものですかね。

まずCCコマンドでコンパイル。中間ファイルができるので、CLINKコマンドでリンクすると.COMファイルができます。今のCと若干文法が違ったり、サブセットということで機能に多少の制限があったりもするようです。

BDS-Cの画像
CCしてCLINKで実行ファイルができる


Turbo Pascal

Turbo Pascalの画像
PC-6601でTurbo Pascal。エディタ画面

次はTurbo Pascalです。Retrocomputing ArchiveのProgramming Languages/Environmentsより、TP_301A.ZIPをダウンロードして展開し、以下のファイルをディスクにコピーします。

TINSTは最初に1回だけ、設定のため起動する必要があります。以下のように進めてください。http://ldlabo.hishaku.com/NO32/main.htmに参考画像があります。

(※ROM/RAMカートリッジ無しのスクリーン4版ではTINSTは動きませんでしたが、実行しなくてもさほど問題は無いような気もします。)

  1. a>width 80
  2. a>tinst
  3. [S]creen installation を選択
  4. Soroc 120を選択 (バージョン3.01なら21[Return])
  5. N を選択
  6. [Return]
  7. [Q]uit を選択

一度済めばTINST.*は消しても構いません。次いで、a>turboとしてプログラム本体を立ち上げると、まずエラーメッセージを出すかどうか聞かれます。YかNで答えると、以下のようなコマンドが使えるモードになります。

表1:Turbo Pascalのコマンド
コマンド内容
O オプション変更 (実行ファイルを作りたい場合はここでCを選択、Qで戻る)
E エディタモードに
S ソースをセーブ
C コンパイル
R 実行
Q 終了
表2:Turbo Pascalエディタの主なキーバインド
コマンド内容
Ctrl-E カーソルを上に
Ctrl-S カーソルを左に
Ctrl-D カーソルを右に
Ctrl-X カーソルを下に
Ctrl-G カーソル上の文字を消去
Ctrl-N 1行挿入
Ctrl-Y 1行消去
Ctrl-K Ctrol-D 終了

エディタのキーバインドは、CP/Mでは一般的なもののようです。上の表ぐらいを覚えれば何とか使えるんじゃないでしょうか。当時としては高性能なエディタでかつ安価だったため、エディタ目当てにTurbo Pascalを買う人もいたんだとか。

Turbo Pascalの画像
コンパイル中

Turbo Pascalの画像
.COMファイルの実行も成功


実装の詳細など

I/O掲載版との相違点


ディスクアクセス


メモリマップ

メモリマップは以下の通りです。ROM/RAMカートリッジ無しスクリーン1版のCCP/BDOS/BIOSの位置関係はI/O掲載版と同じ、スクリーン4版はそれより6KB少ないユーザエリアとなっています。VRAMがネックになっていて、あまり選択の余地がありません。

ROM/RAMカートリッジ使用版では、外部RAM領域をTPAに充てることによって使える領域を増やしました。CP/Mアプリケーションからは表RAMとして、0000h〜7FFFhとC000h〜FFFFhの領域が内蔵RAM、8000h〜BFFFhの領域が外部RAMで見えており、BIOS内で必要な時のみ8000h〜BFFFhを裏RAM (内蔵RAM側) に切り替える仕組みです。スクリーン1版では、バッファエリアやBIOSプログラムの大部分を裏RAMに移動させることで、更にユーザエリアを確保しています。

「○○K CP/M」という表現については、どうやら「BIOS以降のサイズが1.5KBの時に64K CP/Mと称する」という定義になっているようです。じゃあBIOSが512バイトなら65Kになるのかよ!その定義おかしいだろ!よしやってみよう!ということで、ROM/RAMカートリッジ使用スクリーン1版では限界に挑戦してみました。ただ実際は、ジャンプテーブルやBDOS用ワークエリアなど、CP/Mの仕様上必ずBIOS内に確保しなければいけない領域があるため、512バイト全部を使ってBIOSのプログラムを書くことはできません。領域の大きさはFDDの仕様によりますが、1Dでブロックサイズ1024バイト、4ドライブ構成という条件だと、約400バイトが最低限必要です。残りは100バイト程度で、ここにプログラムを収めるのは結構キツかったです。可能な限りの処理を裏RAM側に押し込みましたが、本当にカツカツで、現在表RAM側に余っているメモリはたったの1バイト。何かバグが発覚したらたぶんもう直せません。どうしよう。

表3:スクリーン1版メモリマップ (49K CP/M)
アドレス内容
0000h〜 CP/Mワークエリア
0100h〜 TPA (ユーザエリア)
A800h〜 CCP (CP/M シェル部分)
B006h〜 BDOS (CP/M OS部分)
BE00h〜 BIOSジャンプテーブル、ワークエリア
C000h〜 VRAM
C800h〜 BIOSプログラム
CE00h〜 ディスクリード用バッファ
D200h〜 ディスクライト用バッファ
D600h〜 仮想画面
DE00h〜 空き、BIOS一時スタック
F900h〜 ブート用
FA00h〜 割り込みテーブル

表4:スクリーン4版メモリマップ (43K CP/M)
アドレス内容
0000h〜 CP/Mワークエリア
0100h〜 TPA (ユーザエリア)
9000h〜 CCP (CP/M シェル部分)
9806h〜 BDOS (CP/M OS部分)
A600h〜 BIOS
AF00h〜 割り込みテーブル、BIOS一時スタック
B000h〜 ディスクリード用バッファ
B400h〜 ディスクライト用バッファ
B800h〜 仮想画面
C000h〜 VRAM

表5:ROM/RAMカートリッジ使用スクリーン1版メモリマップ (65K CP/M)
アドレス内蔵RAM 外部RAM
0000h〜 CP/Mワークエリア 無し
0100h〜 TPA (ユーザエリア)
8000h〜 VRAM TPA
8800h〜 ディスクリード用バッファ
8C00h〜 ディスクライト用バッファ
9000h〜 仮想画面
9800h〜 BIOS (裏)
9F00h〜 DMA用バッファ
9F80h〜 空き、BIOS一時スタック
C000h〜 TPA 無し
E800h〜 CCP (CP/M シェル部分)
F006h〜 BDOS (CP/M OS部分)
FE00h〜 BIOS (表)

表6:ROM/RAMカートリッジ使用スクリーン4版メモリマップ (59K CP/M)
アドレス内蔵RAM 外部RAM
0000h〜 CP/Mワークエリア 無し
0100h〜 TPA (ユーザエリア)
8000h〜 VRAM TPA
C000h〜 TPA 無し
D000h〜 CCP (CP/M シェル部分)
D806h〜 BDOS (CP/M OS部分)
E600h〜 BIOS
EE00h〜 ディスクリード用バッファ
F200h〜 ディスクライト用バッファ
F600h〜 仮想画面
FE00h〜 割り込みテーブル、BIOS一時スタック等


制御コード

制御コード、エスケープシーケンスはI/O掲載版と同じものに対応したつもりですが、動作の詳細についてはもしかしたら違っているかもしれません。対応コードは以下の通りです。

表7:制御コード
コード 機能
08h,1dh カーソル左へ
09h,0ch,1ch カーソル右へ
0bh カーソル上へ
0ah 改行+復帰
0dh 復帰
1ah 画面全体クリア、カーソル原点へ
1eh カーソル原点へ
1bh エスケープシーケンス
ESC *画面全体クリア、カーソル原点へ
ESC Tカーソル位置から行末までクリア
ESC Yカーソル位置から画面終わりまでクリア
ESC =yxカーソルを(x-32,y-32)に移動

スクリーン4のフォントについて

スクリーン4版では、80×25文字表示を行うため、自前のフォントを使っています。スクリーン4は画面全体で320×200ドットなので、1文字あたり4×8ドット、縦横1ドットずつ隙間を空けているため、実質3×7ドットのフォントです。多少無理のある文字もありますが、P6ではかつて、スパイパニック、ピラミッド、ハイドライドなど、横3ドットのフォントが良く使われていたので、これもまたP6らしさだとも言えるんではないでしょうか。

CP/Mでは基本的には7ビット文字だけ表示できればいいのですが、実はフォントを作った時に調子に乗って他の文字にも手をつけて、そして見事壁にぶち当たってしまったので、おまけとして苦闘の様子を紹介します。最後はかなりヤケクソ気味にドットを打ったのですが、それでも仮名や漢字は全部埋めることすらできませんでした。横3ドットで平仮名はやはりハードルが高い……。ヤケクソで作ってみたら案外読めるものになった、という字も中にはあるんですけどね。小さい「ゅ」なんかお気に入りです。ちなみに、ご覧の通り表計算ソフトでCtrl-C/Ctrl-Vを叩きまくって作成しました。黒背景のマスに数字の1を入れておいてデータ変換しています。

スクリーン4用フォントの画像
挫折の跡


代替ROMファイル

2012.9.22版より、簡易的な代替ROMファイルを同梱してみました。本来のROMとある程度の互換性があり、エミュレータでBASICROM.66の代用品として使えます。とは言っても、拙作のCP/M BIOSとS-OS"SWORD"が動くための最低限の機能のみの実装です。

実装されているサブルーチンは以下の4個です。


SR対応について

2012.11.24版よりSRに対応しました。ただし、「SRでも起動する」という程度の対応なので、あくまで1D版で、そのためSRにおいては以下のような制限があります。

以前のバージョンではそもそもSRで起動できなかったのですが、その経緯について説明します。これまで、6601やmkIISRは持っていたのですが外付けFDDが無いため、実機確認は6601のみ、SRとしての動作確認はエミュレータでのみ行っていました。まあ6601で動けばたぶんSRでも大丈夫だろうと高を括っていたのですが、いざ66SRに触らせて頂く機会を得て試してみたところ、全然動きません。その後自分でも66SRを入手して、予想外の問題点が色々あることがわかったので、どういった点を修正して動くようにしたのか、参考までに書き留めておきます。

1. FDDに関する問題点

拙作のBIOSでは、ブート後接続ドライブ数を確認するために、ROM内ルーチンをコールしています。mkII/6601では42B9番地をコールしているのですが、SRでは4292番地を呼ばないといけないようです。どうやら42B9hのルーチンもそのまま残っているようで、エミュレータでの確認では気付くことができませんでした。もしかして、1D用ドライブと1DD用ドライブとで担当ルーチンが違うんでしょうか?ともあれ66SRでの動作が確認できたので、とりあえず良しとしたいと思います。

対処方法としては、SR/非SR共にROM内ルーチンの同じ部分からこのルーチンが呼ばれているので (601Ch)、そこに書いてある番地を呼び出すという処理にしました。また、ディスクリードルーチン (4274h) にも変更があるようで、ワークエリアとして使う領域が増えています (FE80h-FE84h)。そこを設定するためにもやはり4292hの方をコールする必要があるようです。BIOS側で使用するエリアと重なっていたりしたので、退避などして対応しました。また、この変更に伴い代替ROMファイルも更新しました。

2. VRAM設定における問題点

SRのVRAMまわりは非SRとはかなり違うのですが、それはSR独自の画面モードの話です。非SRの画面モードであればmkII/6601と全く同じ……そう思い込んでいたのが落とし穴で、微妙な仕様の違いに引っかかっていました。

非SR画面モードは更に2種類に分けられます。初代機画面モード (32x16のテキストと256x192のグラフィック) とmkII画面モード (40x20のテキストと320x200のグラフィック) です。ポートC1hで画面モードを選び、ポートB0hでRAMのどのエリアをVRAMに割り当てるかを選ぶのですが、B0hに書き込む値とVRAMアドレスの対応は、モードによって異なります。以下に簡単にまとめてみました。

表8:ポートB0hとVRAMアドレスの対応
B0hの値 初代機画面モード mkII画面モード
0 C000h 8000h
2 E000h C000h
4 8000h 0000h
6 A000h 4000h

さて、ここで問題です。初代機画面モードでポートB0hに0を書き込んだあと、ポートC1hでmkII画面モードに切り替えました。この時、VRAMアドレスはどうなるでしょう?

答え:SRではC000h、非SRでは8000h

モード切り替え時、SRでは切り替え前の「VRAMアドレス」が維持されるのに対し、非SRでは切り替え前に「B0hに書き込んだ値」が維持されてVRAMアドレスが決まるようです。切り替え後に改めて0を書き込むと、どちらも同じく8000hになります。以下にテストプログラムを示します。モード5ページ3 (スクリーン1のVRAM=8000h) 用です。


10 OUT &HC1,6:REM 32x16
20 OUT &HB0,0:REM VRAM=C000h
30 OUT &HC1,4:REM 40x20

非SRでは一瞬画面がチラつくだけですが、SRでは画面が見えなくなってしまいます。非SRの場合40x20モードになった時点でVRAMアドレスが8000hに再設定されますが、SRではC000hのままだからです。手探りで「OUT &HB0,0」と打ち込むと、VRAMアドレスが正しく再設定されて元に戻ります。また、20行目を変更して以下のようにすると、今度はSR/非SRで逆の動作になります。


10 OUT &HC1,6:REM 32x16
20 OUT &HB0,4:REM VRAM=8000h
30 OUT &HC1,4:REM 40x20

エミュレータでの対応は現状では分かれており、PC6001VW ver 307a2ではSR/非SR共に非SR的な動作を、iP6 Plus Rel.4.7ではSR/非SR共にSR的な動作をするようです。

3. キー入力における問題点

スクリーン1版の仮想80桁モードでは、GRAPH+カーソルという (ちょっと変な) 操作方法を使います。66SRではこれがうまく動きませんでした。

PC-6001シリーズでは、キー入力はサブCPUが処理し、ユーザプログラムは割り込みによってそれを知るという形になっています。通常文字が入力されると要求番号02hの割り込みが、グラフィック文字が入力されると要求番号14hの割り込みがかかり、次いでキーコードがサブCPUから送られます。また、グラフィック文字が割り振られていないキーとGRAPHキーを同時に押した場合にも同様に割り込みがかかります。この場合のキーコードは0です。

一方、カーソルキー/スペース/シフト/STOPに関しては、サブCPUに調査コマンドを送ることで、「現在押されているかどうか」をユーザプログラム側が能動的に調べることが可能です。そこで、拙作のBIOSでは「14hの割り込みがかかった時に、割り込みルーチン内で調査コマンドを発行する」という手順を踏むことで、GRAPH+カーソルを検出しています。キーを押してから離されるまで普通何十ミリ秒かかかるので、その間にキーの状態を調べようというわけです。(ちなみに、このロジックだとカーソル+カナキーなんてのも拾ってしまいます)

しかし66SRでは、2/3くらいの確率でしかカーソルキーを検出できませんでした。キーを押しっ放しにしておいても、返ってきた結果がしばしば「何も押されていない」という風になってしまいます。どうやら、キー割り込みがかかった直後、しばらく調査コマンドを受け付けない状態になることがあるようです。一方、同様のプログラムをmkIISRで走らせると100%検出できるので、恐らく66SRのセパレートキーボード特有の挙動なのではないかと思います。

更に困ったことに、検出できる時とできない時の判断方法がわかりません。ポート92hを使い、CPUとサブCPUの橋渡しをしている8255の状態を調べても同じなので、やはりそこから先、サブCPUとキーボード側の問題のように思われます。しょうがないので、調査コマンド発行前に空ループで適当に時間を潰すようにしたところ、無事動くようになりました。


参考文献

CP/M移植にあたって参考にした資料、今後CP/Mを移植する人にとって参考になりそうな資料などを挙げておきます。

ウェブ上の情報

書籍・雑誌


トップ > 雑多なもの > PC-6001mkII/6601 CP/M導入メモ・その2