2012.5.18
前へ
次へ
ホームページトップへ戻る

復活!CP/M ワンボードマイコンでCP/Mを!
CP/MがTK−80互換のワンボードマイコンの上で復活します
ND80ZVとMYCPU80の上でCP/Mが走ります

[第124回]


●BS(Back Space)プログラム

CP/Mでは、キーボードからの入力も、画面への表示もBIOSをコールすることで行ないます。
前回の説明ではそこのところがちょっとあいまいになっていました。
通常のユーザーはキーボードからの入力にはファンクションコール01(コンソール入力)を使い、画面への表示はファンクションコール02(コンソール出力)を使います。
しかし今回のお話は、そもそもそのファンクションコール01と02のプログラムをどうしようか、というお話ですから、もっと下のレベルでのお話になります。

通常のユーザーはBIOSのレベルには立ち入りません。
しかしシステムを作り上げるためにはBIOSを使わないわけにはいきません。
BIOSレベルでのキーボードからの入力はCONIN、画面への表示はCONOUTになります。

それだけの説明を読みますと、単純にファンクションコール01=CONIN、ファンクションコール02=CONOUT、というように考えてしまうかも知れませんが、実体は全く異なります。
CONINはただキーボードから入力されたコードを受取るだけで、何の判断も行ないません。
CONOUTも渡されたコードを画面表示用文字コードとして送出するだけで、そのほかの何の情報も送りません。

スタンダードなCP/Mの機能としては、たとえば画面の表示位置(カーソルポイント)の制御などは全く備えておりません。
ただのCONIN、CONOUTがあるだけです。

しかし前回お話をしました、BS(Back Space)は皆様ご存知の通り、カーソルポイントを1つ前に戻します。
カーソル制御のできないシステムでどうやってBSの機能を実現するのか?
というのが前回からの宿題でありました。

それでは、前振りはこのくらいにいたしまして、さっさく本題にかかりましょう。
CP/Mはカーソル制御の機能をもってはいませんが、唯一その制御に関わる機能が存在します。
それが前回ヒントとして書きましたCR(Carriage Return)です。
復帰などと訳します。
もともとはタイプライターで印字ヘッドを左端に戻すことを意味した言葉だったようです。
TEXTファイルなどではCRコード(0DH)だけで復帰だけではなくて改行も一緒に行なってしまうようなテキストエディタが一般的ですが、もともとのCRコードには改行の意味はありません。

改行のためのコードとしてはLF(Line Feed。0AH)を使います。
ですから、復帰・改行を合わせて、0D・0Aと2バイトを画面に送ることで、カーソルポイントが画面左端の1行下に移動します。

では、唯一のカーソル制御コードであるCRを使って、どうすればカーソルポイントを1文字前に戻すことができるのでしょうか?

その答えといいますか、考え方を先に説明してから、あとで実際のプログラムをお見せすることにしましょう。

前回の終わりのところでBSを実際に使っている画面をお見せしました。

A>abcde_

という表示です。
ここでA>はシステムによる表示で、abcdeはキーボードからそのように入力したときのエコー表示です(実はこのエコーもシステムによって行なわれています)。
この状態を図で示すと、こうなります。



ある1行の表示画面の左端を00として、そこから順に1文字ずつ番号をつけます。
これがカーソル位置を示す番号になります。
メモリにカーソル位置カウンタを用意します。
さて、そのカウンタはいつクリアするとよいのでしょう?

そうです。
CRコードをCONOUTで送出したときに、カーソル位置カウンタも00クリアします。
そして、CR以外の文字コードをCONOUTで送るたびに、カーソル位置カウンタを+1すればよいですね。

さて、そこで、今カーソルは07の位置にあります。
ここでBSコードが入力されてきました。
必要な作業は「実際の画面」に表示された文字’e’を消して、同時にカーソルを06の位置に「実際の画面」で戻すことです。

そんなことはムリ…。
と思ってしまいますが、上の図をよーく見ていると、ほら、できるじゃありませんか。

まず、CRコードをCONOUTで送出します。
ただしこのときはカーソル位置カウンタはクリアしないようにします。
すると実際の画面上でカーソルは00の位置に戻ります。
ここからがマジックのはじまりです。
現在のカーソル位置(ポジション00)から、カーソル位置カウンタの数値と同じだけ、CONOUTを使ってコード20H(スペース)を送出します。
すると図の、実際の表示画面のように、表示がクリアされます。



本当はポジション06だけをクリアすればよいのですが、カーソル移動機能がありませんから、最初からその位置までスペースを表示させていきます(このときもカーソル位置カウンタは更新しないようにします)。
カーソル位置カウンタの値が00のときは以下の作業はパスします。つまりBSは行なわれません。

そして最後の仕上げです。
実は図の上側に表示していた文字列なのですが、これはカーソル位置カウンタと同様に、同じタイミング(CONOUT送出時)に、あらかじめ用意しておくラインバッファに図の順序で格納しておくのです。
カーソル位置カウンタは常にそのバッファの現在のアドレスを示す位置ポインタでもあります。
CRコードを送出するときにカーソル位置カウンタは00クリアしますが、ラインバッファはクリアする必要はありません(無駄な作業はする必要はありません)。
なぜ、クリアしなくてもよいのか、ということにつきましては、この説明を最後まで読んでいただければ納得できることと思います。

最後の仕上げとして、ラインバッファの先頭から1文字ずつ、カーソル位置カウンタの値−1回分、CONOUTで送出します(カーソル位置カウンタの値が01のときは、なにも送出しません)。
そして、その値をカーソル位置ポインタにセットします。



表示されていた文字が1文字消えて、カーソルが前に移動しました。

しかし。
このBSの機能を実現するためだけに、ラインバッファを用意して、そして画面に文字コードを送るたびに、毎回バッファへの文字コードの書き込みと、カーソル位置カウンタの更新を欠かさず行なわなければなりません。
なかなかに面倒ではあります。

実は、この機能はファンクションコール02に組み込んでありますから、それを使わないでユーザーが勝手にBIOSのCONOUTルーチンを使って画面表示を行なうと、BSは正しく機能しなくなります。
CP/Mに限らないのですが、ユーザーがBIOSを直接コールすることは危険が伴うということのひとつの参考例でもあります。

ところで。
ファンクションコールにはコンソールバッファ入力の機能もあります(ファンクションコール0A)。
上で説明しましたラインバッファは、そのコンソールバッファなのか?とお考えかも知れません。
ここで説明しましたラインバッファは、コンソールバッファとは別物です。
あくまでBSのためだけに必要な表示用のバッファなのです。

考え方はそういうことだとわかったとしまして、理解できたこととそれをプログラムに書く、ということとはまた別の次元になります。
どのようなプログラムを書けばよいのでしょうか?
こういうプログラムがそれほど悩まなくても書けるようになれば、一人前といえるでありましょう。

これが実際のプログラム(アセンブルリスト)のファンクションコール01と02の部分です。
BSの処理は中ほどにあります。
たったこれだけ、というくらい短くまとまっておりますでしょう。
ファンクションコール02がファンクションコール01でもコールされていることに注目してください。

              ;
              ;CONSOLE INPUT FCALL 01
              ;
C4DE C5       CONIN:PUSH BC
C4DF CD09D2   	CALL B_CONIN
C4E2 F5       	PUSH AF
C4E3 5F       	LD E,A
C4E4 CDEAC4   	CALL CONOUT
C4E7 F1       	POP AF
C4E8 C1       	POP BC
C4E9 C9       	RET
              ;
              ;CONSOLE OUTPUT FCALL 02
              ;
C4EA E5       CONOUT:PUSH HL
C4EB C5       	PUSH BC
C4EC 2A64D0   	LD HL,(CURSOR)
C4EF 7B       	LD A,E
C4F0 FE08     	CP 08;BS
C4F2 CA18C5   	JP Z,CONOUT2
C4F5 FE09     	CP 09;TAB
C4F7 CA45C5   	JP Z,CONOUT3
C4FA FE0D     	CP 0D;CR
C4FC CA5AC5   	JP Z,CONOUT4
C4FF FE0A     	CP 0A;LF
C501 CA5AC5   	JP Z,CONOUT4
C504 FE1B     	CP 1B
C506 DA64C5   	JP C,CONOUT44;ctrl+
C509 4B       CONOUT12:LD C,E
C50A 71       	LD (HL),C
C50B CD0CD2   	CALL B_CONOUT
C50E 23       	INC HL
C50F 7D       	LD A,L
C510 FE50     	CP 50
C512 CA5EC5   	JP Z,CONOUT42
C515 C361C5   	JP CONOUT43
              ;BS
C518 7D       CONOUT2:LD A,L
C519 B7       	OR A
C51A CA64C5   	JP Z,CONOUT44
C51D 0E0D     	LD C,0D;CR
C51F CD0CD2   	CALL B_CONOUT
C522 45       	LD B,L
C523 0E20     	LD C,20
C525 CD0CD2   CONOUT22:CALL B_CONOUT
C528 05       	DEC B
C529 C225C5   	JP NZ,CONOUT22
C52C 0E0D     	LD C,0D;CR
C52E CD0CD2   	CALL B_CONOUT
C531 2D       	DEC L
C532 CA61C5   	JP Z,CONOUT43
C535 45       	LD B,L
C536 2100D1   	LD HL,LINEBF
C539 4E       CONOUT23:LD C,(HL)
C53A CD0CD2   	CALL B_CONOUT
C53D 23       	INC HL
C53E 05       	DEC B
C53F C239C5   	JP NZ,CONOUT23
C542 C361C5   	JP CONOUT43
              ;TAB
C545 0E20     CONOUT3:LD C,20
C547 71       	LD (HL),C
C548 CD0CD2   	CALL B_CONOUT
C54B 23       	INC HL
C54C 7D       CONOUT32:LD A,L
C54D FE50     	CP 50
C54F CA5EC5   	JP Z,CONOUT42
C552 E607     	AND 07
C554 C245C5   	JP NZ,CONOUT3
C557 C361C5   	JP CONOUT43
              ;CR,LF
C55A 4B       CONOUT4:LD C,E
C55B CD0CD2   	CALL B_CONOUT
C55E 2100D1   CONOUT42:LD HL,LINEBF
C561 2264D0   CONOUT43:LD (CURSOR),HL
C564 C1       CONOUT44:POP BC
C565 E1       	POP HL
C566 C9       	RET

上のリスト中、B_CONIN、B_CONOUTがBIOSのCONIN、CONOUTルーチンです。

なお、LFをCRと併用しないで、単独で使用することは想定していません。
そういう使い方は可能ですが、ラインバッファのもとでは「ワク」から飛び出してしまいますから、BSの適用範囲外となります。
それを有効にするためには、スクリーンエディタのシステムを構築しなければなりません。

ワンボードマイコンでCP/Mを![第124回]
2012.5.18upload

前へ
次へ
ホームページトップへ戻る