ポケットコンピュータPC-G850VSで平仮名を表示する

# 免責

このページのプログラムは2014年に作成されました。

当時私は転送インターフェイスを持っていなかったので、
手書きでノートに写していたものを書き起こしています。

という理由で、このページのすべてのプログラムの正確性は保証できません。
ソースをそのまま打ち込んで動くほうが奇跡だと思っていただければ幸いです。

2019/1/7 追記：フォントデータと一部プログラムの動作確認をとりました。

# 準備
## GPRINT 命令

PC-G850VSは片仮名の表示しか持っていませんので、
平仮名を表示するためにはグラフィックとして描画するしかありません。

`GPRINT` 命令を使えばそれが可能です。
`GPRINT` について詳しくは取扱説明書を参照ください。

## データファイル領域の確保

PC-G850内部のRAM DATA FILE領域にフォントデータを作成します。
[TEXT]-[Rfile]-[Init]で、N.Fとして662B分の容量を確保してください。

# フォントデザイナー

フォントデータがPCから直接転送できない場合、デザイナープログラムを使って
データを入力できます。

もちろんテキストファイルを作成して直接打ち込んでもかまいませんが、
以下の理由でおすすめしません。

+ デバッグが面倒
+ 行番号に続けて数字が打ち込めないのでスペースが必要
    + 追加の容量が必要

## プログラム

```BASIC:NFD.BAS(確認済)
10CLEAR:FN$="E:N.F":SV=1:DIM F$(55):OPEN FN$ FOR INPUT AS #2:INPUT #2,F$(*):CLOSE
20CLS:PRINT "#";STR$ CC;" (";CHR$(CC+166);") ";MID$(" *",SV,1):GCURSOR (0,24):GPRINT F$(CC)
30IV=ASC INKEY$:IF IV=MK THEN 30 ELSE MK=IV:IF IV THEN IV$=CHR$ IV ELSE 30
40SV=2:IF IV=23 AND F$(CC)>"" THEN F$(CC)=LEFT$(F$(CC),LEN F$(CC)-1) ELSE IF HEX$ VAL ("&H"+IV$)=IV$ THEN F$(CC)=F$(CC)+IV$ ELSE IF IV=42 OPEN FN$ FOR OUTPUT AS #2:PRINT #2,F$(*):CLOSE:SV=1 ELSE IF IV=43 THEN CC=CC+1 ELSE IF IV=45 THEN CC=CC-1
50GOTO 20
```

## 実行

10行は初期化ルーチンです。CLEAR後、`RUN`してください。

ファイルを確保した直後は`INPUT`に失敗してエラーで止まります。
この後は`GOTO 20`でフォントデータの打ち込みができるようになります。

[+][-]で文字を移動し、[*]で全データを保存します。

一度保存すると、`RUN`で前回までのデータを読んで再開します。

## フォントデータ

行番号はデザイナーに表示されるコードと一致します。

行番号と空行は直接転送する場合は必要ありません。削除してください。

```plaintext:N.F(確認済)
0 324E5B4A42
1 687C2840
2 3840000830
3 10545420
4 482A5A48

5 245E341264
6 087C082A14
7 1C483C0830
8 20507C2848
9 0848484830

10  0
11 324E3B0A72
12 3C4000023C
13 0845452518
14 4425354D44

15 327F0A4833
16 44340F443B
17 224B4E5A48
18 0814222140
19 3F40047F04

20 2051414142
21 2445465C44
22 3F40404030
23 025A3F0202
24 023F424F42

25 082D5B4908
26 720F224A48
27 424A4E4B32
28 014141413E
29 0119254341

30 314A444241
31 0A27523922
32 3F40214142
33 334C344876
34 247F0C6478

35 3C423E027C
36 7F00627F22
37 324F403E04
38 1841453A08
39 0804040810

40 7F00657F25
41 2A5A3F2A4A
42 30493F083C
43 1A3F424036
44 334C340876

45 0A3F4A4A20
46 047F04150A
47 1E443F0418
48 20503F2242
49 404C494A30

50 074840211E
51 086D6B4930
52 247F18047C
53 084D4B4930
54 247F184438

55 702C434020
```

# 表示ルーチン

## 座標の指定方法

このライブラリでは画面を1次元座標で管理しています。

|キャラクタ座標|グラフィック座標|1次元座標|
|:---:|:---:|:---:|
|(0, 0) |(0, 7) | 0 |
|(1, 0) |(6, 7) | 1 |
|(0, 1) |(0, 15)| 24 |

キャラクタ座標 `(x, y)` は、１次元座標では `x+y*24` となります。

## 文字列の指定方法

文字列中のカタカナをひらがなに変換します。カタカナ以外はそのまま出力されます。

途中に長音([SHIFT]+[カナ]) を置くと、変換のOn-Offを切り替えられます。
デフォルトはOnです。

長音は表示されません。フォントが同じマイナス記号を代用してください。

例: (長音の代わりに ~ で表現しています)

```
~ポケコン~デ ヒラガナヲ~ヒョウジ~デキマス.

ポケコンで ひらがなをヒョウジできます.
```

```
PC-G850ノ~ポテンシャル~ニ~チャレンジ~シマシタ.

PC-G850のポテンシャルにチャレンジしました.
```


## C言語の場合

以下のプログラム `nf.h` を作成しインクルードして使用します。
直接転送すると容量がえらいことになるので、1行化をおすすめします。

> 昔1行化のためのBASICプログラムを書いたのですが、とても ~~Buggy~~ 癖が強いので
> かなり ~~めんどくさい~~ 配慮が必要です。
> 転送インタフェースを持っている人なら不要でしょう。ここには置きません。

```C:nf.h
#ifndef MSTR
#define MSTR 256
#endif
char _f[56][11];
unsigned int _h=1;
init(){
	int *f=fopen("N.F","r"),c=0;
	for(;c<56;fscanf("%s¥n",_f[c++]));
	fclose(f);
}
int locate(int x){
	gotoxy(x%24,x/24);
	gcursor(x%24*6,x/24*8+7);
	return x;
}
int write(char *s,int x){
	int h=1;
	for(;*s!=0;s++){
		if(*s==-80){
			h=!h;
			continue;
		}else if(*s==10){
			x=x/24*24+24;
			continue;
		}
		locate(x);
		if(*s>-91&&*s<34&&h)
			gprint(_f[*s+90]);
		else
			putchar(*s);
		x+=_h;
	}
	_h=1;
	return x;
}
char *rev(char *s){
	int i=strlen(s);
	char r[MSTR]="";
	while(i>=0)
		r[--i]=*s++;
	_h=-1;
	return r;
}
int msg(char *s){
	line(3,48,141,26,0,65535,1);
	gotoxy(1,5);
	printf("                      ");
	if(*s==62)
		s=rev(++s);
	return write(s,_h<2?121:142);
}
int waitkey(int x){
        int n;
	char s[3]="_ ";
	while(!kbhit()){
		locate(x);
		putchar(s[(++n%160)/80]);
	}
	return x;
}
```

`#define MSTR 256`
読み込み元のプログラムで `MSTR` を`define`しておくことで、
処理対象の最大文字数を設定できます。
オーバーフロー時の挙動は実験したことがないのでわかりません。たぶん不定です。

`void init(void)`
引数: なし
戻り値: なし
ひらがな出力前に、1度だけコールする関数です。
この関数は、字形定義ファイルを読み込みメモリに展開します。
この関数を呼び出さなくても `write` `msg` 関数は実行できますが、ひらがなは正しく出力されません。

`int locate(int x)`
引数: 1次元座標`x`
戻り値: 与えられた`x`
表示位置を設定します。`write` `msg` 関数には影響しません。
ライブラリ内部で呼び出されています。

`int write(const char *s,int x)`
引数: 文字列`s`,1次元座標`x`
戻り値: 続けて出力されるべき位置の1次元座標
ひらがなを出力します。
文字列中に`\n`があれば改行します。

`char *rev(const char *s)`
引数: 文字列`s`
戻り値: 反転後の文字列へのポインタ
文字列を反転します。
もしかすると、`return`前に`strcpy`で`s`を上書きする方がいいかもしれない気がしています。

`int msg(const char *s)`
引数: 文字列`s`
戻り値: `write`関数の戻り値と同じ
画面下2行を使って、メッセージウィンドウを描画し、そこにひらがなを出力します。
`s`を`>`から始めると、右寄せで出力できます。
このとき長音記号の解釈は文字列の後ろから行われますので、注意が必要です。

`int waitkey(int x)`
引数: カーソルの1次元座標`x`
戻り値: 与えられた`x`
カーソルを点滅させ、ユーザのキー入力を待ちます。

## BASIC の場合

BASICではサブルーチンとして提供されます。
プログラム冒頭にルーチンを組み込んでください。

※このルーチンは旧版です。新しいものが見つかり次第更新します。

```BASIC:NF.BAS(確認済)
10RX=0:RY=1:DIM O$(0)*254,F$(55)*10:OPEN "E:N.F" FOR INPUT AS #2:INPUT #2,F$(*):CLOSE #2:GOTO 100
20*W:KS=-1:IF RY=1 LET LS=1:CT=LEN O$(0) ELSE CT=1:LS=LEN O$(0)
30FOR LS=LS TO CT STEP RY:IV=ASC MID$(O$(0),LS,1):IF IV=176 LET KS=NOT KS:GOTO 50 ELSE IF KS AND IV>165 AND IV<222 GCURSOR((RX MOD 24)*6,(RX¥24)*8+7):GPRINT F$(IV-166)+"00":RX=RX+RY:GOTO 50
40LOCATE RX MOD 24,RX¥24:PRINT CHR$ IV;:RX=RX+RY
50NEXT:RETURN
60*K:IV=ASC INKEY$:IF IV=MK THEN *K ELSE MK=IV:IF IV RETURN ELSE *K

100'THEN WRITE YOUR OWN PROGRAM FROM THIS LINE!
```

`RUN` 時に自動的にフォントデータを読み込みます。
読み込みが完了するまで1秒弱、画面が反応しません。
これが嫌なら、10行に手を加えてください。

メインプログラムは100行から書き始めます。

`GOSUB *W` : ひらがなを出力します。

+ `O$(0)`: 出力する文字列
+ `RX`: 出力開始位置 (1次元座標)
+ `RY`: 出力方向 (1: `→` or -1: `←`)
    + ちなみに 2 と指定すれば 1 文字おきに出力できます。

`GOSUB *K` : キー入力を待ちます。

+ `IV`: 押されたキーのキャラクタコードが格納されます。