FreeBSDのマシンからPC-9801へデータやプログラムを送る方法を考えてみた。一昔前であればフロッピーを使うのが常套手段だったが、もはやフロッピードライブは接続されていないのでシリアル通信を使う方法を調べてみた。
Disk BASIC編
PC-9801VM2に付属していたN88-日本語BASIC(86) PC-98H44-MW(K)をPC-9801FA2で実行して、シリアル通信(RS-232C)を試して見ました。
PC-9801は内蔵のROM BASICかフロッピーのDisk BASICかMS-DOSで起動できました。DOS上にもN-88 BASICはありましがが、ここではDisk BASICを使っています。初期のPC-9801にはDisk BASICが付属していましたが、後期の製品には付いていませんでした。
NECのパソコンのBASICはN-BASIC -> N-88 BASIC -> N-88 BASIC(86)となりました。
起動すると以下のように表示されます。
Disk version
How many files(0-15)?
NEC N-88 BASIC(86) version 3.0
Copyright (C) 1983 by NEC Corporation / Microsoft Corp.
599464 Bytes free
Ok
クロスケーブルはHardOffで9ピンのケーブル(220円)を買ってきて手持ちの25ピンのケーブルと切ってつなぎ合わせて作りました。
25ピン | 9ピン | |
---|---|---|
2(TX) | -> | 2(RX) |
3(RX) | <- | 3(TX) |
7(GND) | -- | 5(GND) |
4(RTS) | -> | 8(CTS) |
5(CTS) | <- | 7(RTS) |
6(DSR) | <- | 4(DTR) |
20(DTR) | -> | 6(DSR) |
N-88 BASIC(86)でシリアル関係の関数
関数名 | 機能 |
---|---|
CLOSE | ファイルを閉じます。 |
COM ON/OFF/STOP | RS-232C回線からの割り込み許可、禁止、停止を制御します。 |
INPUT# | シーケンシャルファイルからデータを読み込み、変数に代入します。 |
INPUT$ | 指定されたファイルより指定された長さの文字列を読み込みます。 |
LINE INPUT# | シーケンシャルファイルより、1行(255バイト以内)単位のデータを一括して文字型変数に読み込みます。 |
ON COM GOSUB | RS-232C回線からの割り込みが発生したとき、分岐する処理ルーチンの開始行番号を定義します。 |
OPEN | ファイルを開きます。 |
PRINT# | ファイルにデータを書き出します。 |
PRINT# USING | 文字列、数値などのデータを編集し、ファイルに出力します。 |
WRITE# | ファイルにデータを書き出します。 |
ケーブルを接続してPC-9801側で下記のプログラムを実行します。
10 OPEN "COM1:N81NN" AS #1
20 A$=INPUT$(1,#1)
30 PRINT #1,A$;
40 GOTO 20
一文字読み取って、一文字出力しています。
FreeBSDをインストールしたValueStarに接続して確認して見ます。
% sudo cu -l /dev/cuau0 -s 1200
Connected
123
簡単なファイル転送プログラムを作ってみました。PC-9801で以下のプログラムを実行します。
10 LINE INPUT "File Name "; F$
20 OPEN F$ FOR OUTPUT AS #1
30 OPEN "COM1:N81NN" AS #2
40 LINE INPUT #2,L$
50 IF L$="." THEN GOTO 80
60 PRINT #1,L$
70 GOTO 40
80 CLOSE #1
90 CLOSE #2
FreeBSD側ではSerialPortを入れたmrubyで以下のコードを使います。
# !/usr/local/bin/mruby
sp = SerialPort.new("/dev/cuau0", 1200, 8, 1, 0)
File.open(ARGV[0]){|f|
f.each_line{|line|
sp.puts line.chomp + "\r\n"
}
}
sp.puts ".\r\n"
以下のように実行します。
% sudo ./save.rb test.bas
N88-日本語BASIC(86)ではSAVE "COM1:N81NN"でシリアルに出力されるのですが、EOFはCTSで確認できました。
while 1
p sp.gets
l = sp.get_signals
if l[:cts] == 0
break
end
end
なぜか同じようにしてもLOAD "COM1:N81NN"が終了しません。なにを終了条件にしているのかわかりません。上に書いたプログラムを使う方法でファイル転送するのがよさそうです。
MS-DOS 3.3編
MS-DOSではCOPYAコマンドでシリアル通信ができますが、CONFIG.SYSに以下の設定が必要です。
DEVICE=RSDRV.SYS
ボーレートなどの設定はSPEEDコマンドで行います。
手元にオシロがないので確認できないのですが、バイナリでの転送のEOFは信号線ではなくて、break信号な気がしています。
SPEEDで設定してCTTY AUXするとMS-DOSがシリアル経由で操作できます。漢字コードはもちろんSJISです。
アセンブラでエコーするコードです。
org 100h
loop:
mov ah,3
int 21h
mov dl,al
mov ah,4
int 21h
jmp loop
CCT98編
昔買った、CCT-98II(Ver2.5)のディスクが出てきたので、ファイル転送を試してみました。FreeBSD側にlrzszのpkgを入れて、昔懐かしいXMODEMでファイルを送信します。
% sudo sh -c "lsx test.txt < /dev/cuau0 > /dev/cuau0"
Sending test.txt, 1 blocks: Give your local XMODEM receive command now.
Bytes Sent: 256 BPS:70
FreeBSD側のデフォルトの通信速度が9600なので、CCT98側で合わせてシフト+F4がXMODEMで、ファイル名を入力するとファイル受信が始まります。
XMODDMは128バイトのブロック転送するので、ファイルのサイズが変わります。またPC-9801FAは9600ボーでしか通信できないので、とっても時間がかかります。
これらを回避するために、昔はLHAで圧縮して送受信していましたが、LHAは使用の中止が呼びかけられているので、解凍するlhasaのpkgはありますが、圧縮のコマンドのpkgは用意されていません。
いろいろ考えてみてzipコマンドはpkgもありDOSのコマンドもあるので、zipで圧縮してDOS側でunzipする事にしました。
FreeBSDに標準で入っている圧縮コマンドのxzはDOS用のバイナリもありましたがPC-9801のDOS(3.3)では動きませんでした。
XMODEM
ファイル転送のたびにCCTを起動するのが面倒なので、XMODEMの受信プログラムを作ってみました。エラーチェックなしタイムアウトなしのいい加減な実装です。SPEEDコマンドで設定をして、送信側を準備してから起動する必要があります。ファイル名を引数に渡します。
直結の場合、まったくといっていいほどエラーは起きないのでチェックサムやCRCはあまり必要ないと思います。
; MS-DOS xmodem revice program
SOH EQU 01H
STX EQU 02H
EOT EQU 04H
ACK EQU 06H
NAK EQU 15H
CAN EQU 18H
ORG 100H
MOV BX,80H
XOR CX,CX
ADD CL,[BX]
DEC CX
MOV SI,82H
MOV DI,FOPT
L5:
MOV AL,[SI]
MOV [DI],AL
INC SI
INC DI
LOOP L5
MOV [DI],BYTE 0
; create
MOV DX,FOPT
XOR CX,CX
MOV AH,3CH
INT 21H
JC ER
; open
MOV DX,FOPT
MOV AH,3DH
MOV AL,01H
INT 21H
MOV [FHAND],AX
; XMODEM
MOV AH,04H
MOV DL,NAK
INT 21H
L2:
MOV AH,03H
INT 21H
CMP AL,EOT
JE L1
MOV CX,131
MOV BX,BUFF
L3:
MOV AH,03H
INT 21H
MOV [BX],AL
INC BX
LOOP L3
MOV AH,04H
MOV DL,ACK
INT 21H
; write
MOV BX,[FHAND]
MOV CX,128
MOV DX,BUFF
ADD DX,2
MOV AH,40H
INT 21H
JMP L2
L1:
MOV AH,04H
MOV DL,ACK
INT 21H
; close
MOV BX,[FHAND]
MOV AH,3EH
INT 21H
JMP L4
ER:
MOV DX, ERRSTR
MOV AH,09H
INT 21H
; EXIT
L4:
MOV AH,4CH
INT 21H
ERRSTR DB 'ERROR$'
FHAND DW 0
BUFF DB 256 DUP(0)
FOPT DB 32 DUP(0)
引数の先頭にスペースが入っているのに気づかずはまりました。nasmかfasmでアセンブルできます。jwasmではとおりません。
XMODEMはファイルサイズを送らないので、最後のブロックがパディングされるのですが、バイナリファイルの場合、それがパディングによるものか、もともとあったものかはわからないので、受信側で削除できません。パディングされたもののファイルのハッシュ値を確認すると違っているので、今日では利用は控えたほうが良いのかもしれません。
YMODEM
XMODEMのコードをいじってYMODEMの受信プログラムも作ってみました。
いつものようにトライアンドエラーでFreeBSDでアセンブルしてPC-9801のxrdos.comでyrdos.comを受け取って確認しました。
YMODEMでは先頭のブロックに、ファイル名やファイルサイズが入っています。ファイルサイズはアスキーで入っていて、16ビットだと64Kまでしか扱えません。さすがにそれではまずいのでBXレジスタとAHレジスタの24ビットで数値化して、ブロック数と最終ブロックのサイズに分けて保存するようにしました。
当たり前ですが、16ビットの最大値のFFFFとFFFFを足すと1FFFEで1以上の繰上げはありません。
ロジックは上位のAHを先に10回足して、次にBXを10回足します。BXを足してる間にキャリーが発生した場合はAHをINCします。終わったら数字のアスキーから'0'を引いて数値にしてBXに足します。ここでもキャリーが発生したときにはAHをINCします。スペースがあわられるまでこれを繰り返して、BXの下位7ビットを最終ブロックのサイズとして、AHの7ビットとBXの9ビットをブロック数としています。これで8Mバイトまでのファイルに対応できます。(24ビットフルで使えば16Mですが、1ビット捨ててるので8Mになります。)
ブロック番号の最初の0がファイル名などが入っているブロックでその後に実際のデーターが入ります。ブロック番号の255の後は0になるようでした。
AUTOEXEC.BATに以下を書いておくと自動的に設定されます。
SPEED RS232C-0 9600 B8
FreeBSD側でcshなどであればlsbをaliasしておくと便利です。
alias ys 'sudo sh -c "lsb -b '\!:1' < /dev/cuau0 > /dev/cuau0"'
CRCのチェックはしてないので、MD5などで確認してください。
ブートストラップ
CCTがあったので最初のコピーができましたが、もしCCTがない場合にどうするのが良いか考えて見ました。
パソコン通信で使われていたISH.COMだけはPC-9801側にある状態であればどうにかなりました。
初期のパソコン通信ではバイナリの保存ができなかったので、ISHの様なアスキーに変換するツールで、掲示板に貼り付けたりしていました。
PC-9801のMS-DOSにはCOPYAというコマンドがあります。このコマンドでテキストファイルの受信は以下のmrubyスクリプトからできます。
# !/usr/local/bin/mruby
sp = SerialPort.new("/dev/cuau0", 9600, 8, 1, 0)
File.open(ARGV[0]){|f|
f.each_line{|line|
sp.puts line.chomp + "\r\n"
}
}
sp.puts "\x1A"
FreeBSD側ではishのpkgをインストールしておきます。ishのpkgはメンテナーがいないよって警告がでますね。ishはOSのタイプを入れるようなので-mでMS-DOSを指定します。
% sudo pkg install ish
% ish -s -m a.exe
ish file converter for UNIX Ver2.01a5
original sources are Human68K Ver1.11
original idea By M.ishizuka(for MS-DOS)
copyright 1988 - Pekin - (by Ken+Gigo)
non-kana by keizo July 12, 1990
multi-vol restore supported by taka@exsys.prm.prug.or.jp 1992
multi-vol store supported by aka@redcat.niiza.saitama.jp 1993
< Create to a.ish >
a.exe (8712 Bytes)... converted.
PC-9801のCOPYAをテキストの受信状態にしてFreeBSD側で下記を実行します。
% ./copya.rb a.ish
受け取ってISH.COMで展開すればバイナリの受信が可能です。
A>ISH A.ISH
ish file converter for MS-DOS Ver. 2.03
Copyright (C) 1986, 1987, 1989, 1990 by M. Ishizuka
[ restore <new> (m,*) ]
a.ish:
[2] a.exe(m) : (152/152) ooo
file restored successfully.
1 file(s)/ 0 volume(s) restored.
この方法で上記のxmodemやymodemの実行ファイルを渡せば、次からはxmodemやymodemでバイナリの受信が可能になります。
Windows 3.1編
Windows 3.1にはターミナルアプリケーションが標準で入っています。
XMODEMの送受信ができます。