NEC PC-9800シリーズの特徴のひとつに、漢字表示をハードウェアによって高速に表示できるという点がありました。そのフォントパターンを格納したROMが漢字ROMです。この記事で何を述べたいかというと、シフトJISで格納された文字列をVRAMに書きこむというただそれだけのことです。ぐぐってもまとめておられる方を見つけられませんでしたので(^^;。誰が書いても良いんでしょうけれども、自分の頭の整理も兼ねてしました。また、PC-9801初代などの発売時のことは良く知らないので、時代考証(汗)は間違っている可能性があります。ISO2022-JPは98発売より随分後に規格化されたので、本来は時代が前後するわけですが、現状ではこちらを比較対象にしたほうが理解しやすいので載せてあります。
日本語の文字コードというと、シフトJIS、EUC、Unicode、JIS、などという種類を耳にしたことがあるかと思います。普段WindowsやDOS等で漢字混じりのテキストファイルを保存すると、Shist-JISコードで格納されます。幾分高機能なテキストエディタ(私はTERAPADを愛用しております。)では、文字コードの種類を選ぶことも出来ます。なお、このHTML文書はShift-JISで書かれております。昔EUCで保存したことがあるんですが、テキストブラウザとして有名なLynxでは化ける部分があったからです。また、携帯電話などの環境ではShift-JISの必要があります。さて、この記事ではShift-JISで保存された文字列をテキストVRAMに書きこんで表示するというのが題目なので、それ以外の文字コードには深入りしません(自分でもわからないので)。
例えば「猫」という漢字を保存して、バイナリエディタで見てみます。すると、"94 4C"と言うバイト列で(ワード列でなく)保存されているのが分かります。これをJISで保存してみます。"1B 24 42 47 2D 1B 28 42"と増えたのが見てとれるかと思います。BASICの時代から使われている方はご存知でしょうが、kanji-in(KI)/kanji-out(KO)に相当するコード(KI/KOそのものではない)がついているわけです。
現在ではJISコードで保存という言葉はJIS X 213という文字集合に従って面区点を指定してISO-2022-JPという符号化方式に従って文字をあらわすコードを確定するという意味になります。元々はJISという名前を関した規格はJIS X 208という規格があり、これは94x94のマス目の箱をひとつ用意して、その中に何らかのポリシーの元に漢字を詰め込んでいくという規格です。その箱の中には全角カタカナ、全角ひらがな、第1水準漢字、第2水準漢字などが格納されています。「猫」という漢字は10進数で(39,13),16進数で(27h,0Dh)という座標で表示できます((1,1)から開始します。)。これを39区13点と表現します。
なお、第3、第4水準漢字などは上記の1個の箱では入りきらないわけです。これを改善し、Shift-JISコードにも考慮したJIS X 0208を拡張した規格であるJIS X 0213(2000年策定)では、箱をいくつも用意することにしました。それぞれの箱を「面」と呼びその中に区と点があるという構造です。まあ、98では気にしなくていいのですが。
さて、JIS X 208をASCIIコードと両立させたいということになります。これに都合のよいように、ASCIIコードでは制御コードとされる00hから20hまでを避けて21hから7Ehまでにその箱が見える「窓」を設け、エスケープシーケンスにより窓の向こうに見える面を選択する規格がISO-2022-JPになるわけですです。"1B 24 42"が第一水準文字の存在する面が出てくるようにしろという指定になります。
これまでの流れを術語でいうと、「猫」という漢字をJIS X 0208という文字集合から39区13点に確定し、ISO-2022-JPという符号化方式では"47 2D"というコードを割り当て、エスケープシーケンスをその前後につけて"1B 24 42 47 2D 1B 28 42"にしたといいます。また、「猫」という漢字を39区13点に確定した後にShift-JISという符号化方式により、"94 4C"というコードが割り当てられたということになります。
さて、上のJISコードでは確かに窓を切り替えて沢山の文字を指定することができます。しかし、当時の現実問題としては、文字の切り替えのたびにkanji-in(KI)/kanji-out(KO)をつけるのは不経済でありました。また、副次的にはKI/KOが化けるとその後ずっと化け化け状態になるという困ったことも起きます。それを克服したのがMS-DOS以来で採用されたShift-JISコードになります。噛み砕いて言うと制御コードと英数字と削除コードと半角カタカナを除いたところに漢字の1バイト目をkanji-in(KI)の意味も含めて無理やり押し込んでしまうというものです。これにより、従来の半角文字との共存ができることになります。余談ですが私はこれを見ると国道と市道の交差点を跨いで1つのビルを建てるというような想像をしてしまいます。(^^;
まず第1バイト目は上述のように制御コードと英数字と削除コードと半角カタカナを避ける関係上余り大きく取れないから、94通りもの値の指定はできずに半分の47通りの指定のみします。次に1バイト目で指定し損ねた分を2バイト目で補います。2バイト目では英数字とカタカナは避けません。すると94x2以上の値を指定できます。その方法を踏まえて、「区/2」を81h〜9Fhとカタカナを飛ばしてE0h〜EFhの計47個のアドレスに割り当てます。2バイト目は「区%2」が0、つまり偶数区のときには40h〜7Ehと削除コードを飛ばして80h〜9Ehの計94個のアドレスに、「区%2」が1、つまり奇数区のときには9Fh〜FChの94個のアドレスに点の小さいほうが小さいアドレスになるように割り当てます。
テキストVRAMはISO-2022-JPと比べると分かりやすいですが、完全にそのコードを書けばそれに応じたフォントパターンが返されるというわけではないです。また、エスケープシーケンスは不要です。PC-9800シリーズではセグメントA000h-A200hがテキストVRAMでセグメントA200h-A400hがアトリビュートエリアになっていますね。
繰り返しますが、「猫」のISO-2022-JPでのエスケープシーケンスを除いたコードは"47h 2Dh"です。下位バイトから20hを引きますと、「猫」だと"27h 2Dh"になります。まずこれを1バイト目が区で下位バイト、2バイト目が点で上位バイトになるようにし、ワード単位またはバイト単位でテキストVRAMに書きこみます。その直後のテキストVRAMの2バイトには2バイト目のBIT7を立てて同じ物を書きこみます。「猫」だと"27h ADh"になりますね。
まあ、世の中もっと効率的なコードを書いている人もおられるようですが、私が書いたのは次のようなコードです。いちいちこんなコードに他人の利用制限をつけるのもあほらしいので、使いたいという方は勝手に使ってくださって結構です。連絡もいりません。ただ、心無い人に排他的な利用を目的とした著作権を主張されると困りますので、著作権は主張しておきます。計20バイトのコードになります。
;Shift-JISコードの1バイト目、つまり区/2がAL、 ;2バイト目、つまり区%2と点がAHに格納されている状態 INC AH JNS SK1 DEC AH SK1: ADD AH,61h;[9Fh,FCh]->[00h,5Dh] [41h,9Eh]->[A2h,FFh] JC SK2 SUB AH,0A2h;[A2h,FFh]->[00h,5Dh] SK2: RCL AL,01h;[81h,9Fh]->[02h,3Eh] [E0h,EFh]->[C0h,DEh] AND AL,7Fh;[02h,3Eh]->[02h,3Eh] [C0h,DEh]->[40h,5Eh](連結!) ; ADD AX,211Fh ;Shift-JIS->ISO2022-JP変換済み ; SUB AL,20h;VRAM書きこみのための措置 ADD AX,20FFh;上のコメントアウトした行を合わせた ; STOSW等と続く・・・当たり前ですがDWORDアクセス禁止 ; OR AH,80h ; STOSW