外字&アイコンとデータ書き込みプログラム

 考えてみれば、クローンデータの書き換えは、大きなリスクを伴う実験である。

 一般に、パソコンプログラムの"改造"などは、仮に不具合があっても、パソコンがフリーズするだけで済む。けれども、クローンデータの書き換えは、5Wもの出力を有する無線機を制御するプログラムの"改造"であり、ひとつ間違えば、ファイナルトランジスタをはじめとするハードウエア(デバイス)の破壊に直結する。

 VX-7は安くはないから、無線機としての核心(?)にあたる周波数データの書き換えには、慎重を期さねばならないことは言うまでもない。

 そこで、まずは無線機の本質(?)とは無関係な外字データの書き換えを試みることにした。

外字データ

 VX-7では、外字5文字を使用することができる。外字1文字は、タテ11dot × ヨコ10dotで構成され、セットモードのフォントエディタで編集可能である。この外字データも、もちろんクローンデータ内に存在しており、外字1文字あたり22byteが割り当てられている。
 VX-7のフォントエディタの操作性は最悪なので、クローンデータの書き換えによって外字を定義してみることにした。

 外字データの場所(オフセット)は、次のとおりである。

[0754] - [0769] : 外字1(C1)
[076A] - [077F] : 外字2(C2)
[0780] - [0795] : 外字3(C3)
[0796] - [07AB] : 外字4(C4)
[07AC] - [07D7] : 外字5(C5)


fig 1 外字データとビットマップの対応

 外字データとビットマップの対応は、fig 1に示すとおりである。
 fig 1は、外字1の例であるが、フォントの左上隅が[0754]のbit1、右下隅が[0754]のbit0であり、"1"でLCDのdotが点灯(黒ドット表示)、"0"で消灯(ドット表示なし)である。フォントとして有効なのはクリーム色のアミがかかった部分のみで、それ以外は"0"で埋められる。

アイコンデータ

 VX-7では、オリジナルのアイコンを3種類を使用することができる。ひとつのアイコンは、タテ41dot × ヨコ21dotで構成されており、クローンデータとしては123byteになる。

 オリジナルのアイコンデータの場所(オフセット)は、次のとおりである。奇数オフセットからデータを配置するのは具合が悪いのか、アイコン1と2、2と3のデータの間には、1byteぶんの"空き地"があることに注意して欲しい。

[0852] - [08CC] : アイコン1(I1)
[08CE] - [0948] : アイコン2(I2)
[094A] - [09C4] : アイコン3(I3)


fig 2 アイコンデータとビットマップの対応

 アイコンデータとビットマップの対応は、fig 2に示すとおりである(アイコン1の例)。
 

外字&アイコン書き換え手順

クローンデータSAVE

 "SAVEVX7C.BAS"を使って、VX-7からクローンデータをパソコンに取り込む。

 なお、私のVX-7は、MAINバンド/SUBバンド同時受信の状態で電源を切り、クローン機能("送り側")を使うと、マニュアルの記載に反して、データを送出し終えたあと、受信状態に戻ってしまう。マニュアルに反した挙動を示すというのは、これは一種のバグである。よって、クローン機能を使う際には、あらかじめモノバンド受信の状態で電源を切る必要があるのだが、あなたのVX-7ではいかがであろうか。

クローンデータの書き換え

 "SAVEVX7C.BAS"で生成されるファイルは、形式的にはテキストファイルなので、適当なエディタを使って書き換え可能である。

 私は、快適なGUIを持つ外字/アイコン編集プログラムを作る術は知らないので、方眼紙を使って外字/アイコンの構成を立案し、手作業で16進データに変換した。VX-7のフォントエディタ・アイコンエディタ劣るとも勝らない(!?)効率の悪さであるが、MS-DOS全盛の頃は、皆、こうやってゲームのキャラクタを作ったものである。

 とにかく、昔を偲びながらデータを書き換える。あくまでも実験用なので、クローンデータファイルは、16進表記に使えるのは0〜9とA〜F(小文字は不可)だけ、1行9文字(CR・LFを除く)に限るという窮屈な仕様になっているので了解願いたい。

 上記の外字/アイコンデータ以外の部分を書き換えることも可能で、実はそうすることによってクローンデータの構成を知るうえで貴重な情報を得られることも多いのだが、仮にそのような冒険をしてVX-7が壊れても筆者は一切責任を負わないので、念のため。

クローンデータLOAD

 次はいよいよ書き換えたデータをVX-7に書き込む。

 しかし、外字/フォントのデータを書き換えただけでは、書き込む際にエラーが発生する。なぜなら、一連のVX-7クローンデータの末尾1byteはチェックサムになっているからである。

 "LOADVX7C.BAS"では、この点を考慮し、正しいチェックサムを生成するように工夫してある。

 データLOAD(書き込み)が成功すると、VX-7は、電源投入時と同じグラフィック(アニメーション)を表示して受信状態に戻る。セットモードのフォントエディタ・アイコンエディタを使って、意図したとおりの外字/アイコンが設定されていることを確認するとよい。

10000 '**********************************************************
10010 '
10020 'VERTEX STANDARD VX-7
10030 'クローンデータ書き込みプログラム Ver3.00 (Oct 6 2002)
10040 '
10050 'Copyright by Heian Software Engineering
10060 '(C)2002 H.S.E. Allrights reserved.
10070 '
10080 '**********************************************************
10090 '
10100 '
10110 '
10120 *START
10130 CLEAR
10140 DEFINT A-Z
10150 WIDTH 80,25:CONSOLE 0,25,1,1:COLOR 7,0,0,7,2:CLS 2
10160 '
10170 D.MAX=&H3FFF
10180 RX.BUFF.SIZE=320:'受信バッファサイズ
10190 '
10200 KEY 2,"SYSTEM"+CHR$(&HD)
10210 '
10220 VERSION$="LOADVX7C.BAS VER 3.00"
10230 '
10240 COMPORT.NAME$="COM1:N81NN":'通信ポートファイルディスクリプタ
10250 COMPORT.SPEED=8:'通信速度 8:19200,0:SPEEDコマンドによる
10260 G.SEND.DATA.TO.TRX.W=1000:'送信時間調整
10270 '
10280 GOTO *MAIN
10290 '
10300 '
10310 *READ.HEX.CHR
10320 DIM HEX.CHR$(255)
10330 FOR READ.HEX.CHR.W=0 TO 255:HEX.CHR$(READ.HEX.CHR.W)=RIGHT$("0"+HEX$(READ.HEX.CHR.W),2):NEXT
10340 RETURN
10350 '
10360 *READ.ERROR.MES
10370 DIM ERROR.MES$(100)
10380 RESTORE *READ.ERROR.MES
10390 *READ.ERROR.MES.1000
10400 READ ERROR.MES.W
10410 IF ERROR.MES.W<0 THEN RETURN
10420 READ ERROR.MES$(ERROR.MES.W)
10430 GOTO *READ.ERROR.MES.1000
10440 DATA 0,"--- 正常終了(000) ---"
10450 DATA 60,"*** 入力データが不足しています。(060) ***"
10460 DATA 61,"*** 入力データ構造に誤りがあります。(061) ***"
10470 DATA 80,"*** 無線機との通信で異常が発生しました。(080) ***"
10480 DATA 81,"*** 応答信号が不正です。(081) ***"
10490 DATA 90,"*** 強制終了(090) ***"
10500 DATA -1,""
10510 '
10520 *RX.BUFF.INIT
10530 DIM RX.BUFF(RX.BUFF.SIZE-1)
10540 GOSUB *RX.BUFF.CLEAR
10550 RETURN
10560 '
10570 *RX.BUFF.CLEAR
10580 G.RX.BUFF.RP=0:'読み出しポインタ
10590 G.RX.BUFF.WP=0:'書き込みポインタ
10600 G.RX.BUFF.N=0 :'バッファ内データ数
10610 G.RX.BUFF.OV=0:'バッファオーバーフロー
10620 G.RX.BUFF.EM=0:'バッファ内エンプティ
10630 RETURN
10640 '
10650 *RX.BUFF.WRITE
10660 IF G.RX.BUFF.N>=RX.BUFF.SIZE THEN G.RX.BUFF.OV=-1:RETURN
10670 RX.BUFF(G.RX.BUFF.WP)=G.RX.WRITE.DATA
10680 G.RX.BUFF.WP=(G.RX.BUFF.WP+1) MOD RX.BUFF.SIZE
10690 G.RX.BUFF.N=G.RX.BUFF.N+1
10700 G.RX.BUFF.OV=0
10710 RETURN
10720 '
10730 *RX.BUFF.READ
10740 IF G.RX.BUFF.N=0 THEN G.RX.BUFF.EM=-1:RETURN
10750 G.RX.READ.DATA=RX.BUFF(G.RX.BUFF.RP)
10760 G.RX.BUFF.RP=(G.RX.BUFF.RP+1) MOD RX.BUFF.SIZE
10770 G.RX.BUFF.N=G.RX.BUFF.N-1
10780 G.RX.BUFF.EM=0
10790 RETURN
10800 '
10810 '
10820 *INIT.DATA.TABLE
10830 DIM D(D.MAX)
10840 FOR INIT.DATA.TABLE.W=0 TO D.MAX:D(INIT.DATA.TABLE.W)=-1:NEXT
10850 DEF FNADRS.D$(A)=HEX.CHR$(A \ 256)+HEX.CHR$(A MOD 256)+" : "+HEX.CHR$(D(A))
10860 RETURN
10870 '
10880 *READ.DATA.TABLE
10890 READ.DATA.TABLE.N=&H3F52
10900 READ.DATA.TABLE.NC=0
10910 *READ.DATA.TABLE.0000
10920 IF EOF(2) THEN READ.DATA.TABLE.ERR=60:RETURN
10930 LINE INPUT #2,READ.DATA.TABLE.I$
10940 IF READ.DATA.TABLE.I$="" THEN GOTO *READ.DATA.TABLE.0000
10950 IF LEFT$(READ.DATA.TABLE.I$,1)="'" THEN GOTO *READ.DATA.TABLE.0000
10960 IF LEN(READ.DATA.TABLE.I$)<>9 THEN READ.DATA.TABLE.ERR=61:RETURN
10970 READ.DATA.TABLE.IH$=MID$(READ.DATA.TABLE.I$,1,2)
10980 READ.DATA.TABLE.IL$=MID$(READ.DATA.TABLE.I$,3,2)
10990 READ.DATA.TABLE.ID$=MID$(READ.DATA.TABLE.I$,8,2)
11000 IF HEX.CHR$(VAL("&H"+READ.DATA.TABLE.IH$))<>READ.DATA.TABLE.IH$ THEN READ.DATA.TABLE.ERR=61:RETURN
11010 IF HEX.CHR$(VAL("&H"+READ.DATA.TABLE.IL$))<>READ.DATA.TABLE.IL$ THEN READ.DATA.TABLE.ERR=61:RETURN
11020 IF HEX.CHR$(VAL("&H"+READ.DATA.TABLE.ID$))<>READ.DATA.TABLE.ID$ THEN READ.DATA.TABLE.ERR=61:RETURN
11030 READ.DATA.TABLE.AD=VAL("&H"+READ.DATA.TABLE.IH$)*256+VAL("&H"+READ.DATA.TABLE.IL$)
11040 IF READ.DATA.TABLE.AD>READ.DATA.TABLE.N THEN READ.DATA.TABLE.ERR=61:RETURN17300 IF READ.DATA.TABLE.AD<0 THEN READ.DATA.TABLE.ERR=61:RETURN
11050 D(READ.DATA.TABLE.AD)=VAL("&H"+READ.DATA.TABLE.ID$)
11060 READ.DATA.TABLE.NC=READ.DATA.TABLE.NC+1
11070 '
11080 IF READ.DATA.TABLE.NC MOD 256=0 THEN PRINT "*";
11090 IF READ.DATA.TABLE.NC<=READ.DATA.TABLE.N THEN GOTO *READ.DATA.TABLE.0000
11100 READ.DATA.TABLE.ERR=0
11110 FOR READ.DATA.TABLE.W=0 TO READ.DATA.TABLE.N
11120 IF D(READ.DATA.TABLE.W)<0 THEN READ.DATA.TABLE.ERR=60
11130 NEXT
11140 RETURN
11150 '
11160 '
11170 *COMPORT.INIT
11180 OPEN COMPORT.NAME$ AS #1
11190 IF COMPORT.SPEED=0 THEN GOTO *COMPORT.INIT.1000
11200 OUT &H75,COMPORT.SPEED MOD 256
11210 OUT &H75,COMPORT.SPEED \ 256
11220 *COMPORT.INIT.1000
11230 G.COMPORT.ERROR=0
11240 GOSUB *RX.BUFF.INIT
11250 ON COM GOSUB *COMPORT.INT
11260 RETURN
11270 '
11280 *COMPORT.INT
11290 *COMPORT.INT.1000
11300 IF LOC(1)=0 THEN RETURN
11310 G.RX.WRITE.DATA=ASC(INPUT$(1,1))
11320 GOSUB *RX.BUFF.WRITE
11330 IF G.COMPORT.ERROR=0 THEN G.COMPORT.ERROR=G.RX.BUFF.OV
11340 GOTO *COMPORT.INT.1000
11350 '
11360 *SEND.DATA.TO.TRX
11370 PRINT #1,CHR$(G.SEND.DATA.TO.TRX);
11380 FOR SEND.DATA.TO.TRX.W=0 TO G.SEND.DATA.TO.TRX.W:NEXT
11390 RETURN
11400 '
11410 *SEND.BLOCK
11420 IF SEND.BLOCK.N=0 THEN SEND.BLOCK.ERR=0:RETURN
11430 G.SEND.DATA.TO.TRX=D(OFFSET.ADRS):GOSUB *SEND.DATA.TO.TRX
11440 *SEND.BLOCK.0550
11450 COM STOP:GOSUB *RX.BUFF.READ:COM ON
11460 IF G.RX.BUFF.EM=-1 THEN GOTO *SEND.BLOCK.0550
11470 IF G.RX.READ.DATA<>D(OFFSET.ADRS) THEN SEND.BLOCK.ERR=80:RETURN
11480 PRINT FNADRS.D$(OFFSET.ADRS)
11490 OFFSET.ADRS=OFFSET.ADRS+1:SEND.BLOCK.N=SEND.BLOCK.N-1
11500 GOTO *SEND.BLOCK
11510 '
11520 *REC.ACK
11530 COM STOP:GOSUB *RX.BUFF.READ:COM ON
11540 IF G.RX.BUFF.EM=-1 THEN GOTO *REC.ACK
11550 IF G.RX.READ.DATA<>G.ACK THEN REC.ACK.ERR=81:RETURN
11560 REC.ACK.ERR=0:RETURN
11570 '
11580 '
11590 *STOP.INIT
11600 ON STOP GOSUB *STOP.INT:STOP ON
11610 RETURN
11620 '
11630 *STOP.INT
11640 ERROR.CODE=90:GOTO *FINISH
11650 '
11660 '
11670 *WAIT.TYPE.COMMAND
11680 IF INKEY$<>"" THEN GOTO *WAIT.TYPE.COMMAND
11690 *WAIT.TYPE.COMMAND.1000
11700 WAIT.TYPE.COMMAND.C$=INKEY$
11710 IF WAIT.TYPE.COMMAND.C$="" THEN GOTO *WAIT.TYPE.COMMAND.1000
11720 IF INSTR(WAIT.TYPE.COMMAND.S$,WAIT.TYPE.COMMAND.C$)<>0 THEN RETURN
11730 GOTO *WAIT.TYPE.COMMAND.1000
11740 '
11750 '
11760 *FINISH
11770 IF ERROR.CODE<>0 THEN COLOR 2 ELSE COLOR 7
11780 PRINT
11790 PRINT ERROR.MES$(ERROR.CODE)
11800 CLOSE
11810 COLOR 7
11820 END
11830 '
11840 '
11850 *MAIN
11860 GOSUB *READ.HEX.CHR
11870 GOSUB *READ.ERROR.MES
11880 GOSUB *INIT.DATA.TABLE
11890 GOSUB *STOP.INIT
11900 INPUT "入力ファイル:",INFILE$:PRINT
11910 OPEN INFILE$ FOR INPUT AS #2
11920 PRINT "データを読み込んでいます。":PRINT
11930 GOSUB *READ.DATA.TABLE:PRINT:PRINT
11940 CLOSE #2
11950 IF READ.DATA.TABLE.ERR<>0 THEN ERROR.CODE=READ.DATA.TABLE.ERR:GOTO *FINISH
11960 PRINT "チェックサムを検証します。":PRINT
11970 CS=0
11980 FOR W=0 TO &H3F51:CS=(CS+D(W)) MOD 256:NEXT
11990 IF CS=D(&H3F52) THEN PRINT "チェックサムは正当です。":PRINT:GOTO *MAIN.2000
12000 PRINT "チェックサムに誤りがあります。"
12010 PRINT "入力ファイルに記録されたチェックサムは "+HEX.CHR$(D(&H3F52))+" ですが、正しくは "+HEX.CHR$(CS)+" なので、修正しました。":PRINT
12020 D(&H3F52)=CS
12030 PRINT "チェックサムを修正したデータをファイルとして保存しますか? (Y/N)":PRINT
12040 WAIT.TYPE.COMMAND.S$="YNyn":GOSUB *WAIT.TYPE.COMMAND
12050 IF (WAIT.TYPE.COMMAND.C$="N") OR (WAIT.TYPE.COMMAND.C$="n") THEN GOTO *MAIN.2000
12060 INPUT "出力ファイル:",OUTFILE$:PRINT
12070 LINE INPUT "コメント:",RMKS$:PRINT
12080 PRINT "データファイルを作成します。":PRINT
12090 OPEN OUTFILE$ FOR OUTPUT AS #2
12100 PRINT #2,"' "+VERSION$
12110 PRINT #2,"' "+DATE$+" "+TIME$
12120 PRINT #2,"' "+RMKS$
12130 FOR MAIN.W=0 TO &H3F52:PRINT #2,FNADRS.D$(MAIN.W):NEXT
12140 CLOSE #2
12150 '
12160 '
12170 *MAIN.2000
12180 PRINT "データの準備ができました。":PRINT
12190 PRINT "VX−7への書き込みを開始します。"
12200 PRINT "[MON]を押しながらVX−7の電源を入れてください。"
12210 PRINT "VX−7のLCDに”CLONE”と表示されたら、[V/M]を押してください。"
12220 PRINT "VX−7のLCDに”CLONE WAIT”と表示されたら、パソコンのスペースバーを押してください。":PRINT
12230 GOSUB *COMPORT.INIT:COM ON
12240 WAIT.TYPE.COMMAND.S$=" ":GOSUB *WAIT.TYPE.COMMAND
12250 COM STOP:GOSUB *RX.BUFF.CLEAR:COM ON:OFFSET.ADRS=0
12260 '
12270 PRINT "第1ブロック送信開始":PRINT
12280 SEND.BLOCK.N=10:GOSUB *SEND.BLOCK:IF SEND.BLOCK.ERR<>0 THEN ERROR.CODE=SEND.BLOCK.ERR:GOTO *FINISH
12290 '
12300 PRINT "応答信号待ち":PRINT
12310 G.ACK=6:GOSUB *REC.ACK:IF REC.ACK.ERR<>0 THEN THEN ERROR.CODE=REC.ACK.ERR:GOTO *FINISH
12320 '
12330 PRINT "第2ブロック送信開始":PRINT
12340 SEND.BLOCK.N=8:GOSUB *SEND.BLOCK:IF SEND.BLOCK.ERR<>0 THEN ERROR.CODE=SEND.BLOCK.ERR:GOTO *FINISH
12350 '
12360 PRINT "応答信号待ち":PRINT
12370 G.ACK=6:GOSUB *REC.ACK:IF REC.ACK.ERR<>0 THEN THEN ERROR.CODE=REC.ACK.ERR:GOTO *FINISH
12380 '
12390 PRINT "第3ブロック送信開始":PRINT
12400 SEND.BLOCK.N=16193:GOSUB *SEND.BLOCK:IF SEND.BLOCK.ERR<>0 THEN ERROR.CODE=SEND.BLOCK.ERR:GOTO *FINISH
12410 '
12420 PRINT "全ブロック送信完了":PRINT
12430 ERROR.CODE=0:GOTO *FINISH

 このプログラム使った場合のVX-7のメモリデータのLOAD方法(概略)はつぎのとおりである。

1) VX-7と自作インターフェース、パソコンを接続する。
2) パソコンの電源を入れ、MS-DOSを起動する。
3) N88-BASIC(86)を起動し、プログラムをLOAD、更にRUNする。
4) "入力ファイル名:"の入力を促されるので、適当なファイル名を入力する。ここで指定するのは、"SAVEVX7C.BAS"で生成されたクローンデータファイル、もしくは、これをもとにエディタでデータを書き換えたファイルに限る。
5) 入力ファイルのチェックサムが誤っている場合は、自動的に修正される。正しいチェックサムを持つデータをファイルとして保存することも可能である。
6) VX-7の操作等が示されるので、指示通りに操作する。
7) "--- 正常終了(000) ---"が表示されれば、OK。VX-7は、電源投入時と同じグラフィック(アニメーション)を表示して、受信状態に戻る。

プログラムの解説

 例によって、8253(相当)を直接コントロールする暴挙に出ているので、いわゆる"8MHz系"の9801/9821では使えない。また、システムがハングアップする可能性もある。また、あくまでも実験用なので、エラー処理などは大幅に省略している。

 タイムアウトを判定する機能はないので、あまりに長い間画面に動きがなければ、VX-7との交信でエラーが生じた可能性がある。"STOP"キーを押せば、プログラムはいつでも停止する。

 プログラムの使用条件、ダウンロード等は、こちらにどうぞ。

★  ★  ★

 外字/アイコンという、私にとってはほとんど使わない機能ではあるが、とにかく、『クローンデータを書き換えることによって、VX-7の設定に変更を加える』ことに成功した。これは、実用面ではほとんど意味がないけれど、『VX-7 メモリデータ パソコン編集への道』の大きな一里塚を超えたことを意味する。(---何と大げさな!)

 次は、最終目標である『メモリデータのパソコン編集』に挑む。

注意

 ここに記載された情報は、私が独自に収集したものです。内容の正確性については保証しません。あなたの自己責任でご利用ください。
 VX-7のクローン機能は、あくまでも2台のVX-7を接続したうえで、設定等の情報をコピーするためのものです。ここに記した情報は私が個人的に解析した結果であり、各種のテーブルやフラグの名称は、私が独自に命名したものです。
 この件に関して、(株)バーテックススタンダードもしくはその販売店に問い合わせることはご遠慮ください。

続き

戻る


Copyright by Heian Software Engineering (C)H.S.E. 2002 Allrights reserved.
2002年10月6日 制作 2002年11月13日 修正