SHARP PC-1600KでCP/M80!その7

悩んでいたフォント問題

PC-1600Kへ移植しているCP/M80の続報だ!

今は表示系のプログラムを触っている。

先日のブログで「表示をどうしようか迷っている」という旨を書いていた。Twitterでも同様の嘆きをしていたら、なんとフォントを書いてくださった方が現れた!!しかも4x4ドット(下の空白いれたら4x5ドット)のサイズ!

 

自分のセンスでは絶対に描けないような、超イケてるフォントを作成してもらえたのは本当に助かる!(^O^) すぐに製作者へご連絡して、使わせてくださいとお願いした!

敬意と感謝の意を込めてこのフォントを「案山子フォント」と呼んでいる(^-^)

f:id:PocketGriffon:20210508222728j:plain

↑これがPC-1600Kへ実際に表示させてみた案山子フォント。

Mac上で作ったビットマップデータを、そのままPC-1600K上で「絵として表示」しているだけだ!(^^; 小さいながらもしっかりと読めることが分かるだろう。

 

f:id:PocketGriffon:20210508223121j:plain

↑こちらも「絵として表示」をしている、CP/Mが起動した時の想像図(^^)

f:id:PocketGriffon:20210508223325j:plain

↑こちらが実際に起動していたCP/M80の状態。↑の2つを比較しても、だいーぶ雰囲気が変わることが分かるだろう。

よし!だいぶモチベーションが上がった!(^-^)

 

CP/Mへ組み込む前に組み立ててみる

さて、使うフォントが決まったところでCP/Mへの組み込みをしていこう。

…と思ったけれども、今まで表示系は極力触らずにIOCSの機能を使う流れに徹していた。そのため、脳内で諦めていた事もたくさんあるのだ!

せっかくフォント系を組み込む事にしたので、脳内にあった構想を整理してみたい。

・フォント表示をどうやって実現しよう?

・仮想スクリーンを持ち80x24をサポートしたい

・仮想スクリーンの中をスクロールして自由に見てみたい

・全体を大雑把に把握出来る機能がほしい

エスケープシーケンスはどこまでサポートする?

ひとつずつ考えていってみる。

 

フォント表示をどうやって実現しよう?

まず今回採用したフォントを用いると、標準状態で26x4キャラクタという構成だった画面が、一気に39x6キャラクタとなる。画面中に表示出来るキャラクタ数は104 vs 234と倍以上だ!

f:id:PocketGriffon:20210508225119j:plain

↑の図は外枠が80x24の大きさ、そのうち黄色が標準フォントで見える範囲、赤が今回採用した案山子フォントで見る範囲だ。めちゃ広い!!これは是が非でも実現したい!(^0^)

だが…これをPC-1600Kで実現するにはいろいろとハードルがある。

 

PC-1600KのVRAMはオンメモリにはなく、表示させたいデータをいちいちLCDCへ送り込まないといけない。ROM内部でもスクリーン情報はメモリ内に保持しているが、実際の表示データはメモリ中に無く、その都度LCDCへ送り込む事で解決している(んだと思う)。

 

VRAMの構成は、縦8ドットが1バイト構成となっている。PC-1600K標準のフォントも横方向は6ドットとなっているが、縦は8ドット構成だ。ハード的にもとても理にかなっていると思う。ただし横方向も64ドット境界という問題があり、横6ドットはその点で少々面倒だ。

f:id:PocketGriffon:20210508225719j:plain

↑の図は…だいーぶ分かりづらいが、各行の文字を描画しようとした時、どっちに何ドットずれているのか…を示したモノだ。描画にはANDしてORすることが必要だが、その辺りを自分の中で分かるように図式にしてみたのだ(^^;

 

私はこんな感じに図を書きながらプログラムを妄想したりする。世のプログラマの多く(おそらく多く)は、コーディングし始めた時にはもう脳内でプログラミングが終わってる。そしてより慣れたプログラマであれば、完成時のコードサイズと実行速度もだいたい目処がついていて、そしてソレを外すことはまずない。

 

今回は増設メモリもあることなので、画面に表示すべきビットマップ情報をメモリ内に持つ事にする。スクリーン情報を元にビットマップ情報を書き換え、それをLCDCに転送する。効率その他は後から頑張ることが出来る構造にしておけば、動かす事に専念出来る。

よしこれで行こう(^-^)

 

仮想スクリーンを持ち80x24をサポートしたい

もうこれは個人的な趣味なのだが、CP/M上で動くいくつかのゲームは画面サイズが80x24以上あることを期待している。特にSTARTREKROGUEはそうで、この2つを動かしたいがためだけに80x24をサポートする事にした!(^^)

 

色情報やアトリビュート情報(太字、反転、アンダーラインその他)はサポートしない。アンダーラインはもしかしたらサポートしても良いかも知れないけど、フォントの上と下がくっついてしまうため文字の可読性が非常に悪くなる恐れがある。太字は無理だって!(^^;

 

そう考えると、メモリ中には80x24=1920バイトのエリアがあれば事足りる。ここで…ちょっと考えてみたが、せっかくなので横を256桁にして座標計算を簡単にしてみたい。24個のアドレステーブルを持てばいいだろう…とも思ったが、将来的にアセンブラ化した時のための布石にしたいので、ここは横256に拘りたい。

 

というわけで、スクリーン情報は256x24=6140バイト確保した。本来の容量の3.2倍だ!

 

仮想スクリーンの中をスクロールして自由に見てみたい

案山子フォントを使うことで画面上に39x6キャラもの情報量が表示されることとなるが、内部的には80x24の広大な(!)スクリーンが存在する。

大きなスクリーンの一部を覗く「のぞき窓」の役割をするのがPC-1600Kの液晶だ。…となると、のぞき窓を動かして全体を見たくなるだろう。むしろ見えないと広くした意味がない!

 

そこで、CP/M80実行中に[MODE]キーを押す事により、画面を自由にスクロールさせるモードに切り替える方式を考えた。つまりこんな感じだ。

f:id:PocketGriffon:20210508233402j:plain

↑これが初期画面。80x24の左上が見えている状態。

 

f:id:PocketGriffon:20210508233507j:plain

↑のぞき窓を右に動かしてやると、右側の見えていなかった情報が見える。

 

f:id:PocketGriffon:20210508233554j:plain

↑のぞき窓を下に動かすと、下側の情報が見える。

どの状態からでも[CL]を押すと、左上のポジションに戻れる。

これがソコソコの速度で動いてくれれば実用性は高いはずだ。

 

全体を大雑把に把握出来る機能がほしい

のぞき窓を使って動かしてみていたが、どうにも全体像が分かりづらい。

ものすごーく大雑把で良いので「画面全体はこんな感じになってる」というレイアウトが表示されると助かるなぁ…と思った。

f:id:PocketGriffon:20210508233842j:plain

↑というわけで作ってみたのがこちら。

こんな感じに、80x24の領域の文字が置かれている部分のドットが点灯する。この状態では文字が読めないが、なんとなくあそこにこんな情報、こっちにこんな情報というのが想像出来るだろう(^^)

 

これを「ビューモード」と名付けて、上記ののぞき窓モードからいつでも呼び出せるようにしたら使いやすそうだ!

 

エスケープシーケンスはどこまでサポートする? 

エスケープシーケンスとはなんぞや…という説明まで書き始めると大変なので、ここでは画面に表示されるテキストを制御する便利なモノ…くらいに捉えてくれたらだいたいあってる。

 

サポートする端末はVT100を基準として考えようと思う。以前、CP/Mエミュレータを作った時に、なんとなくSoroc基準で作っていたら、いくつかの動かないソフトが出てきてしまい、途中からVT100に変更するという疎き目にあった(^^;;

 

ただVT100の全部をサポートするのは億劫だし、PC-1600K用CP/M80では意味のないコードも存在する(太字とか無理だってば!(^^;;) そこで必要なものを最低限のみ実装する方式にする。

STARTREKは制御コード($00〜$1F)があれば動くらしい。問題はROGUEだな…。

実装していないエスケープシーケンスが来たら、その旨を表示して停止するようにしておけば、対応も早そう。

よし、エスケープシーケンスの実装もなんとなく決まった!

 

作ってみるよ!

さて、ここまで材料が揃えばあとは作るのみだ!

 

実は上の写真の一部は、実際に動いてるところを撮影している。開発速度を高めるために、いきなりCP/Mへ組み込むことはせず、単独で動くようにしている。

 

メモリは贅沢にも増設メモリの1バンク分(16KB)を充てがうことにした。でも大きなスクリーンやらビットマップやらを保持するので、そこまで余裕があるわけではない。

 

実機で動かしてみたら…想像していた通りの重さ(しかも激重!)になった(^^;; 回避方法も考えてはあるけれども、まずは安定動作が先!ちゃんと動くようになったら、楽しい楽しい高速化をしてみようと思う!そこまでまずはたどり着かねば(^^;;

 

実は今、私生活で大変な状態になっているので、あまりCP/M80の開発に時間が掛けられないでいる。ちょこちょこTwitterなどで情報を流していくので、気長にお待ちくだされ(^^)

 

ではまた次回!(^-^)ノ

祝!ブログ開設1周年!

ブログを書き始めて、今日でちょうど1年経ちました!

三日坊主で終わるかなぁ…と思っていたけれど、意外に続いてる(^^;;

 

このブログは趣味の垂れ流しというか、普段の私生活では絶対に出せないような素をさらけ出せるので、自分としてはとても気が楽。しかもレトロパソコンやプログラムについて熱く語っても怪訝な顔されないし(読んでる人はしてるのかもw

 

これからも興味の赴くままに、あれこれやっていこうと思います。

もっとレトロで謎なパソコンを触っていくね!

たまにはメジャーマシンも使うけど(^^;

 

これからもよろしくおねがいします!(^o^)ノ

f:id:PocketGriffon:20210507114800j:plain

 

SHARP PC-1600KでCP/M80!その6

悩める表示系

今日は珍しく「こういう事をした」ではなく「こうしようと思ってるけど…でも…」という悩み相談だ!ご意見あったらぜひコメントまたはTwitterで教えて欲しい(T-T)

f:id:PocketGriffon:20210504222911j:plain

まず↑こちらがノーマルフォントでの表示画面で横26桁、縦4行の構成。

 

f:id:PocketGriffon:20210504223016j:plain

そして↑こっちがフォントを小さく表示した場合のサンプルで、横31桁、縦5行だ。

文字数的に言えば104 vs 155と約1.5倍の情報量がある。

そりゃ多い方がいいに決まってるよ…と思ってはいるのだが……

 

今回のCP/M80では、内部的に80x24キャラクタのバッファを持とうと考えている。つまり画面的には80x24の標準サイズがあるんだけど、見えてる範囲が26x4または31x5という制限があるという感覚だ。

気になってるのは、どっちのサイズを選択したとしても、全体を見るために画面を切り替えたりスクロールさせる仕組みは必要だ。どちらを選択しても作業量が変わらないのならば、どっちでも同じじゃん…くらいの感覚に陥ってる(^^;;;

 

画面表示の手間

もしも標準フォントを選択した場合は、表示系の多くはIOCSに任せる事が出来る。1文字だけ再描画したいなどの要求は頻繁に起こるけれど、これを全てシステムに任せる事が出来るのだ。この安心感は計り知れない(^^) 

IOCSの1文字表示処理(PRTANK:0100h)は制御文字を一切解釈してくれない。したがってエスケープシーケンスなどの組み立ては独自で作る必要がある。これはどちらのフォントを選択してもかかる手間は同じだ。VT100とまではいかないが、おそらく近しいものを実装する流れにはなるだろう。

 

縮小フォントを選択した場合は、全ての描画処理を独自にやる必要がある。フォントが縦6ドットという厄介な数字でもあるので、この部分は頑張りどころだ。そして全てを独自で作るということは、システム側のご機嫌を伺わずに作る事が出来るということでもある。この部分のプラス方向に考えて良いと思う。

 

ただ…縮小フォントの場合は、あの厄介なLCDCと格闘せねばならない。画面中の1文字だけを更新された場合には少々面倒な計算とプログラムが必要となる。この辺りも縮小フォントに踏み切れない要因だったりする。

 

…という感じでどっちも決定打がない(ToT)

 

そんな流れで調べていたら、Amstrad NC100というマシンで動くCP/M互換OSが、4x5ドットフォントを表示しているモノがあった!

うーん……ちゃんと読める(^^;;

これまた悩ましいものを見てしまった!

 

あたまから湯気が出そうなので、今日のところはここまで!(^^;;

ではまた次回!(^0^)ノ

SHARP PC-1600KでCP/M80!その5

f:id:PocketGriffon:20210503230237j:plain

ついにPC-1600K上のCP/M80で、スタートレック(ゲーム)が動くようになった!

これはMBASIC.COMの上で動く、それなりのサイズがあるプログラムだ。この規模のものが動くようになれば「とりあえず動くようになったよ」くらいは言っても良いかと思い始めた(^^;;

 

CP/M80 on PC-1600Kディスク事情

さて、前回に引き続きディスク関連を書いていこうと思うけど、前回ほど面倒な話ではなくて、運用面での話を書いてみようと思う。なんにも整備されてないって言いたいだけだw

 

一般的にエミュレータを使おうとすると「ディスクイメージがうんぬん」という、ちょっと面倒な手続きがあるかと思う。エミュレータ側からはホスト側のファイルを直接見ることが出来ないので、1つのファイルをディスクと見立ててアクセスをするようになっている。

ファイルの中に沢山のファイルが入っている…という表現が良いんだろうか。

そのため、そのファイルの中の構造を変更しようとしても簡単にはいかない。コピペなんて簡単には出来ないのだ。

 

今開発しているCP/M80もディスク事情は同じだ。

そのためファイルのやりとりは非常に面倒となる。

今、私が実際にやっている手順を書き記してみると…

 

1、ディスク(増設メモリ)をフォーマットする(独自開発したプログラム)

2、CP/M80が起動した時に0100h番地となるメモリにファイルをロードする

  →専用のローダーを開発

3、CP/M80を起動する(メモリはクリアされていない)

4、「SAVE xx ファイル名」として0100hにあるバイナリをファイルへセーブ

5、実行

 

ファイルのかず分だけ、2〜4を繰り返しているのだ!

なぜこんな面倒な事をしてるのかと言えば、まだまだシステムが安定してるとは言い難く、ツールなどで固めてしまうには時期尚早と感じていたからだ。

 

こんな煩雑な状態なのでドキュメントをまとめておかないと、次にチャレンジする時に絶対に思い出せない(^^; 私はそういうツール類がとても多い…(自慢じゃないけどw

cpmtoolsという便利なものがあるらしいので、自作するばっかりじゃなく使ってみたいと思う。

 

ちなみにディスクはAとBの2ドライブ構成にしてあり、Aドライブが448KB、Bドライブが512KBの容量を持つ。これは1MB増設メモリを装着する事を前提とした構成だ。512KBの増設メモリの場合は448KBドライブが1つとなる(もっと細かく分けても良いけど)。

 

かなり安定してきた!

f:id:PocketGriffon:20210504000607j:plain

ここ1〜2日の間の修正対応で、かなり安定した動作をするようになってきた。本体裏面にあるリセットボタンを押す回数が激減した。

 

開発面で一番効果が高かったのは、正しくCP/M80を終了させてBASICへ戻る事が出来るようになった事だ。今までは一度CP/M80をスタートさせるとリセットするしか復帰する方法が無かった(^^; メモリ周りや割り込みの処理が安定してきた事により、正しく終了させる方法を確立させる事ができた。

 

キー操作をきちんと実装した事も相当ストレス軽減になった。開発の初期にありがちなBS(Back Space)が機能しないなど、とにかく動かすだけでもストレスが溜まった。

PC-1600Kは「SHIFT+なんとか」というキーオペレーションが多く、特に「,(カンマ)」や「:(コロン)」がワンキーでは打てない。そして「"(ダブルクオート)」などは特殊キーに割り当てられていて、コードを変換しない限り使えない状態だ。

この辺りを整備した事でキー入力によるストレスはだいーぶ減った!

 

つい先日まで、しばらく使っていると気がつくとハングアップしていたり、シリアル経由で表示される文字が化けてみたり、どうにも怪しい動きをしていた。シリアルの文字化けは単純に通信状態の問題かと思っていたが、突然のハングアップはそういう事ではない。

MBASIC上で計算をした結果を表示させている時に、答えを間違う場合がある事に気がついた。1+1が2にならず、小数点以下が表示されてしまう。これはトンデモないバグだ(ToT)

これも症状が安定しない事から、真っ先に割り込み処理内のバグが疑われた(^^;;

そしたら案の定、フラグレジスタをPUSHする前に計算式が入っていた!割り込みから抜けてくるとフラグ状態が壊れる事があり、それにより答えを間違える…という現象だった。

これも無事に直せた!

 

同時にBIOSの実装も進めていったおかげで、MBASICからSYSTEMコマンドでCP/M80へ復帰できるようになったり、運用面での安定感が増していった。

 

意味もなくベンチマーク

世界標準のCP/M80が動いたという事で、なんとなくやってみたくなったベンチマーク

MBASICの単純ループで時間を測ったみた!手作業での判定なので1秒くらいの前後がある可能性があるよ!!(^^;

 

10 FOR I=0 TO 10000

20 NEXT I

 

このプログラムの実行速度を測ってみた!

20行の「I」が無い方が速かったり、先頭に「DEFINT I」とか入れると速くなるのは分かっているけど、とりあえずこのプログラムで統一する。

 

PC-1600K上のCP/M80で動くMBASICで測ってみたところ14.10秒となった。

同じくCP/M80上で動くBASICを主言語とするHC-40で測ってみると20.47秒と出た。

あれ…?CPUクロック同じだと思うんだけど、なぜかPC-1600Kの方が速い。

じゃあ…という事で、久しぶりにPC-8300にも登場してもらった。

f:id:PocketGriffon:20210504005218j:plain

その結果、17.69秒だった!

おお、PC-1600K、意外にも速い!!(^-^)

特に高速化のために何かしたわけでないけれども、1割2割も速くなると嬉しいね!

 

CP/M80自体の動作が安定してきた事もあるので、この先はCONOUTの出力先をRS-232Cから元の画面に戻していこうと思う。

その後は運用をもっと便利に出来る環境を整えて一旦キリかな…という感じだ!(^^)

 

ではまた次回!(^-^)ノ

SHARP PC-1600KでCP/M80!その4

f:id:PocketGriffon:20210502105404j:plain

PC-1600Kの狭い画面で開発続けるの大変だなぁ…と思っていたところ、Twitterで「シリアルに出してみたら」という感じのご意見を頂いた!なるほど、ぜーんぜん気づいてませんでしたw これだけブログ内でV20-MBCとか触ってるっていうのに…汗

 

さて今回は、ディスク周りをどのようにしているのかを解説していきたい!

 

ディスク周りの設定など

CP/M80におけるディスクについては、BIOSの設定パラメータの中に「ディスク・パラメータ・ブロック(略してDPB)」というデータが存在する。ここで細かいことを説明する事はしないけれども、詳しく知りたい場合はCP/M80の解説本を読んでみてほしい。本によって説明が異なったりするので、何冊か読んでみる事をオススメする。

 

CP/M80内部ではデータを128バイトひとまとめとして扱っている。ここではこれをセクタと呼んでおく。

DPBではこのセクタが何個あって、どこがどのように使われるのかが記されている。

DPBの書き方によってディスクのサイズが変わったり、アクセス方法が変わったりする。なので、DPBは厳格に定義せねばならない。CP/M80の移植で難しいところはココかも?

f:id:PocketGriffon:20210502113139j:plain

↑難しい事を簡単に書かれている辺りが初心者キラーっぽい(T-T)この図ではDPBLKと書かれているが、本に掲載されているソースではDPBと書かれている

DPBは「どんな感じの機材が繋がっているのか」をCP/M80本体(主にBDOS)に教える定義だが、その機材へアクセスをするのは(自分がこれから書く)BIOSの方だ。極端な事を言えば、ディスクとしてアクセスされたものが、実際のディスクだろうとメモリだろうとシリアルだろうと関係ない。そこはBIOSが吸収してやればいいのだ。

そして今回のPC-1600K CP/M80では、増設メモリをディスクと見立ててアクセスをする。

 

PC-1600Kの増設メモリは以下のようになっている。

f:id:PocketGriffon:20210502114123j:plain

1MB増設メモリはスロット2に取り付けられているので、アドレス8000h〜BFFFhのバンク2と3に見えている。この2つのバンクで32KB分。残りの992KB(1024KB-32KB)分のメモリは縦バンク(図ではVerticalBank)としてバンクを切り替えていけばアクセスが出来る。

以降、図に書かれている横方向に伸びるバンクをHBANK、縦に伸びていくバンクをVBANKと呼ぶ。

 

VBANK0(のHBANK2と3)はメインメモリとして0000h〜7FFFhに顔を出しているのでディスクとしては使えない。

VBANK1(のHBANK2と3の合計32KB)は今後の拡張用として残しておこうと考えている(80x24表示用のバッファとか小さいフォントデータ格納用とか)。

RAMディスクとしてはVBANK2以降を使うこととした。これはDPBに書くことではなく、BIOS側が吸収する。

 

1つのメモリブロックは16KBのサイズがある。1セクタ128バイトなので1トラック=128セクタと管理したら、とても簡単な計算で実アドレスを求められる事となる。

本CP/M80では、Aドライブとして448KB(512-64KB)、Bドライブとして512KB用意した。わざわざドライブを分けたのは、512KBメモリモジュールを持っている人でも使えるようにしたかったためだ。

 

RAMディスクアクセスの問題

ここでひとつドハマリしたバグがあったのでご紹介したい(^-^;

 

CP/M80では、ディスクから128バイトを読み込むバッファ(DMAバッファ)がある。 このバッファ、通常はメインメモリの0080hからの128バイトとなっている。

このPC-1600K CP/M80システムでは、ディスクはメモリモジュールとなっているから、ディスクからデータを読む イコール メモリからメモリへのコピーとなる。正しいメモリバンクを設定しておいてZ80よろしくLDIRすれば安直にコピーが出来る……と思い込んでいた!

 

実際にそのようなコードを組んでみたところ、セーブしたファイルを実行しようとすると、どうにも不安定になる場合がある。この「場合がある」というのが厄介だ。なぜ不安定になるのかがさっぱり見当付かなかった。例えばAというコマンドを実行しようとすると不安定になる、ディスクをフォーマットして書き込んでいくうちに、今度はBのコマンドが不安定になる…という感じで法則性が読めない症状だ。

 

この手の不安定要素は早急に対応しなければ、作業を先に進める事が困難になる。ほぼ半日悩んでようやく原因らしきものを突き止めた。残念ながらこの期に及んでも推測の域は出ていない(TT) 

 

上にも書いた通り、DMAバッファがあるのはメインメモリの0080hだ。ここは64KBフルRAMモードではHBANK1だが、実際にはHBANK3のVBANK0が見えている。

この状態で、ディスクをアクセスしようとしてVBANKを切り替えた瞬間、どうやらメインメモリの0000h〜3FFFhも一緒に切り替わってしまうようなのだ(おそらく4000h〜7FFFhも同様)。メインメモリの0080hへデータをコピーをしたつもりが、別のVBANKに切り替わっていてディスクの情報を破壊する…というのが不安定になる原因だったようだ。

考えてみれば「そりゃそうだ」と思うのだが、メモリマップだけを見ているとコレがなかなか気がつけない(T-T)

 

 仕方ないので、バンク切り替えには影響されないC000h以降に中間バッファを設け、そこを介してディスクとのやりとりをするように構造を変更した。転送時間は2倍掛かる事となったが、これ以降はすこぶる安定して動作するようになったので、まぁヨシとしよう(^^;;

 

現状のPC-1600K CP/M80

一旦、今の状態をまとめておきたい。

まだまだ実装できていない事が多くて、安定動作には程遠い状態だ。

 

まずキー入力がまともではない。SHIFT+なんとかのオペレーションが出来ていないので、ダブルクオートとか「;」などが入力出来ない状況だ。BSも機能しないため、非常に面倒。

 

WBOOTを実装していないため、MBASICでSYSTEMしてもCP/M80に戻れない(^^; いつもリセットしているのだ!面倒すぎるww これはトラック0セクター1にCCP+BDOSを書き込んでやれば良いのだが、システムが安定していないのでまだ先にしたいという想いから。

 

CP/M80自体を抜けて、素のPC-1600Kへ戻る事が出来ない。これも実装しなければならないことがいくつかあるようで、その調査が進んでいない。

 

多分、キー入力とWBOOTを実装すればある程度CP/Mとして「動いた」と見てよいのかも知れないが、そこへ到達するまでにはもう少し時間がかかりそうだ。

 

次回はディスク関連その2を書こうと思う。ディスク周りは面倒が多い(T-T)

ではまた次回!(^-^)ノ

SHARP PC-1600KでCP/M80!その3

f:id:PocketGriffon:20210501104229j:plain

CP/M80のPC-1600Kへの移植、その3

 

とりあえずいろんなソフトを動かしてみては動いたり動かなかったり、安定動作とはほど遠い状態で、あくまでも実験的な側面が大きい今回のCP/M80移植。

MBASICが動いた事で、なんとなくこの辺りまでで良いかな…とかも思い始めたので、この先どうするのかを考えつつも、技術資料だけは残さねば…と思ってのブログ(^^;

次にチャレンジする人(!)のためにも後世に残さねば!

MSX-DOSの移植とかも楽しいかもよ!(ハード構成が違うから簡単じゃないと思うけど…)

 

CP/M80の入手と起動準備

まずはCP/M80起動に必要なCCP、BDOSの入手のお話。

この辺り、だいぶグレーゾーンな事をしているので、ぱぱっと書きたい(^^;;

私は文豪のROMから該当するバイナリ部分を抜き出している!CCPとBDOS部分はバイナリでしか手元にない。前回のブログで「過去にCP/M80を動かすエミュレータを作った」と書いたが、その時にもバイナリ状態のCCP+BDOSでCP/M80を起動させている。

 

なぜこんな芸当でCP/M80が起動させられるのかと言えば、CP/M80は1バイト単位で「どこのアドレスに何の情報が書いてある」のかが細かく決められているからだ。所定の場所に決められた情報を配置する事で、CP/M80は起動させる事が出来る。

f:id:PocketGriffon:20210501100926j:plain

ユーザーが書くべき部分はBDOS直後に続くBIOS部分のみだ。

先頭の34バイトに17個分のジャンブテーブル、その直後に16バイトのディスクパラメータが必要。それ以降は任意のアドレスに好きに書いていく事が出来る。

「なーんだ、そんな簡単なのか」と思われた方も多いかと思うが、CP/M80は移植性が高い構成になっているので、決まりごとさえ覚えてしまえば小難しい事はない。

最低限、8080(またはZ80)の知識と、その移植するマシンについての事の理解がありさえすれば、CP/M80の移植難易度はぐっと下がる。

 

ちょっとだけ補足をすると、文豪のCP/MはPC-1600Kよりも高アドレスで動作するようになっているため、そのままではPC-1600Kで動かす事が出来ない。しかし移植性の優れたCP/MなのでMOVECPMやSYSGENなど、他環境で動かすためのツールがたくさんある。これらを駆使してPC-1600Kで使えるCCP/BDOSに作り変えた。

 

無理矢理CP/M80を起動させる!

もうこのタイトルから言って無茶してるなーって思ってもらえれば(^^;;

CP/M80の起動シーケンスは、正しい方法を取ろうとすれば結構面倒くさい。実際にどのようにしてOSが起動するのかは、その手の本を読めば沢山の情報が出てくる。

しかし今回はお気楽にCP/Mを動かしたいと考えているので、面倒な部分をすべてすっ飛ばして起動させたい。

 

具体的な方法を書いちゃうけど、メモリのC400hからCP/M80のセット(CCP+BDOS+BIOS)をロードし、BIOSの先頭アドレスへジャンプさせる。これだけだ(^^;;

そしてBIOSを初期化する前に(ブログのその1でも書いた)メモリマップの変更や割り込みへの対応をした後、通常シーケンスの初期化をしてCP/M80を起動させている。

 

BIOSには17個のエントリ(BOOT WBOOT CONST CONIN CONOUT LIST PUNCH READER HOME SELDSK SETTRK SETSEC SETDMA READ WRITE PRSTAT SECTRN)が存在するが、このうち起動させるために必要なのはBOOT、CONOUT、SELDSKの3つだ。他のは何もしない(RETのみ記述。SECTRNだけはHL=BCしてRET)だけしておけば大丈夫。起動した直後にCONSTとCONINが必要となる。

 

上記3つのエントリのうち、ちゃんと書かないといけないのはBOOTとCONOUTだけだろう。SELDSKは既定値(HL=ディスクパラメータへのアドレス)を返すようにプログラミングしておけば良い。これだけでCP/M80は起動する。

その後、CONST、CONINを正しく実装すればキー入力を受け付けるようになる、というわけだ。

 

起動までの問題は?

ぱぱっとテキストレベルで書いてみたが、実際には起動までにさまざまな問題が出た。

デバッガやエミュレータがあるわけではないので、動かなかった時の対応は非常に難しい。実はどうしてもバグが取れず、部分的ではあるがZ80エミュレータを用いている。以前書いたCP/M80エミュレータの、Z80エミュレーション部分だけを抜き出してきて動かしていた。

 

そのエミュレータ+PC-1600K本体の2段構えでCP/M80起動を試していたわけだが、どうしてもエミュレータと同じ動作にならず困惑した。

もっとも多かったトラブルが「スタック領域が足りない」というものだった。

 

CCPやBDOSがどのくらいのスタック領域を用意しているのかが分からない上に、スタックが破壊されるバグは、後々になって動作が変になる現象が出るため、非常に追いづらい。実験をしてみた結果、どうやらCCP、BDOSでは数バイト程度のスタックしか用意されていない感じだ。CP/M80という非常に軽い(軽快という意味)OSから呼び出す、PC-1600Kという非常に多機能なIOCSを呼び出すにはスタック不足になってしまうらしい。その結果、スタック付近のプログラムを破壊してしまい、後に動作が変になる…という症状だった。

 

おかげでIOCSコールをする部分にはすべてスタックを別途用意するコードを入れねばならなくなったため、さらに処理が重くなった(^^; 同様に割り込みでも同じ問題が起こる事が予想出来たため、割り込み処理の先頭にもスタックの設定をするコードが入った。うむむ…重いぞw

f:id:PocketGriffon:20210501112606j:plain

 

CP/M80の事を書いていると、テキストばっかりになってしまい、読んでる側もつまらないだろうなぁ…と思う(^^; 

一時期、文豪を活用するためにCP/Mの本を買いまくってた時期があった。そのため、私の手元にはCP/Mの本がとても沢山ある。まさか令和の時代になってまで大活躍するとは思っていなかったw

 

次回、増設メモリを用いたRAMディスクの話をしつつ、この先どうしていこうかの話を書いて終わりくらいだろうか…。MBASICが動いたことでちょっと安心してしまった自分がいる(^^;

 

ではまた次回!(^-^)ノ

SHARP PC-1600KでCP/M80!その2

CP/M80のPC-1600Kへの移植!

その後も少しずつ進めていて、標準コマンドはだいたい動くようになったかな?というところまで来た。

f:id:PocketGriffon:20210429092508j:plain

DDT(デバッガ)が起動した時の画面↑

 

このブログでは、PC-1600K上でどのようにしてCP/M80を動かしているのかを、自分の備忘録も含めて書いていこうと思う。

 

先に正直な事を書いてしまうと、PC-1600K自体がCP/M80を起動するだけの能力を備えているので出来ているのであって、ソフトウェアで頑張って動かしました…という事で無かったりもする。当然、PC-1600Kを開発された方はCP/M80の移植も想定していたんだろうな…と思う(^^) 実際にやるかどうかは別として!!(^^;;

 

PC-1600K CP/M80のメモリサイズ

先日のブログで、大体のCP/M80イメージを固めた。

SHARP PC-1600KでCP/M80!その1 - レトロパソコンであそぼう!

この中で「IOCSを利用する」と決めた流れがあったが、これにはもう少し制限が加わる事になる。IOCSはメモリ中のF000h〜FFFFhをワークエリアとして利用する。この部分は詳しく調べられていないため、どこを壊して良いかなどの情報を持ち合わせていない。そのため、この領域は「触らない部分」として残す必要がある。

 

ところで…良くCP/M80の記述を見ていると「xxK CP/M」などの表記がある。「48K CP/M」「64K CP/M」とか、見た覚えがあると思う。このメモリサイズがどうやって決まるのかが気になっていた。

 

CP/M80の事について書かれた本は数あれど、私は移植する際にはこの本を活用している。

f:id:PocketGriffon:20210429095434j:plain

2014年にCP/M80が動くエミュレータを書いた際にもとても参考にした。

この本に、こんな記述がある。

f:id:PocketGriffon:20210429095548j:plain

CCPの先頭番地…というのが、実際にCP/Mシステムを置くアドレスを指しているのだが、このアドレスから始まるシステムであればCP/Mのサイズはこう名乗って良い…という感じだ。

なんとなく…このCP/Mで「使えるメモリサイズ」ではなく、このCP/Mが「動いているシステムのメモリサイズ」を表しているような気もする。

 

今回のPC-1600K CP/M80はCCPアドレスがC400hから始まっている。ということは56K CP/Mと名乗っても良いのかな…と思い、オープニングタイトルに56Kと書いた!

 

64KBオールRAMメモリマッピング

ここを詳しく書いていくと技術書並の情報量が必要になってしまうので、とても簡潔に済ませてしまおうかと思う(備忘録にはなる程度にw)。

 

PC-1600Kで64KB全体をRAMにするためには条件がある。

PC-1600Kをひっくり返して裏を見ると、拡張モジュールを取り付ける部分に「S1」「S2」と刻印がある。これがスロット1と2の事だ。

  スロット1に16KB以上のメモリモジュール

 スロット2に32KB以上のメモリモジュール

上記の拡張ハードウェアが必須。これが64KBオールRAMの最低条件。

PC-1600Kで良くある構成の、スロット1にCE-1600M、スロット2にCE-1650M(文節変換辞書)が取り付けられている人は、残念ながらアウトだ。

f:id:PocketGriffon:20210429101936j:plain

↑頑張って上記のどちらかを手に入れよう!

 ※たとえCE-1600Mを2つの構成にしたとしてもCP/M80は動かない(この辺りは後のブログで紹介)。

 

PC-1600Kはメモリマッピングがスロット1とスロット2で別々に設定出来る。ここの設定が非常にややこしい。

PC-1600Kデータブックに以下の記述がある。

f:id:PocketGriffon:20210429103019j:plain

これはスロット1の事を書かれた図なのだが、残念ながら誤植がある。

図2-63のA=1とA=0、これが左側のパラメータに書かれている言葉と図が逆になっている。これは書かれている文章の方が正しい。図のA=1とA=0を逆にして読む。

私が所有している本が初版だからかも知れないが、直ってる版を持っている人は当たりかもw

64KBマッピングではA=0(ややこしいが図の上、A=1と書かれている方)を利用する。

これでスロット1のαのメモリがバンク0の8000h〜BFFFhに見えるようになる。

 

スロット2の方はこちら。

f:id:PocketGriffon:20210429103441j:plain

これも…スロット1の図と同じα/βの記号が使われてしまっているため分かりづらい(ToT) 図だけを見た時に何を意味しているのかが理解出来ない。この図は「スロット2のメモリがメインメモリのどこに割り振られるのか」を示している。

 

こちらはA=02hを使用すると、条件的には上の図が採用される事となる。

これでバンク1の0000h〜7FFFhにスロット2のメモリが顔を出す。気をつけなければならないのは、メモリのβ側が0000h、α側が4000hから見える。なんとなく気持ち的には逆なのだが、そういうモノらしい(-_-;

 

上記の設定に加えて「メモリ空間にマッピングされるバンク設定」をする。

f:id:PocketGriffon:20210429105259j:plain

0000h〜3FFFhはバンク1(スロット2のβ)

4000h〜7FFFhはバンク1(スロット2のα)

8000h〜BFFFhはバンク0(スロット1のα)

C000h〜FFFFhはバンク0(標準搭載のメモリ)

ということで、I/Oポート31hには0_000_001_1b(つまり03h)を出力する。

 

これで64KB全てがRAMになる!

 

……と思ったら大間違いだった。

いや概念的には正しいのだが、これだけではダメだった。

IOCSのプログラムが置いてあるアドレス空間が切り替わってしまうため、メモリマッピングを変更した瞬間に暴走しちゃうのだ。そりゃそうだ!(ToT) 

これを回避するためには、IOCSのプログラムをメモリマッピングの影響のない領域、具体的にはC000h〜FFFFhに移動させる必要がある。これはもうIOCSのプログラムを解析して移植するしかない!(TOT)

ここまででようやく64KB全ての空間がRAMとなった。結構めんどい(T-T)

 

IOCS呼び出し

ちなみにこの状態ではCP/M80からIOCSを呼び出す事が出来ない。上にも書いた通り、IOCSのプログラム自体が見えなくなっているからだ。

IOCSを呼び出す際には、PC-1600Kが起動された時の状態にメモリマッピングを戻す必要がある。そのためにマッピングを切り替える前の状態を記録しておく必要がある。

実験した結果、I/Oポート31hは直接読めるようだが、スロット1、2のマッピングを切り替えるI/Oポートは読めないようだった。ワークエリアに保存されている値を見つける事が出来たため、それを利用することに。

 

Z80割り込み

さらに割り込みをONにすると不安定…というか、暴走するけれどもタイミングが一定ではない…という、割り込みにありがちな問題が出た。これも割り込み先のアドレスがなくなるために起きる問題だ。

 

PC-1600Kデータブックによると、割り込みはZ80のモード2を利用しているらしい。モード2の割り込みって珍しい気がする…。何度と無くZ80エミュレータを書いた事があるので、それぞれの割り込みについて動作も理解しているつもりだが、モード2って誰が使うんだろう…と思いつつ実装をしていた(^^;; まさかここで遭遇するとはw

 

モード2割り込みはちょっと理解が難しい。Z80のIレジスタに上位8ビットを設定、周辺機器(I/Oポート)に下位8ビットを設定する。割り込みが掛かるとPCレジスタに「Iレジスタ*256+周辺機器から送られてきた値」が代入される。もうこの時点でワケ分からん…となるだろう(^^;;

 

割り込みの処理もIOCSを解析し、似た処理を自前で書くこととなった。具体的には割り込みが掛かった時点でメモリマッピングを元に戻し→割り込み処理→64KB RAMへ戻す、という事をしている。割り込み処理自体が重くなってしまったのは心配だが、なんとか動いてる。

 

なんとなく文章ばかりになってきて読む側もつまらないと思うので、今回はここまで!

次回はCP/M80のブート、BIOSの書き方などをご紹介する予定!(^-^)

 

ではまた次回!(^-^)ノ