[今更誰得] Psion series 3aでシリアル通信してファイルをやりとりできるようにする #PSION #ポケコン

前回のエントリでPsionがOPLというプログラミング言語を搭載しているのはご紹介しましたが、やはり作ったプログラムを保全できないと、ちょっと心配なところがあります。

ということで、eBayさんでシリアル通信ケーブルも落札しまして。


1本しかないので(というか、別の出品を落札していたのですが、エコノミー便で送付されてしまい、全く届く気配なくトラッキングもできず、諦め気味ですw)、その1本が正常に動作するのかどうかもまだわからない状態なのですがw

まずはPB-1000よろしく標準の内蔵ツールなどで送受信ができないものかを実験してみます。

Psionのシステムメニューから「Special」→「Remote Link」と選ぶと、シリアルポートの設定ができます。





これを設定して、PCと接続し、TeraTermを起動してみると、


なんだか、よくわからない通信が定期的に受信されています。

おや?これはもしかして通信ケーブルの不具合・・・?
と一瞬心が折れそうになりましたが、確かこういう挙動、レシートプリンタを繋いだときにもあったような気がします。

実は、中の人は保有していないのですが(eBayのケーブルには残念ながら付属していなかった)、PsionにはPSIWINという、PalmでいうところのPalmDesktopみたいなGUIのシンクロソフトがあるようなんですね。



たぶん、推測ですがPsion側で「Remote Link」をONにすると、PSIWINとの疎通確認のために定期的になにか通信をするんじゃないかと思われます。

中の人はPSIWINを持っていないのと、仮に入手できたとしても大昔のWindowsでしか動かないでしょうから、環境を用意するのが面倒そうなので、いったん、この線は諦めました。

さっきのメニューでシリアルポートをONにすると余計な通信が発生するので、これも一旦OFFに戻します。

まあ、とはいえ通信ケーブルが生きていそうなのはわかったので、気を取り直して、今度はOPLプログラムからの送受信に挑みます。

PB-1000のC61-BASICだと、シリアル通信の手順は、OPEN文でポートを開き、あとはPRINT#やINPUT#などで送受信して最後にCLOSEで閉じるだけなので、とってもシンプルでした。

OPLはどうなのか・・・ですが、結論から言えば、ややこしいですw
OPLの通常の文法は癖はあるもののとても素直なんですが、シリアル通信やファイルアクセスでちょっと凝ったことをしようとすると途端に面倒になります。

それでも、単に出力するだけなら、LOPEN文→LPRINT文→LCLOSE文でなんとかなるのですが、入力するとまるで別物の複雑さに。

まずは簡単な出力の方から。

最初に、LOPEN文でシリアルポートを開きます。
LOPEN "TTY:A"
出力したい内容は、LPRINT文で出力します。
LPRINT 出力内容
終わったら、LCLOSE文で閉じます。
LCLOSE
ちなみに、LOPENの引数をファイル名にすると、ファイルへ書き出すことができます。
LOPEN "\OPL\test.txt"
ただし、シリアルポートの設定をデフォルト以外の設定にするときは、ちょっと面倒なのですが、ネットで調べたら、それ用のプロシジャーが紹介されていたので、それをそのまま使いました。以下のプログラムをrssetというファイル名(rsset.opl)で保存します。

PROC rsset:(baud%,parity%,data%,stop%,hand%,term&)
	local frame%,srchar%(6),dummy%,err%
	frame%=data%-5
	if stop%=2 :frame%=frame% or 16 :endif
	if parity% :frame%=frame% or 32 :endif
	srchar%(1)=baud% or (baud%*256)
	srchar%(2)=frame% or (parity%*256)
	srchar%(3)=(hand% and 255) or $1100
	srchar%(4)=$13
	pokel addr(srchar%(5)),term&
	err%=iow(-1,7,srchar%(1),dummy%)
	if err% :raise err% :endif
ENDP

baud%でボーレートを指定しますが、値の意味は以下の通りです。

Baud

50

75

110

134

150

300

600

1200

1800

2000

2400

3600

4800

7200

9600

19200

value

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16


hand%はエラー訂正方式ですね。

Handshaking

ALL

NONE

XON

RTS

XON+RTS

DSR

XON+DSR

RTS+DSR

value

11

4

7

0

3

12

15

8


指定はこんな感じです。

LOADM "rsset"
rsset:(15,0,8,1,7,&0)

LOADMは指定されたプログラムを読み込んで、現在のプログラム内からプロシジャーとして呼び出せるようにする構文のようです。

では、入力の方に行きましょう。

ポートを開くところは出力時とほとんど同じなのですが、先ほどのrssetプロシジャーの引数term&に「&04002000」と指定します。「&04002000」は、CTRL+Z、CR、LFを受信したら終了するようにするために設定します。

続いてポートからの読み込みですが・・・ここからがエグいw

まず読み込みには「IOW」という関数を使います。指定例は以下の通りです。

ret%=IOW(-1,1,#UADD(pbuf&,1),len%)

最初の2つの引数の意味は正直よくわからないですw ネット上のサンプルをそのまま使ってますww

次の#UADD(pbuf&,1)ですが、読み込んだ内容を書き込むバッファの位置(メモリ上のアドレス)を指定しています。UADDは引数同士を加算する関数です。

pbuf&というのが唐突に出てきますが、これは、あらかじめ
local pbuf$(255),pbuf&
と宣言して、文字列変数と、整数型変数を用意しておいたうえで、
pbuf&=ADDR(pbuf$)
としてある前提になります。ADDR()関数は、pbuf$のメモリ上のアドレス(いわゆるポインタというやつでしょうか)を求める関数のようですね。

ちなみに文字列変数のポインタが示す最初の番地には文字数が書き込まれているようで、実際の文字列データはその次の番地から書かれているようです。そのため、書き込むバッファの位置は、文字列変数のアドレスの1つ後ろからになるということのようです。

IOW関数の最後の引数len%は、実際に読み込まれた文字数を受け取るための変数のようです。

戻り値を格納しているret%は、いわゆる実行結果を示す値でしょうね。
マイナスの値が異常を表しているのですが、EOFが-36なので、それ以外のマイナスの値だったら異常終了とみなす必要があります。

で、実際に読み込む文字数はその時によって違うでしょうから、さっきのpbuf&の場所に、実際に読み込んだ文字数len%を書き込んでおきます。
POKE pbuf&,len%

といった具合です。

で、いつまでも読み込み続けるわけにはいかないので、

end% = LOC(buf$,chr$(26))

によって、CTRL+Z(chr$(26)のことかな?)の出現位置を計測して、0以外(つまり読み込んだバッファの文字列変数のどこかにCTRL+Zが含まれているという意味。0は見つからなかったことを示す)になったら、

UNTIL end%

によってループが終了します。
ちなみにOPLでは0以外の値はすべて真、0が偽とみなされるようです。

読み込んだ内容をファイルへ書き出す場合は、上で書いたLOPEN→LPRINT→LCLOSEでいいんじゃないかと思いますが、ほかの方法もあって、ネット上の参考情報ではIOOPEN、IOWRITE、IOCLSEを使ってましたので、中の人のサンプルコードもそうなっています。

ということで、いったん完成版のソースコードを。
試行錯誤しつ作ったので無駄なところとかが多いかもしれませんがご容赦ください。

PROC filerecv:
	local ret%,fName$(128)
	local handle%,mode%,k%
	local pbuf&,pbuf1&,buf$(255),len%,end%
	local skipel$(1)
	
	lopen "TTY:A"
	loadm "rsset"
	rsset:(7,0,8,1,7,&04002000)
	rem 9600=15,4800=13,2400=11,1200=8,600=7,300=6
	rem none=4,Xon=7
	pbuf&=ADDR(buf$)
	print "Filename?";
	input fName$
	mode%=$0100 or $0020 or $0002
	rem replace=$0002,text=$0020,update=$0100
	ret%=IOOPEN(handle%,fName$,mode%)
	if ret%<0
		showErr:(ret%)
		return
	endif
	
	print "Skip empty line?(y/n):";
	input skipel$
	
	do
		k%=key
		if k%	rem if keypressed
			if k%=27	rem Esc
				break
			elseif get=27
				break
			endif
		endif
		
		len%=255
		ret%=IOW(-1,1,#UADD(pbuf&,1),len%)
		pokeb pbuf&,len%
		end%=loc(buf$,chr$(26)) rem non-zero for Ctrl+Z
		if ret%<0 and ret%<>-36
			showErr:(ret%)
			break
		endif
		rem print buf$
		print "[";(len%-1);"]";
		
		if skipel$<>"y" or len%>0
			print buf$
			ret%=IOWRITE(handle%,pbuf&+1,len(buf$))
		endif
	until end%
	ret%=IOCLOSE(handle%)
	if ret%
		showErr:(ret%)
	endif
	lclose
	pause -100:key
ENDP
PROC showErr:(val%)
	print "Error",val%,err$(val%)
	get
ENDP

とりあえず現場からは以上です。



コメント