LoginSignup
4
3

【MSX0】IOT.LIB

Last updated at Posted at 2023-10-27

はじめに

理屈はよくわからないけれど、Turbo Pascal で『MSX0 Stack』の IoT 関係の機能が使えそうだったので、ライブラリとして整備してみました。

ライブラリ

汎用ライブラリの SYSUTILS.LIB と、IoT 関係の機能を使うための IOT.LIB から構成されます。

SYSUTILS.LIB

よく使うであろう機能を入れてあります。

SYSUTILS.LIB
(*
    SYSUTILS.LIB
*)

type
  HexStr  = string[8];
  BinStr  = string[32];
  ItemStr = string[80];

  PItem = ^TItem;
  TItem = record
    Data: ItemStr;
    Prev: PItem;
    Next: PItem;
  end;

  procedure AddListItem(var List, Last: PItem; s: ItemStr);
  var
    p: PItem;
  begin
    New(p);
    if List = nil then
      begin
        List := p;
        Last := nil;
      end
    else
      Last^.Next := p;
    p^.Data := s;
    p^.Prev := Last;
    p^.Next := nil;
    Last := p;
  end;

  procedure ClearListItem(var List: PItem);
  var
    p, n: PItem;
  begin
    p := List;
    while p <> nil do
      begin
        n := p^.Next;
        Dispose(p);
        p := n;
      end;
    List := nil;
  end;

  function UpperCase(s: ItemStr): ItemStr;
  var
    r: ItemStr;
    i: byte;
  begin
    r[0] := s[0];
    for i:=1 to Length(s) do
      begin
        if s[i] in ['a'..'z'] then
          r[i] := Chr(Ord(s[i]) - 32)
        else  
          r[i] := s[i];
      end;
    UpperCase := r;  
  end;

  function LowerCase(s: ItemStr): ItemStr;
  var
    r: ItemStr;
    i: byte;
  begin
    r[0] := s[0];
    for i:=1 to Length(s) do
      begin
        if s[i] in ['A'..'Z'] then
          r[i] := Chr(Ord(s[i]) + 32)
        else  
          r[i] := s[i];
      end;
    LowerCase := r;  
  end;

  function Inkey: char;
  var
    c: char;
  begin
    Inkey := #$00;
    if KeyPressed then
    begin
      Read(kbd, c);
      Inkey := c;
    end;
  end;

  function IntToHex(v: Integer; w: byte): HexStr;
  var
    s: HexStr;
  begin
    if w < 1 then w := 1;
    if w > 4 then w := 4;
    s[0] := Chr(w);
    while w > 0 do
    begin
      if v = 0 then
        s[w] := '0'
      else  
      begin
        s[w] := Chr($30 + Ord(v and $000F > 9) * 7 + v and $000F);
        v := v shr 4;
      end;  
      w := w - 1;
    end; 
    IntToHex := s;
  end; 

  function IntToBin(v: Integer; w: byte): BinStr;
  var
    s: BinStr;
  begin
    if w <  1 then w :=  1;
    if w > 16 then w := 16;
    s[0] := Chr(w);
    while w > 0 do
    begin
      if v = 0 then
        s[w] := '0'
      else  
      begin
        s[w] := Chr($30 + Ord(v and 1));
        v := v shr 4;
      end;  
      w := w - 1;
    end; 
    IntToBin := s;
  end; 

動的配列のない Turbo Pascal のために、文字列リストを実装しました。

ルーチン 説明
AddListItem() リストにアイテムを追加します。
ClearListItem() リストのアイテムを全削除します。
UpperCase() 文字列を大文字にしたものを返します。
LowerCase() 文字列を小文字にしたものを返します。
Inkey() 押されたキーの文字を返します。
IntToHex() 整数を 16 進文字列に変換します。
IntToBin() 整数を 2 進文字列に変換します。

Turbo Pascal 3.0 では、プログラム中で一度も使わないルーチンがあってもライブラリとしてリンクされてしまいます。実行形式ファイル (*.COM) が肥大化しがちなので、不要なルーチンはコメントアウトしてお使いください。

IOT.LIB

HRA! さんのコードを参考にしています。

IOT.LIB
(*
    IOT.LIB
*)

  procedure IotSetNode(s: LibStr);
  var
    i, l: byte;
  begin
    port[8] := $E0; port[8] := $01; port[8] := $53; port[8] := $C0;
    l := Length(s); port[8] := l;
    for i:=1 to l do port[8] := Ord(s[i]);
    port[8] := $00; l := port[8];
  end;

  function IoTFindCount(s: LibStr): integer;
  var
    r: array [0..1] of byte;
    v: integer absolute r;
  begin
    IotSetNode(s);
    port[8] := $E0; port[8] := $01; port[8] := $11; port[8] := $80;
    r[0] := port[8]; r[0] := port[8]; r[1] := port[8];
    IoTFindCount := v;  
  end; { IoTFindCount }  

  procedure IoTFind(s: LibStr; var List, Last: PItem);
  var
    r: LibStr;
    i, j, l, Cnt: byte;
  begin
    ClearListItem(List);
    Cnt := IoTFindCount(s);
    IotSetNode(s);
    port[8] := $E0; port[8] := $01; port[8] := $13; port[8] := $80;
    for j:=1 to Cnt do
      begin
        l := port[8]; r[0] := Chr(l);
        for i:=1 to l do r[i] := Chr(port[8]);
        AddListItem(List, Last, r);
      end;
  end; { IoTFind }

  function IoTGetInt(s: LibStr): integer;
  var
    r: array [0..1] of byte;
    v: integer absolute r;
  begin
    IotSetNode(s);
    port[8] := $E0; port[8] := $01; port[8] := $01; port[8] := $80;
    r[0] := port[8]; r[0] := port[8]; r[1] := port[8];
    IoTGetInt := v;  
  end; { IoTGetInt }  

  function IoTGetStr(s: LibStr): LibStr;
  var
    r: LibStr;
    i, l: byte;
  begin
    IotSetNode(s);
    port[8] := $E0; port[8] := $01; port[8] := $03; port[8] := $80;
    l := port[8]; r[0] := Chr(l);
    for i:=1 to l do r[i] := Chr(port[8]);
    IoTGetStr := r;
  end; { IoTGetStr }

  procedure IoTPutInt(s: LibStr; v: integer);
  begin
    IotSetNode(s);
    port[8] := $E0; port[8] := $01; port[8] := $41; port[8] := $C0; 
    port[8] := SizeOf(integer);
    port[8] := v and $00FF; port[8] := v shr 8;
  end; { IoTPutInt }  
  
  procedure IoTPutStr(s: LibStr; v: LibStr);
  var
    i, l: byte;
  begin
    IotSetNode(s);
    port[8] := $E0; port[8] := $01; port[8] := $43; port[8] := $C0;
    l := Length(v); port[8] := l;
    for i:=1 to l do port[8] := Ord(v[i]);
    port[8] := $00; l := port[8];
  end; { IoTPutStr }  
ルーチン 説明
IoTFindCount() 対象のノードが持つアイテムの個数を取得します。
IoTFind() 対象のノードが持つアイテム名のリストを取得します。
IoTGetInt() ノードから整数値を読み出します。
IoTGetStr() ノードから文字列を読み出します。
IoTPutInt() ノードに対して整数値を書き込みます。
IoTPutStr() ノードに対して文字列を書き込みます。

使い方

SYSUTILS.LIBIOT.LIB は Turbo Pascal のフォルダに放り込んでください。{$I ファイル名} としてインクルードすると使えます。

program IOTTEST;
type
  LibStr = string[80];
{$I SYSUTILS.LIB}
{$I IOT.LIB}

var
  List, Last, p: PItem;
begin
  { Get Data }
  writeln(IoTGetInt('host/battery/level'));
  writeln(IoTGetStr('host/ip'));

  { Put Data }
  IoTPutInt('conf/sound/volume', 7);
  IotPutStr('conf/wifi/list/3/ssid', 'DEADBEEF');
  IotPutInt('conf/save', 1);
  writeln(IoTGetInt('conf/sound/volume'));
  writeln(IoTGetStr('conf/wifi/list/3/ssid'));

  { Find Data }
  writeln(IoTFindCount('host/media/dsk/');
  List := nil;
  IoTFind('host/media/dsk/', List, Last);
  p := List;
  while p <> nil do
    begin
      Writeln(p^.Data);
      p := p^.Next;
    end;
  ClearListItem(List);
end.

IoTFind() にはリストを使っています。List, Last というリスト (の先頭) を指すポインタと、リストの最後のアイテムを指すポインタを用意すると、リストが生成されます。リストが不要になったら ClearListItem() を使ってリストアイテムを解放してください。

LibStr は MDL-LIB でも定義されています。MDL-LIB を使う場合には定義不要です。

トランジェントコマンド

本記事にあるライブラリを用いて作られたトランジェントコマンド (外部コマンド) です。アーカイブには実行形式ファイル (*.COM) とソースファイル (*.PAS) が含まれています。

MSX-DOS2 上でコンパイルされているため、少なくとも MSX0 上で動作しない事はないと思います (MSX-DOS の方が TPA の空き領域が広いため、MSX-DOS 上でそのままコンパイルすると MSX-DOS2 で動作しない事があり得る)。

記事中のトランジェントコマンドのソースコード/バイナリは GitHub からもダウンロードできます。

■ M0CLKUP

エミュレータのクロックアップを行う。

image.png

Usage
M0CLKUP [パーセント]  
  • パーセントを指定しない場合には現在の速度が表示されます。
  • パーセントは 32..255 の間で有効です。
  • C コンパイラでコンパイルする前にクロックアップして、実行前にクロックを元に戻すとかできます。

Download: M0ClkUp_110.zip (ht-deko.com)

■ M0SETUP

Setup Utility を起動する。

image.png

Usage
Usage: M0SETUP
  • Setup Utility を起動します。
  • 実行後にリセットが行われます。

Download: M0Setup_100.zip (ht-deko.com)

■ M0SHTDN

電源断または再起動を行う。

Usage
M0SHTDN [-h|-r]
  • パラメータなしまたは -h で『MSX0 Stack』の電源断、-r で再起動します。
  • スイッチは - でも / でもよく、大文字/小文字も区別しません。

Download: M0Shtdn_100.zip (ht-deko.com)

■ M0CHGDSK

ディスク交換を行う。
image.png image.png

Usage
M0CHGDSK [ディスクイメージ]
  • パラメータなしで実行すると、ファイルリストと現在のディスクを示すインジケーターを表示します。
  • ディスクイメージファイル名を指定すると、ディスクを変更します。
  • ファイル名は自動的に大文字に変換されます。
  • ファイル名は拡張子の省略が可能です。省略されると .DSK を追加します (拡張子のないファイルは使えません)。
  • 空白を含むファイル名のためにダブルクォーテーション処理が入っていますが、簡易的なので可能な限り空白を含むファイル名は使わないでください。
  • ディスク交換に失敗するとメッセージが表示されます。
  • 交換先に M0CHGDSK があるとは限らないので、RAM ディスクにコピーして使う事をオススメします。
  • MSX-C の環境ディスクを作るのに使ってみました。

Download: M0ChgDsk_110.zip (ht-deko.com)

■ M0SETAP

Wi-Fi の AP リストを登録する。

image.png image.png

Usage
Usage: M0SETAP [登録AP番号 SSID パスワード [ON]]
  • パラメータなしで実行すると、AP リストを表示します。
  • 登録AP番号は 1~3。SSID と パスワードは平文で入力する事になるので、セキュリティに注意。
  • 4 番目のパラメータに ON (大文字/小文字の区別なし) を指定すると、登録した AP がアクティブになり Wi-Fi リスタート。
  • 現在の登録 AP の内容が変更されると Wi-Fi リスタート。
  • SSID に - を入力すると、登録 AP を空にできます。
  • 一度登録した AP を未登録状態にはできない。環境設定ファイルを直接いじる必要がある。

Download: M0SetAP_110.zip (ht-deko.com)

■ M0CHGAP

M0CHGAP: Wi-Fi の接続先を変更する。

image.png

Usage
Usage: M0CHGAP [登録AP番号]
  • パラメータなしで実行すると、登録APリストを表示します。
  • 登録AP番号を指定するとその AP に切り替わり、Wi-Fi リスタート。
  • 0 で OFF です。

Download: M0ChgAP_110.zip (ht-deko.com)

■ M0I2CSCN

M0I2CSCN: I2C スキャナ。

image.png

Usage
Usage: M0I2CSCN
  • I2C ノードのデバイスを検索し、その ID を表示します。
  • 内部接続 (Internal) は device/i2c_i にリストされます。
  • 充電用のポゴピンに出ているのも内部接続用の I2C バスです。
  • Port_A 接続 は device/i2c_a にリストされます。

Download: M0I2CScn_100.zip (ht-deko.com)

■ M0VER

M0VER: バージョン情報。

image.png
image.png

Usage
Usage: M0VER
  • MSX0 に関するバージョンを表示します。
  • MSX-DOS1 では MSX-DOS に関する詳しいバージョン情報を取得できません。

Download: M0Ver_100.zip (ht-deko.com)

■ M0CHGINP

M0CHGINP: 入力デバイスの変更

image.png
image.png

Usage
Usage: M0ChgInp [/Gnum] [/Pnum] [/Jnum] [/Anum] [/Bnum]

Setup Utility の Input と同等の設定を行います。

スイッチ 説明
/Gn GamePad Face の接続先を指定します (n=0..3)。
/Pn タッチパネルの接続先を指定します (n=0..3)。
/Jn タッチジョイパッドの接続先を指定します (n=0..3)。

接続先として設定可能な値です。

n の値 説明
0 割り当てない
1 キーボード
2 ジョイスティックポート #1
3 ジョイスティックポート #2

ゲームパッドの連射に関する設定です。

スイッチ 説明
/An A ボタンの連射速度を変更する (n=0..3)。
/Bn B ボタンの連射速度を変更する (n=0..3)。

連射速度として設定可能な値です。

n の値 説明
0 連射なし
1 低速
2 中速
3 高速
  • パラメータ未指定の場合には使い方と現在の設定を表示します。
  • GamePad / TouchPad / TouchJoypad の接続先とジョイパッドのボタンの連射速度を変更可能です。
  • スイッチは - でも / でもよく、大文字/小文字も判断しません。
  • 範囲外の値は 0 とみなされます。
  • 無効なスイッチは無視されます。

Download: M0ChgInp_100.zip (ht-deko.com)

■ M0TPASZ

TPA サイズを表示。

image.png

Usage
Usage: M0TPASZ
  • TPA サイズを表示します。
  • MSX 固有の機能を使っていないので、恐らく CP/M-80 で動作します。
  • コンパイラオプションで 40K システムをターゲットにしました。MSX0 (MSX-DOS2) よりメモリ搭載量の少ないシステムでも動作すると思います。

Download: M0TPAsz_110.zip (ht-deko.com)

おわりに

そのうち整理するっ!

See also:

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3