このエントリーをはてなブックマークに追加
スポンサーリンク

漢字ROM

漢字ROM

PC-9801はDOS/Vと異なり漢字(全角文字)のフォントはROMで持っています。といいますか、漢字ROMがなくてファイルからフォントを読むようになったのは、パソコンのハードディスクやメモリーの容量が上がったDOS/V(DOS5.0/V)が登場して以降であり、それ以前のPCでは普通はフォントはROMで持ってました。

現在、Windowsで多国語表示が可能なのは、各国の言語のフォントを都度ファイルから読み込む事が可能なためですが、昔のPCはハードディスクは搭載されていないか、あっても容量が少ない(20Mから100Mぐらい)ので、フォントにそんなにディスク容量を食うわけにもいかなかったのでした。

おまけにPC-9801初期の頃は今のようにRAMを多く搭載しておらず、貴重なRAMをフォントのために使うわけにもいきませんでした。

普通の全角文字

普通の全角文字とは、つまり、外字や特殊記号以外の全角文字の事です。

7620H〜7FFFH 外字
2B21H〜2F7EH 特殊記号
この2つは読み出し方が普通の全角文字とは異なるので後ほど別に説明します。

まず、外字または特殊記号かどうかを判別します。
; 全角文字の場合

		MOV	AH,[MOJI1]
		MOV	AL,[MOJI2]
		MOV	BX,AX

;    外字なら半分ずつ出す
		CMP	BX,7620H
		JB	PUTK1_1
		CMP	BX,7FFFH
		JA	PUTK1_1
		JMP	GAIJI

;    特殊記号なら半分ずつ出す
PUTK1_1		=	$
		CMP	BX,2B21H
		JB	PUTK1_2
		CMP	BX,2F7EH
		JA	PUTK1_2
		JMP	GAIJI

PUTK1_2		=	$
外字または特殊記号ならばGAIJIというラベルのルーチンに行きます。(後述)

ここでは、普通の全角文字の処理、PUTK1_2について説明します。モードレジスタ(68H)にドットアクセスモードを指示します。

68H モードレジスタ
ビット7〜4 ビット3〜1 ビット0 意味
0000 000 1 簡易グラフ
0 バーティカルライン
0000 001 1 モノクロ
0 カラー
0000 010 1 40行
0 80行
0000 011 1 7x13
0 6x8
0000 100 1 200ライン
0 400ライン
0000 101 1 ドットアクセスモード
0 コードアクセスモード
0000 110 1 不揮発メモリ ライト不可
0 不揮発メモリ ライト可
0000 111 1 画面表示する
0 画面表示しない
モードレジスタは、ビット7〜4は常に0、ビット3〜1に設定するモード、ビット0に設定値を指定します。

たとえば、ドットアクセスモードにするためには、00001011B=1+2+8=11=0BHをセットします。コードアクセスモードにすると、走査線が画面にない時を狙って漢字ROMを読まないといけなくなります。その方が画面が一瞬乱れなくて良いのですが、タイミングを誤るとフリーズしてしまう危険があるので、ここではドットアクセスモードにしています。

次にフォントのどの部分を取得するかを指定します。

A5H ラインカウンタ
ビット 意味
7〜6 未使用(常に0)
5 1: 左半分
0: 右半分
4 未使用(常に0)
0〜3 上から何ライン目を読むか

次に、A1H、A3Hに漢字コードをセットします。
I/Oポート 意味
A1H JISコード下位バイト
A3H JISコードの上位バイトから20Hを引いたもの

EGC搭載機種ではない場合はI/Oポート(A9H)から読みます。I/Oポートから読む場合は8ビットずつしか読めないので、ラインカウンタ(A5H)に「上からn番目」「右」という風に指定して8ビットずつ読むわけですが、これは非常に低速です。
I/Oポート 意味
A9H READ: フォントパターン読み出し
WRITE: 外字登録

そこで、ここでは対応機種を「PC-9801UX/VX以降」という事にして、CGウィンドウから読み出します。OFFSET KANPATにフォントパターンを格納する32バイトが確保されているものとします。CGウィンドウ(A4000H〜A4FFFH)にフォントパターンがセットされるので、ここからフォントを読み出します。普通の全角文字の場合、「右半分」を指定すれば左右どちらもまとめて取得できます。なので、ここでは「右半分」を指定します。

先ほどのサンプルの続きです。BXレジスターに取得する漢字コード(JISコード)が入ってるものとします。
PUTK1_2		=	$
		MOV	AL,0BH
		OUT	68H,AL

		MOV	AL,00000000B
		OUT	0A5H,AL

		MOV	AL,BH
		SUB	AL,20H
		OUT	0A3H,AL
		MOV	AL,BL
		OUT	0A1H,AL

		PUSH	ES

		MOV	AX,0A400H
		MOV	ES,AX

		MOV	CX,16
		MOV	SI,0
		MOV	DI,OFFSET KANPAT
PUTK1B		=	$
		MOV	AX,ES:[SI]
		MOV	[DI],AX
		ADD	SI,2
		ADD	DI,2
		LOOP	PUTK1B

		POP	ES

		MOV	AL,0AH
		OUT	68H,AL
最後に68Hに0AHを入れてコードアクセスモードにします。ドットアクセスモードでは画面上の漢字がグラフィックキャラクターに化けてしまうので、ドットアクセスモードにするのは必要最小限の一瞬だけにしましょう。

外字、特殊記号

外字や特殊記号は、どういうわけかCGウィンドウに半分ずつしか現れてくれないので、右半分、左半分の2回に分けて読みます。
;--------外字の場合
GAIJI		=	$
		MOV	AL,0BH
		OUT	68H,AL

;        左
		MOV	AL,00100000B
		OUT	0A5H,AL

		MOV	AL,BH
		SUB	AL,20H
		OUT	0A3H,AL
		MOV	AL,BL
		OUT	0A1H,AL

		PUSH	ES
		MOV	AX,0A400H
		MOV	ES,AX
		MOV	CX,16
		MOV	SI,0
		MOV	DI,OFFSET KANPAT
GPUTK1B		=	$
		MOV	AL,ES:[SI+1]
		MOV	[DI],AL
		ADD	SI,2
		ADD	DI,2
		LOOP	GPUTK1B
		POP	ES

;        右
		MOV	AL,00000000B
		OUT	0A5H,AL

		MOV	AL,BH
		SUB	AL,20H
		OUT	0A3H,AL
		MOV	AL,BL
		OUT	0A1H,AL

		PUSH	ES
		MOV	AX,0A400H
		MOV	ES,AX
		MOV	CX,16
		MOV	SI,0
		MOV	DI,OFFSET KANPAT
GPUTK1C		=	$
		MOV	AL,ES:[SI+1]
		MOV	[DI+1],AL
		ADD	SI,2
		ADD	DI,2
		LOOP	GPUTK1C
		POP	ES

		MOV	AL,0AH
		OUT	68H,AL

外字登録

外字を登録しておくと、テキスト画面に外字が表示されます。

ATOKの外字(JFGAIJ.UFO)を使っている場合、外字は自前のルーチンで登録しなくても、一瞬ATOKをオンにしてからオフにすれば自動的にATOKの方で登録してくれます。PC-9801用のATOK8は、J-3100用と同じAPIを使う事が出来ます。詳しくはATOKのAPIを参考にしてください。この場合、画面右下に一瞬だけ「あ連R漢」と出てしまうのがカッコ悪いのですが、プログラムの頭に一瞬だけ出すようにすれば大丈夫でしょう。エンドユーザーもあまり気にしないでいただけるとありがたいです。

え?無理?気になる?右下に一瞬だけ「あ連R漢」と出るのは許せない?というか、そもそもATOKを使ってない?という時は仕方なく自前ルーチンで外字パターンを登録します。

ちなみに、PC-9801用のATOK7を、MS-DOS6.2で動かすと、外字登録時(つまり、初回ATOK起動時)にフリーズする場合があります。おそらくコードアクセスモードで登録を行っていて、VSYNCのタイミングがうまく取れてないのだと思います。そのため、PC-9801用のATOK8は画面が一瞬乱れるのを覚悟で、ドットアクセスモードで外字登録を行っているようです。

このようにコードアクセスモードにすると、走査線が画面にない時を狙って外字登録をしないと、ATOK7みたいに時折フリーズするようになってしまいます。しかし、そのタイミングを計るのは非常に難しく、ATOK7ですらフリーズさせてしまうほどです。そのため、ここではドットアクセスモードで行いましょう。

A5H ラインカウンタ
ビット 意味
7〜6 未使用(常に0)
5 1: 左半分
0: 右半分
4 未使用(常に0)
0〜3 上から何ライン目を登録するか

次に、A1H、A3Hに漢字コードをセットします。
I/Oポート 意味
A1H JISコード下位バイト
A3H JISコードの上位バイトから20Hを引いたもの

I/Oポートを使って外字登録する場合、ラインカウンタ(A5H)に右半分・左半分の区別、上から何ライン目かを指定し、A1HにJISコードの下位バイト、A3HにJISコードの上位バイトから20Hを引いたものをセットして、A9Hにフォントパターンを1バイトWriteします。

I/Oポート 意味
A9H READ: フォントパターン読み出し
WRITE: 外字登録

この場合、1バイトずつラインカウンタをセットしてはA9HにWriteしなければならず面倒です。

そこで、BIOSを使って外字登録することもできます。BX:CXレジスタにフォントパターンのアドレス(セグメント:BX、オフセット:CX)、DXレジスタにJISコード、AHレジスタに1AHを入れてINT 18Hを呼びます。ただし、フォントパターンの最初の2バイトはワークエリアとして内部的に使いますので、先頭2バイトには0000Hを入れておいて、その次からの32バイトにフォントパターンを置きます。つまり、フォントデーターは34バイト必要です。

AH=1AH INT 18H 外字登録
レジスタ 意味
AH 1AH
BX:CX フォントパターンのアドレス
 +0+1 0000Hをセット
 +2〜+33 外字フォントパターン(32バイト)
DX 登録するJISコード(7621H〜767EH、7721H〜777EH)

終わったら、最後に68Hに0AHを入れてコードアクセスモードにします。ドットアクセスモードでは画面上の漢字がグラフィックキャラクターに化けてしまうので、ドットアクセスモードにするのは必要最小限の一瞬だけにしましょう。

半角文字

半角文字は、A3Hにアスキーコード、A1Hに00Hを入れて、A5Hに右半分を指定します。CGウィンドウには右半分の部分にフォントが現れます。ただし、ここで注意しなければならないのは、ANKのフォントはドットアクセスモードでは読めないという事です。

つまり、ANKのフォントコードアクセスにして、走査線が画面にない時を狙って読み出さなければならないという事です。具体的には、I/Oポートの60Hを読んでビット5が0のタイミングでしか読めないという事です。これは非常に低速です。1文字や2文字ならそれでも良いのですが、全ての半角フォントを読みたい場合、その間にユーザーを待たせてしまう事になってしまいます。そんな事をしたらユーザーから「フリーズしてしまった」「バグではないのか?」などなどクレームが山のように来る事間違いなしです。

そこで、半角文字フォントは別途吸い出しプログラムを作っておき、イントール時に吸い出してファイルとして取っておいて、使う時はそのプログラムの先頭でファイルから読んでメモリーに入れておく方が良いでしょう。さいわい半角フォントは数が少ないので、全てのフォントをメモリーに読み込んでもコンベンショナルメモリーが溢れる心配はないはずです。

え?いちいちインストール時にフォントを吸い出すルーチンを走らせるのが面倒?フロッピーベースで使えない?インストール時にその作業を忘れたらどうするって?そ、それは・・・

終わりに

PC-9801でプログラムを作っていると、漢字ROMへのアクセス1つにしても、PC/AT互換機やJ-3100に比べるとあまりに複雑で、プログラムするのがだんだんと嫌になってきます。一度PC/AT互換機でプログラムを作った人は、もうPC-9801には戻れないでしょう。(もっとも私の場合、PC/AT互換機が先だったわけですが。)

しかし、PC-9801自体がもともとビジネス向けに作られた機種なので、アプリ層からアセンブラでV-RAMやテキストV-RAM、漢字ROMに直接アクセスする事は想定になかったのかもしれません。これは、同じくビジネス向けに設計されたFM-7が、V-RAMへのアクセスが複雑だったり、キースキャンがコントローラーを介さないとできずリアルタイムキースキャンができないのと似ていますね。

参考文献
『PC‐9801スーパーテクニック』 1992年 小高輝真、清水和文、速水祐 著 アスキー
『J-3100解析ハンドブック』 1989年 土屋勝著 ナツメ社
『DOS/Vテクニカル・リファレンス・マニュアル』 1993年 芦達剛著 ソフトバンク
スポンサーリンク