考えてみれば、クローンデータの書き換えは、大きなリスクを伴う実験である。
一般に、パソコンプログラムの"改造"などは、仮に不具合があっても、パソコンがフリーズするだけで済む。けれども、クローンデータの書き換えは、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は、外字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に示すとおりである(アイコン1の例)。
"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が壊れても筆者は一切責任を負わないので、念のため。
次はいよいよ書き換えたデータを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 メモリデータ パソコン編集への道』の大きな一里塚を超えたことを意味する。(---何と大げさな!)
次は、最終目標である『メモリデータのパソコン編集』に挑む。
ここに記載された情報は、私が独自に収集したものです。内容の正確性については保証しません。あなたの自己責任でご利用ください。 |
Copyright by Heian Software Engineering (C)H.S.E.
2002 Allrights reserved.
2002年10月6日 制作 2002年11月13日 修正