復活!CP/M ワンボードマイコンでCP/Mを!
CP/MがTK−80互換のワンボードマイコンの上で復活します
ND80ZVとMYCPU80の上でCP/Mが走ります!
[第128回]
●ファンクションコール0A(コンソールバッファ入力)
CP/M2.2で[BS]の機能が正しく働くようになりましたので、あらためてその動作を確認してみましたところ、同じコード08Hでもキー入力コードと画面表示コードとでは、その動作が異なっていることがわかりました、というところまでが前回のお話でした。
CP/Mでキー入力というのはコンソール入力になります。
BIOSのコンソール入力ルーチンは、ただキー入力コードを取得するだけですが、ファンクションコール01のコンソール入力は、キー入力コードを取得すると同時に、それをエコー表示するため、コンソール出力も行ないます。
このときに行なうエコーとしてのコンソール出力では、08Hは[BS]の動作をします。
しかしファンクションコール02のコンソール出力では、08Hは[←]の動作をします。
そこのところを使い分けるように、CP/M互換DOSのプログラムを直さなければなりません。
なかなかに面倒なことです。
面倒ではありますが、そんな程度のことでめげてしまうような私ではありません。
なんとか直してしまいましたよ。
さてそこで。
そのように直したCP/M互換DOSプログラムが、CP/M2.2と同じ動作をするかどうかを検証していたのでありますが。
まてよ。
コンソール入力は、他にもあったのでは?
おお、そうでした。
ファンクションコール0A(コンソールバッファ入力)もコンソール入力です。
こちらのほうでは[BS]はどのように働くのでありましょうか?
そこに気が付いて、確認してみましたところ。
そこにはなんと。
まさかの複雑な仕組みがあったのでありました。
いえ。
「まさか」といいますのは、まあ、言葉のあやでありまして、ファンクションコール0Aにつきましては、それが複雑な仕組みらしいということは、おおよそはわかっておりました。
しかし、そんな面倒な仕組みは要らんだろうよ、ということで無視してしまうつもりだったのですよねえ。
はじめは。
ですけれど。
そこはそれ、持ち前の性格といいますか。
考えておりますうちに、そういう複雑なものだからこそ、なおさら自分の手で作ってしまいたい、という悪魔の誘惑につい乗ってしまうのでありますね。
かくして、いつも泥沼でもがくことになってしまいます。
よくよく落ち着いて考えてみましたら、そもそも[BS]の主たる活躍場所はファンクションコール01(コンソール入力)やファンクションコール02(コンソール出力)ではなくて、このファンクションコール0A(コンソールバッファ入力)であるように思えてきました。
[BS]はどういう時に使われるかといいますと、それはもちろんキー入力で誤入力をしてしまった場合にそれを訂正する時、ということになりますでしょう。
ではキー入力をする時、というのはどういう時かといいますと、ただ1文字だけの入力、という場合もありますけれど、ほとんどはある文字数を入力して、最後に[Enter]キーを押して入力を確定する、という使い方をしている時である、と言えますでしょう。
つまりそういうプログラムを書く時は、ふつうはファンクションコール01(コンソールから1文字を入力)ではなくて、ファンクションコール0A(コンソールバッファ入力)を使うことになると思います。
すると、その場合の[BS]キーの入力は、画面にエコー表示された文字を1文字消去すると同時に、コンソール入力バッファに入力されている文字コードも消去する、という動作になります。
勿論、そこまでは想定内だったのですよね。
ところが。
さらに詳細を調べていきましたら、コンソールバッファ入力の想像を超える複雑な仕組みが明らかになってきました。
あ。ここでひとつご注意を。
以下の説明は、あくまでCP/Mのファンクションコール0Aにおける動作についての説明です。
一般的なキー入力時でもそのような動作をする、ということではありません。
以上。念の為。
さて。
コンソールバッファ入力の複雑な仕組みのひとつは、[Ctrl]キー入力時のエコー表示に関わるものです。
ここで[Ctrl]キー入力といいますのは、[Ctrl]キーと同時にアルファベットキーを押す、という入力のことです。
[Ctrl]+[H]はコード08Hで、これは[BS]キーを入力したのと同じ動作になります。
また[Ctrl]+[J]は0AH、[Ctrl]+[M]は0DHで、それぞれLF、CRコードの入力と同じ動作になります。
しかしたとえば[Ctrl]+[K]は0BHですが、CP/Mではこのコードには特別の機能は与えられていません。
そのような[Ctrl]コードの入力があった場合には、エコーとして、 ^K のように ^ に続けてアルファベットを表示します。
しかしコンソールバッファには0BHの1文字コードが入力されるだけです。
するとエコー表示される画面上のカーソルアドレスとコンソール入力バッファのポインタには1文字分のずれが生じます。
何を言っているか、おわかりになりますでしょうか。
これは面倒なことなのですよ。
たとえば[A]、[B]、[C]、[Ctrl]+[K]、[D]と入力したとしますと、画面には、
ABC^KD
と表示されますが、コンソールバッファには
41・42・43・0B・44のように入力されます。
それではここで[BS]を2回入力するとどうなるでしょうか?
コンソールバッファは2文字消されますから、
41・42・43
の3文字が入力されていることになります。
このとき表示画面はどうなるかと言いますと、
ABC^
ではなくて、この場合には^も含めて3文字が消去され、
ABC
になります。
確かにつじつまが合いますけれど。
[Ctrl]入力をするたびに画面のカーソル位置カウンタとコンソールバッファの入力文字カウンタの値はどんどん相違していきます。
でも[BS]はそこのところをちゃんと間違い無く処理しなくてはなりません。
どうやっているのでしょうかねえ。
でも、こんなもんじゃないのです。
もっとすごいのはTABの扱いです。
TABのキーコードは09Hですが、このコードは画面表示コードとしては働きません。
それがTABとして働くのは、システム(OS)の働きによるのです。
CP/Mでは0、8、16、24、…というように8字ごとに固定したTABが設定されています(先頭が0カラムです)。
この数を16進数で示しますと、00H、08H、10H、18H、20H…になります。
今キーボードから、
[1][2][3][TAB][4][TAB][5] ……@
と入力したとします。
このときコンソールバッファには、
31・32・33・09・34・09・35 ……A
というようにコードが入ります。
では、このときコンソール出力には、エコーとしてどのようなコードが出力されるかといいますと、
31・32・33・20・20・20・20・20・34・20・20・20・20・20・20・20・35 ……B
というようにコードが送られます。
その結果、画面には
123□□□□□4□□□□□□□5_ ……C
と表示されます(□は1文字分の空白です。また_はカーソルの表示です)。
なんと、[TAB]入力のたびに、現在のカーソル位置から次のTAB位置までの文字数を計算して、そこまでに必要な空白の個数を算出して、コンソール出力を行なっているのです。
しかも、それはコンソールバッファの現在の入力文字カウンタをもとに計算されるのではなくて、あくまで画面表示の現在のカーソルポイントをもとに計算されているのです。
これがTABの仕組みです。
その仕組みがどれほど複雑であるか、想像できますでしょうか?
たとえばさきほどの@〜Cの例は、説明を簡単にするために、入力開始時にカーソルポイントが画面の左端にあるものと仮定して説明をしています。
でもいつもそうであるとは限りません。
もしも、
IN:_
と表示されたあとにファンクションコール0Aがコールされたとすると、そのときに@と同じ入力を行なった場合、A〜Cはどのように変化するでしょうか?
この場合でもAは同じです。
つまりコンソールバッファへの入力は画面のカーソル位置(カーソルポインタの値)からは独立しています。
それでありながら、TAB入力に対するエコーはカーソル位置に依存しているのです。
ですからさきほどはBだったものが、
31・32・33・20・20・34・20・20・20・20・20・20・20・35 ……B’
になります。
このようにカーソル位置から、必要な空白の数を計算しているのです。
その結果、画面には、
IN:123□□4□□□□□□□5_ ……C’
と表示されます。
このC’をさきほどのCと重ねて表示してみますと、
123□□□□□4□□□□□□□5_ ……C IN:123□□4□□□□□□□5_ ……C’ |
123□□□□□4_ ……D IN:123□□4_ ……D’ |
123□□□□□4□□□□□□□5_ ……C IN:123□□4□□□□□□□5_ ……C’ |
123□□□□□4_ ……D IN:123□□4_ ……D’ |