🎵

MSX0でSocket通信

2023/10/13に公開

先日、MSX0でHTTP通信を試してみました。結果的に通信自体は出来たのですが、レスポンスヘッダが厄介だったり、_IOTGET("msx/me/if/NET0/msg",...)で受け取ったデータの扱いが面倒ということがわかりました。

色々と試してみるうちに、_IOTGET("msx/me/if/NET0/msg")自体はHTTPに機能を限定しているわけではないという事に気が付きました。おそらく、低レベルなSocket通信が出来そうです。

サーバを準備する

MSX0からSocket通信をする相手=サーバが必要になります。お手軽な手段ということで、Pythonを使ってみます。PythonのSocketライブラリが使える環境であれば、OSは何でも大丈夫だと思います。私はWindows10と、Microsoft StoreからインストールできるPython3を使ってみました。

まずはサーバ側のプログラムです。適当なフォルダに以下の内容のファイルを作成して保存します。ファイル名は何でもよいです。

server.py
import socket

serverIpAddress = socket.gethostbyname(socket.gethostname())
portNumber = 5500
print(f"server IP Address:{serverIpAddress}")
print(f"server port:{portNumber}")

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(), portNumber))
s.listen(5)

while True:
    clientsocket, clientIpAddress = s.accept()
    print(f"Connection from {clientIpAddress}")
    clientsocket.send(bytes("hello MSX0", 'shift_jis'))
    clientsocket.close()

コマンドプロンプトを起動して、python server.py で実行開始です。

サーバプログラムを実行しているPCのIPアドレスとポート番号が表示されて、待機状態となります。
クライアントから接続されたらhello MSX0を返すというサーバの完成です。

MSX0からSocket接続してみる

MSX0側で、以下のプログラムを入力します。
2010行目のIPアドレスは、先に実行したサーバプログラムで表示されるアドレスにしてください。

1010 CLS
1020 CLEAR 800
2010 SV$="192.168.10.108"
2020 PA$="msx/me/if/NET0/"
3010 _IOTPUT(PA$+ "conf/addr",SV$)
3020 _IOTPUT(PA$+ "conf/port",5500)
3030 _IOTPUT(PA$+ "connect",1)
3510 FOR T=0 TO 100:NEXT
3520 _IOTGET(PA$+ "connect",S)
3530 IF S<>1 THEN PRINT "connect fail":GOTO 8010
3540 PRINT "connect success"

4010 FOR T=0 TO 100:NEXT
4020 _IOTGET(PA$+"msg", RV$)
4030 PRINT RV$

8010 _IOTPUT(PA$+ "connect",0)

プログラムの細かな説明は、MSX0でHTTP通信の方でしているので省略します。
単純に、サーバに接続しにいって、_IOTGET()でデータを受け取って、それをPRINT文で表示しているだけです。

RUNしてみましょう。

サーバから文字列hello MSX0を受け取ることが出来ました。
(なぜか初回の実行時に失敗することがあるようです。HTTP通信の時もあったような...)

待機しているサーバ側も反応していますね。

バイナリデータで試してみる

先ほどは文字列の受信でしたが、バイナリデータでも大丈夫なはずですのでテストしてみます。

サーバ側プログラムを変更します。

server.py
import socket

serverIpAddress = socket.gethostbyname(socket.gethostname())
portNumber = 5500
print(f"server IP Address:{serverIpAddress}")
print(f"server port:{portNumber}")

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(), portNumber))
s.listen(5)

data = bytes(range(0, 256))

while True:
    clientsocket, clientIpAddress = s.accept()
    print(f"Connection from {clientIpAddress}")
    clientsocket.send(data)
    clientsocket.close()

バイト列を0x00から0xFFまで送り返すという処理にしてあります。

MSX0側のプログラムを一部修正します。

4010 FOR T=0 TO 100:NEXT
4020 CC=0
4030 _IOTGET(PA$+"msg", RV$)
4040 IF RV$="" THEN 8010
4050 LN=LEN(RV$)
4060 FOR I=1 TO LN
4070 C0=ASC(MID$(RV$,I,1))
4080 if CC<>C0 THEN PRINT"MISSMATCH":GOTO 8010
4090 PRINT C0;",";
4100 CC=CC+1
4110 NEXT
4120 GOTO 4030

動作確認用ということもあって若干ややこしいですが、0x00から0xFFから順に受け取れているかを比較確認しつつ、その数値を表示しています。

実行してみたところ問題なく動作しました。制御コードなどを意識せずにバイトデータの受け取りが出来ました。

MSX0からデータを送ってみる

今度はMSX0からScoketサーバにデータを送ってみようと思ったのですが、なかなか厄介な事になりました。

まずはサーバ側のプログラムから。

server.py
import socket

serverIpAddress = socket.gethostbyname(socket.gethostname())
portNumber = 5500
print(f"server IP Address:{serverIpAddress}")
print(f"server port:{portNumber}")

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((socket.gethostname(), portNumber))
s.listen()

while True:
    clientsocket, clientIpAddress = s.accept()
    print(f"Connection from {clientIpAddress}")

    fullMsg = b''
    while True:
        recvMsg = clientsocket.recv(4096)
        fullMsg += recvMsg
        if '\r' in recvMsg.decode('shift_jis'): break

    decodedMsg = fullMsg.decode('shift_jis')
    print(f"recv msg:{decodedMsg}")
    upperMsg = decodedMsg.upper()
    print(f"send msg:{upperMsg}")
    clientsocket.send(bytes(upperMsg, encoding='shift_jis'))
    clientsocket.close()

クライアントからの接続を待ち、それを文字列としてデータを受け取り、大文字に変換して返すというプログラムです。

一つ目のwhileは、何度でもメッセージを受け取る無限に動作するサーバとして動かすためのものです。

2つ目のwhileからが少しややこしいです。
MSX0からPythonで立ち上げたSocketサーバに対して文字列を送ると、なぜか1文字目とそれ以降の文字列が分離した状態になって受け取ってしまうようです。MSX0に限らず、同一PC上のtelnetソフトから手入力で文字入力した時も似たような状態になったので、PythonのSocket recv()にタイムアウトのようなものがあるのかもしれません。この辺の挙動がよくわかりませんでした。
なので、ここでは、MSX0からのCHR$(13)、つまり改行コードである'\r'を受け取るまで、データをつなげ続けるようにしています。
改行コード受け取り以降は、文字列への変換と、大文字化を行って、send()でMSX0に送り返しています。

続いてMSX0側のプログラムです。

1010 CLS
1020 CLEAR 800
2010 SV$="192.168.10.108"
2020 PA$="msx/me/if/NET0/"
3010 _IOTPUT(PA$+ "conf/addr",SV$)
3020 _IOTPUT(PA$+ "conf/port",5500)
3030 _IOTPUT(PA$+ "connect",1)
3510 FOR T=0 TO 100:NEXT
3520 _IOTGET(PA$+ "connect",S)
3530 IF S<>1 THEN PRINT "connect fail":GOTO 8010
3540 PRINT "connect success"

4010 INPUT "MSG";MS$
4020 _IOTPUT(PA$+"msg", MS$ + CHR$(13))
4030 _IOTGET(PA$+"msg", RV$)
4040 IF RV$="" THEN 4030
4050 PRINT RV$

8010 _IOTPUT(PA$+ "connect",0)

INPUTで文字入力を行い、Socketサーバに送って、受け取って表示しているという処理になります。
4040行目ですが、_IOTGET()が空のデータを受け取ることがあるようなので、何かしらのデータが入ってくるまで受け取りを繰り返しています。このような受け取り方法をしていいのかわからないのですが、動いてるのでOKとしましょう。

サーバ側の表示結果

MSX0の表示結果

まとめ

最後はちょっとすっきりしませんでしたが、生のSocket通信であれば手軽にデータのやり取りを行えることがわかりました。サーバとMSX0の間でのやり取り形式を自分で決めることになるのでオレオレプロトコルになってしまいますが、それを考えて決めることもMSX0での創作活動かなと思います。

PythonはWindows、Mac、Linuxなどの様々な機器で動きますし、そういった機器とワイヤレスでMSX0とデータのやり取りができるというのは、色々な可能性が広がりそうですね。

Discussion