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

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

[第130回]


●LF(Line Feed、コード0AH)

前回はLFについてほんの少しだけ、ちらりと書きました。
LFは通常はCR(Carriage Return、コード0DH)とペアにして使われます。
CRが復帰でLFは改行(行送り)です。
CRは昔のプリンタで印字ヘッドを左端に戻す動作のことを言いました。
LFは用紙を1行送るという動作に使われました。

画面表示のためのコードとして使われるときも、プリンタと同じように、CRはカーソルを画面左端に移動させ(改行はしない)、LFで改行が行なわれます。

そういうことから、そして通常はCRLFというように、コード0DHとコード0AHを続けて使うことから、カーソルが左端ではなくて画面の途中位置にある場合には、そこでLF(0AH)を使うと、カーソルはそのままの位置で1行下に移動する、と理解していました。

ところが。
確かめてみますと、なんとLF(0AH)だけで(CRなしで)改行と復帰の動作を両方ともしてしまうということがわかりました。

下は[第127回]で使ったBSコードをテストするためのプログラムをLFのテストをするように書き直したものです。

// LFtest
//
#include <stdio.h>
//
void main()
{
	printf("abcxyz");
	printf("%c%c%cA\n",0x0a,0x0a,0x0a);
}

画面に’abcxyz’と表示したあと、0AH(LF)を3回出力し、それから’A’を表示します。
私が今まで考えていたLFの動作からすれば、

abcdef


      A

というように表示されるはずでした。

しかし、実行した結果は、下の画面にありますように、カーソルは画面の左端に移動してしまいました。



LF(0AH)は[↓]の動作とは違うということが確認できました。
それなら復帰と改行を共に行なうときに、画面に0D・0Aと続けて送らなくても、ただ0Aだけを送ればよいはずなのですが、それでも一般的にはそうする場合には0D・0Aと続けて送るのはなぜなのでしょうか?

うーん。
よくわかりませんけれど。
いろいろさぐってみましたら、どうもOSによって違いがあるらしいのですよねえ。
MacとWindowsでは動作が異なります、と書いてある記事を見かけました。
私はMacは使ったことがないのでわかりません。

まあ。しかし。
LFの振舞いはそういうことだと理解したうえで、しかし復帰改行を行なう場合には、今まで通りに、0D・0Aと続けて送るようにプログラムを書くほうが無難なように思います。

待てよ?
どこかで何かやってるんだけどなあ。
はて?
どこだっけ?
思い出せないなあ。

ここまで書いてきて、やっと思い出しました。
おお。
そうでした。
Cでは復帰改行をするときに、¥nを使ってるじゃありませんか。
上のリストでも使っていました!

で。
よくよく調べてみましたら、¥nの16進コードは0AHなんだそうです。
そうかあ。
Cでは、0D・0Aではなくて0Aを使っていたんだ。

ま、しかし。
やっぱりアセンブラで書くときは、古いしきたり通りに、0D・0Aと書くことにいたしましょう。
私はどうせ古い人間なのですから。

●CP/M互換DOSもファンクションコール0Aを直しました

作成作業中のCP/M互換DOSのファンクションコール0Aを、前回説明をしました通りの動作になるように、大幅に書き直してしまいました。
そのように書き直したCP/M互換DOSをND80ZVにインストールして、そこでFTST9.COMを実行して、前回と全く同じ操作をしてみました。

前回お見せしたCPM2.2でのFTST9.COMの実行画面は、画面と説明を対比して見られるように、画面を少しずつ切り分けてお見せしました。
実際は1枚の画像がもとになっています。
今回は前回と全く同じことを、CP/M互換DOSでも実行してみました。
その結果を前回のCP/M2.2の実行結果と一目で比較できるように、CP/M2.2での実行画面に互換DOSでの実行画面を重ねて1枚の画像にしました。
左がCP/M2.2で右が互換DOSです。



いかがでしょうか?
左右の表示結果は全く同じになっています。
コンソールバッファのダンプ表示で、左端の値がCP/M2.2では’FF’であるのに対して、互換DOSでは’14’になっているところだけが異なっています。
このことについては、次回に説明いたします。

このままではどちらがCP/M2.2でどちらが互換DOSだか、ちょっと見には見分けがつきませんでしょう。
ただよく見ますと、区別がつけられます。
CP/M2.2の場合にはプロンプトの表示が’a>’ですが、互換DOSでは’A>’と表示されています。
左右のDIRの表示結果からも、Aドライブの中味が異なっていることがわかります。

このくらい見事に一致すると気持ちがいいですね。
ちょいと自慢してしまいたい気分です。

どのようなプログラムを書いたのか、参考までにお見せすることにいたします。
こちらはZBDOSプログラムの、ファンクションコール01(コンソール入力)とファンクションコール02(コンソール出力)の部分です。

              ;CONSOLE INPUT FCALL 01
              ;if ctrl+D set zf
              ;
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 CA17C5   	JP Z,CONOUT2
C4F5 FE09     	CP 09;TAB
C4F7 CA25C5   	JP Z,CONOUT3
C4FA FE0D     	CP 0D;CR
C4FC CA41C5   	JP Z,CONOUT4
C4FF FE0A     	CP 0A;LF
C501 CA41C5   	JP Z,CONOUT4
C504 FE1B     	CP 1B
C506 DA4AC5   	JP C,CONOUT44;ctrl+
C509 4B       CONOUT12:LD C,E
C50A CD0CD2   	CALL B_CONOUT
C50D 2C       	INC L
C50E 7D       	LD A,L
C50F FE50     	CP 50
C511 CA45C5   	JP Z,CONOUT42
C514 C347C5   	JP CONOUT43
              ;BS
C517 7D       CONOUT2:LD A,L
C518 B7       	OR A
C519 CA4AC5   	JP Z,CONOUT44
C51C 4B       	LD C,E;BS
C51D CD0CD2   	CALL B_CONOUT
C520 2D       	DEC L
C521 7D       	LD A,L
C522 C347C5   	JP CONOUT43
              ;TAB
C525 CD2BC5   CONOUT3:CALL TABOUT
C528 C347C5   	JP CONOUT43
              ;
C52B 7D       TABOUT:LD A,L
C52C 2F       	CPL
C52D E607     	AND 07
C52F 3C       	INC A
C530 47       	LD B,A
C531 0E20     	LD C,20
C533 CD0CD2   TABOUT1:CALL B_CONOUT
C536 2C       	INC L
C537 05       	DEC B
C538 C233C5   	JP NZ,TABOUT1
C53B 7D       	LD A,L
C53C FE50     	CP 50
C53E C0       	RET NZ
C53F AF       	XOR A
C540 C9       	RET
              ;
              ;CR,LF
C541 4B       CONOUT4:LD C,E
C542 CD0CD2   	CALL B_CONOUT
C545 3E00     CONOUT42:LD A,00
C547 3264D0   CONOUT43:LD (CURSOR),A
C54A C1       CONOUT44:POP BC
C54B E1       	POP HL
C54C C9       	RET;

そしてこちらがファンクションコール0A(コンソールバッファ入力)です。

              ;CONSOLE BUFFER INPUT FCALL 0A
              ;if ctrl+D then BREAK
              ;
C576 2A64D0   CONBFIN:LD HL,(CURSOR)
C579 7D       	LD A,L
C57A 3268D0   	LD (CURSORWK),A
C57D 62       	LD H,D
C57E 6B       	LD L,E
C57F E5       CONBFIN1:PUSH HL
C580 4E       	LD C,(HL)
C581 0600     	LD B,00
C583 23       	INC HL
C584 E5       	PUSH HL
C585 23       	INC HL
C586 2266D0   	LD (CONBFINWK),HL
C589 CD09D2   	CALL B_CONIN
C58C E67F     	AND 7F
C58E FE04     	CP 04;ctrl+D
C590 C299C5   	JP NZ,CONBFIN22
C593 C336D2   	JP ZREENT
C596 CD09D2   CONBFIN2:CALL B_CONIN
C599 FE0D     CONBFIN22:CP 0D;CR
C59B CACCC5   	JP Z,CONBFIN4
C59E FE0A     	CP 0A;LF
C5A0 CACCC5   	JP Z,CONBFIN4
C5A3 FE09     	CP 09;TAB
C5A5 CAE5C5   	JP Z,CONBFIN3
C5A8 FE08     	CP 08;BS
C5AA CA07C6   	JP Z,CONBFIN5
C5AD FE12     	CP 12;^R
C5AF CA45C6   	JP Z,CONBFIN6
C5B2 FE15     	CP 15;^U
C5B4 CA9AC6   	JP Z,CONBFIN7
C5B7 FE18     	CP 18;^X
C5B9 CAB9C6   	JP Z,CONBFIN8
C5BC FE1B     	CP 1B
C5BE DACFC6   	JP C,CONBFIN9
C5C1 77       CONBFIN23:LD (HL),A
C5C2 5F       	LD E,A
C5C3 CDEAC4   	CALL CONOUT
C5C6 23       CONBFIN24:INC HL
C5C7 04       	INC B
C5C8 0D       	DEC C
C5C9 C296C5   	JP NZ,CONBFIN2
              ;CR,LF
C5CC E1       CONBFIN4:POP HL
C5CD 70       	LD (HL),B
C5CE 54       	LD D,H
C5CF 5D       	LD E,L
C5D0 EB       	EX DE,HL
C5D1 78       	LD A,B
C5D2 B7       	OR A
C5D3 CAE2C5   	JP Z,CONBFIN43
C5D6 23       CONBFIN41:INC HL
C5D7 7E       	LD A,(HL)
C5D8 B7       	OR A
C5D9 F2DEC5   	JP P,CONBFIN42
C5DC 3609     	LD (HL),09;TAB
C5DE 05       CONBFIN42:DEC B
C5DF C2D6C5   	JP NZ,CONBFIN41
C5E2 EB       CONBFIN43:EX DE,HL
C5E3 D1       	POP DE;dummy
C5E4 C9       	RET
              ;
              ;TAB
C5E5 E5       CONBFIN3:PUSH HL
C5E6 C5       	PUSH BC
C5E7 2A64D0   	LD HL,(CURSOR)
C5EA 7D       	LD A,L
C5EB 2F       	CPL
C5EC E607     	AND 07
C5EE 47       	LD B,A
C5EF 3C       	INC A
C5F0 4F       	LD C,A
C5F1 1E20     	LD E,20
C5F3 CDEAC4   CONINBF31:CALL CONOUT
C5F6 0D       	DEC C
C5F7 C2F3C5   	JP NZ,CONINBF31
C5FA 78       	LD A,B
C5FB 07       	RLCA
C5FC 07       	RLCA
C5FD 07       	RLCA
C5FE 07       	RLCA
C5FF F689     	OR 89;TAB
C601 C1       	POP BC
C602 E1       	POP HL
C603 77       	LD (HL),A
C604 C3C6C5   	JP CONBFIN24
              ;
              ;BS
C607 78       CONBFIN5:LD A,B
C608 B7       	OR A
C609 CA96C5   	JP Z,CONBFIN2
C60C 2B       	DEC HL
C60D 7E       	LD A,(HL)
C60E B7       	OR A
C60F FA22C6   	JP M,CONBFIN54;TAB
C612 FE20     	CP 20;ctrl check
C614 D21AC6   	JP NC,CONBFIN52
C617 CD38C6   	CALL BSOUT
C61A CD38C6   CONBFIN52:CALL BSOUT
C61D 05       CONBFIN53:DEC B
C61E 0C       	INC C
C61F C396C5   	JP CONBFIN2
C622 E5       CONBFIN54:PUSH HL
C623 C5       	PUSH BC
C624 0F       	RRCA
C625 0F       	RRCA
C626 0F       	RRCA
C627 0F       	RRCA
C628 E607     	AND 07
C62A 3C       	INC A
C62B 4F       	LD C,A
C62C CD38C6   CONBFIN55:CALL BSOUT
C62F 0D       	DEC C
C630 C22CC6   	JP NZ,CONBFIN55
C633 C1       	POP BC
C634 E1       	POP HL
C635 C31DC6   	JP CONBFIN53
              ;
C638 CD40C6   BSOUT:CALL BSOUT2
C63B 1E20     	LD E,20
C63D CDEAC4   	CALL CONOUT
C640 1E08     BSOUT2:LD E,08
C642 C3EAC4   	JP CONOUT
              ;
              ; ctrl+R
C645 1E23     CONBFIN6:LD E,23;#
C647 CDEAC4   	CALL CONOUT
C64A C5       	PUSH BC
C64B 3A68D0   	LD A,(CURSORWK)
C64E 4F       	LD C,A
C64F CD90C6   	CALL CRLF
C652 79       	LD A,C
C653 B7       	OR A
C654 CA60C6   	JP Z,CONBFIN62
C657 1E20     	LD E,20
C659 CDEAC4   CONBFIN61:CALL CONOUT
C65C 0D       	DEC C
C65D C259C6   	JP NZ,CONBFIN61
C660 2A66D0   CONBFIN62:LD HL,(CONBFINWK)
C663 7E       CONBFIN63:LD A,(HL)
C664 B7       	OR A
C665 FA80C6   	JP M,CONBFIN66;TAB
C668 FE20     	CP 20;ctrl check
C66A D273C6   	JP NC,CONBFIN64
C66D CDD5C6   	CALL CTRLDSP
C670 C377C6   	JP CONBFIN65
C673 5F       CONBFIN64:LD E,A
C674 CDEAC4   	CALL CONOUT
C677 23       CONBFIN65:INC HL
C678 05       	DEC B
C679 C263C6   	JP NZ,CONBFIN63
C67C C1       	POP BC
C67D C396C5   	JP CONBFIN2
C680 E5       CONBFIN66:PUSH HL
C681 C5       	PUSH BC
C682 2A64D0   	LD HL,(CURSOR)
C685 CD2BC5   	CALL TABOUT
C688 2264D0   	LD (CURSOR),HL
C68B C1       	POP BC
C68C E1       	POP HL
C68D C377C6   	JP CONBFIN65
              ;
C690 1E0D     CRLF:LD E,0D
C692 CDEAC4   	CALL CONOUT
C695 1E0A     	LD E,0A
C697 C3EAC4   	JP CONOUT
              ;
              ;ctrl+U
C69A 1E23     CONBFIN7:LD E,23;#
C69C CDEAC4   	CALL CONOUT
C69F 3A68D0   	LD A,(CURSORWK)
C6A2 4F       	LD C,A
C6A3 CD90C6   	CALL CRLF
C6A6 79       	LD A,C
C6A7 B7       	OR A
C6A8 CAB4C6   	JP Z,CONBFIN72
C6AB 1E20     CONBFIN71:LD E,20
C6AD CDEAC4   	CALL CONOUT
C6B0 0D       	DEC C
C6B1 C2ABC6   	JP NZ,CONBFIN71
C6B4 E1       CONBFIN72:POP HL
C6B5 E1       	POP HL
C6B6 C37FC5   	JP CONBFIN1
              ;
              ;ctrl+X
C6B9 2B       CONBFIN8:DEC HL
C6BA 7E       	LD A,(HL)
C6BB FE20     	CP 20;ctrl check
C6BD D2C3C6   	JP NC,CONBFIN82
C6C0 CD38C6   	CALL BSOUT
C6C3 CD38C6   CONBFIN82:CALL BSOUT
C6C6 05       	DEC B
C6C7 C2B9C6   	JP NZ,CONBFIN8
C6CA E1       	POP HL
C6CB E1       	POP HL
C6CC C37FC5   	JP CONBFIN1
              ;
              ;ctrl+
C6CF CDD5C6   CONBFIN9:CALL CTRLDSP
C6D2 C3C1C5   	JP CONBFIN23
C6D5 F5       CTRLDSP:PUSH AF
C6D6 1E5E     	LD E,5E;^
C6D8 CDEAC4   	CALL CONOUT
C6DB F1       	POP AF
C6DC F5       	PUSH AF
C6DD C640     	ADD A,40
C6DF 5F       	LD E,A
C6E0 CDEAC4   	CALL CONOUT
C6E3 F1       	POP AF
C6E4 C9       	RET

ZBDOSプログラムのもとのリストは[第119回]で全体のリストをお見せしましたが、そのときはまだファンクションコール01も02も、そして0Aもみんな簡単なプログラムでした。
それと今回のリストとを見比べていただければ、プログラムがいかに複雑なものに進化したかがご理解いただけることと思います。

今はちょっと時間がありませんから、それにまだまだデバッグ中ですから、このように参考程度にリストをお見せするだけですが、いずれ落ち着いて時間ができましたら、あらためて要点を解説するようにできたらいいな、と考えております。

実はこのCP/M互換DOSプログラムは、Z80アセンブラを使ってはおりますが、Z80固有の命令は使っておりません。
8080の命令だけで書いてあります。
このシステムは、MYCPU80にも移植する予定ですから、そのようにしておく必要があるのです。
Z80で追加された命令やレジスタを使えばもっと簡単になるのに、なぜそうしないの?と疑問に思われたかもしれませんが、そういう理由からでもあるのです。

ところで。
問題の[BS]でのTABの処理についてですが。
CP/M2.2では、正攻法といいますか正面突破の考え方でやっているようです(とにかく実にややこしいプログラムではっきり解読できませんが)。
BSでTABコードを削除して画面を表示させるときは、一旦その行の表示を全部クリアして、もう一度再表示する、ということをやっているようです(違っているかもしれません)。

私は全く別の方法を考えました。
TABコードを入力したときは、表示に必要な空白の個数を計算で求めるのですから、このとき求めた数値をなんとかTABコードと一緒に保存しておけないだろうか?と考えたことがもとになっています。
どのようにしているか、マシン語について相当の経験をお持ちの方でしたら、上のリストをじっくりお読みいただければ、ご理解いただけることと思います。
なお、CP/M2.2ではTABを入力したときに、空白の個数を算出する、という方法はとっていないようです。

同じ動きをするプログラムでも、それを書く人によって全く異なったプログラムになってしまうことが多いのではないでしょうか。
プログラムはそれを書く人の思考の軌跡であると思います。

え?
リストだけではよくわからないのだけど。
フローチャートはないのですか?

フローチャートねえ。
申し訳ありません。
そういうものは面倒くさいだけですから書きませんのです。

プログラムの入門書などですと、まず最初にフローチャートを書きなさい、などと書いてあるようですけれど。
それにフローチャートをしっかり書かないと、まともなプログラムは書けません、などと言う先生などもいらっしゃるようで。
ま。
好き好きですから。

ずっと昔、私がまだプログラムに慣れていなかったころは、フローチャートも書いたりしていたこともありましたけれど。
慣れてしまってからは、フローチャートなんかをちまちま書いているよりも、いきなりコーディングしてしまったほうが余程早くできてしまいます。
この程度のプログラムでしたら、メモもとりませんです。
フローなんぞは自然に頭の中にできております。
ですから、必要ならば要点だけをコメントしておけば十分です。
自分で考えて書いたプログラムですから、後でリストを見れば何をやっているかはわかります(そのくらいでなければプロとは言えませんでしょう)。

なんて、ちょっとかっこいいことを書いたりして、余裕の雰囲気でありましたが。
実は、ファンクションコール0Aは、これだけでは終わってくれなかったのであります。
まだ、続きがありました。

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

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