【MSX0】DHT_KNJ.PAS【Turbo Pascal】

# はじめに
『MSX0 Stack』付属の IoT BASIC サンプルを『Turbo Pascal』へ移植してみます。

 - [【MSX0】IoT BASIC サンプルの移植【Turbo Pascal】(Qiita)](https://qiita.com/ht_deko/items/2362488c5c99aec4afbf)


# DHT_KNJ.BAS
◆温度湿度センサ

| 項目 | 説明 |
|:---|:---|
| 概要 | 温度湿度センサから取得した値を表示します。 |
| ファイル名 | DHT_KNJ.BAS |
| 対応デバイス | DHT11 (水色)<br>DHT20 (黒色)|
| コメント | MSX0 起動時に Port A に DHT20 が接続されていれば DHT20 を利用します。そうでなければ Port C に DHT11 が接続されている想定で動作します。 |

:::note warn
このサンプルを動作させるには外部センサーが必要です。
:::

**See also:**

 - [『MSX0 Stack』の操作方法 - ハードウェア (Qiita)](https://qiita.com/ht_deko/items/cdb1594e9879a5e9e660)
 - [Grove Beginner Kit for Arduino (Switch Science)](https://www.switch-science.com/products/6361)

## MSX0 Stack での挙動

まず事前にセンサーをつないでおきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/7422a9be-8cd6-3dd3-cef9-2ecaa4e817b1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/62f448b7-f814-57be-061a-9a9014eab192.png)

右中央のセンサーが温湿度センサーなのですが、 [『Grove Beginner Kit for Arduino』](https://www.seeedstudio.com/Grove-Beginner-Kit-for-Arduino-p-4549.html)の製造ロットによって、センサーの種類と MSX0 への接続先が異なります。具体的には 2022/04 以降製造分に DHT20 が付属するようです。

| センサー | 接続方法 | Grove ポート |
|:---|:---|:---|
| [D3] DHT11 (水色) | Digital | Port C (水色) |
| [I<sup>2</sup>C] DHT20 (黒)| I<SUP>2</SUP>C  | Port A (赤) |

:::note
『Grove Beginner Kit for MSX0』には DHT20 が付属します。
:::


`DHT_KNJ.BAS` をロードして、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/8c865c5c-a65a-36e1-0412-e1c1c5a43813.png)

実行してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/cf9dc38c-4ab2-4ecd-7e7a-8816946a98ba.png)

:::note info
プログラムは〔Ctrl〕+〔Stop〕(リモートコントロールパネルからは〔Ctrl〕+〔F12〕)で中断できます。
:::

**See also:**

 - [Grove Beginner Kit for Arduinoの温湿度センサーがアップデート (Seeed K.K. エンジニアブログ)](https://lab.seeed.co.jp/entry/2022/12/26/120000)

## Turbo Pascal へ移植

別途、MDL-LIB と `SYSUTILS.LIB`、`IOT.LIB` が必要です。

 - [MDL-LIB version 2.2 の使い方 (Qiita)](https://qiita.com/ht_deko/items/6f15aee0bd25b550e4df)
 - [【MSX0】IOT.LIB (Qiita)](https://qiita.com/ht_deko/items/a5c0bf6e7969093beb3c)

### DHT_ANK.PAS

漢字はおいといて、とりあえず ANK で表示できるものを作ってみましょう。

```pascal:DHT_ANK.PAS
program DHT_ANK;
{$I MDLLIB.LIB}
{$I SYSUTILS.LIB}
{$I IOT.LIB}
type
  Str8 = string[8];
var
  A: Integer;
  function GetTime: Str8;
  var
    i: byte;
    s: Str8;
  begin
    s := '';
    for i:=0 to 5 do
    begin
      if i in [2, 4] then
        s := ':' + s;
      LibRegs.C := i;
      SubROM($01F5);
      s := Chr($30 + LibRegs.A) + s;
    end;
    GetTime := s;
  end;
begin
  ClrScr;
  while not KeyPressed do
  begin
    GotoXY(1, 4);
    Writeln('TIME : ', GetTime);
    A := IoTGetInt('device/dht/humidity');
    Writeln('HUM. : ', A, '%');
    A := IoTGetInt('device/dht/temperature');
    Writeln('TEMP.: ', A, #$DF'C');
    Delay(100);
  end;
end.
```

実行してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/b8802f50-8d9d-b07a-9ae8-0fcab8120970.png)


センサーが正しく接続されていれば温湿度で値が変動します。

:::note info
任意のキーで中断できます。
:::

### 解説 (DHT_ANK.PAS)

#### GetTime()
MDL-LIB には時刻を持ってくるルーチンが存在しないため、`SubROM()` で CLOCK-IC にアクセスし、値を読み出しています。後日、日付時刻用のライブラリを作るかもしれません。

#### GotoXY()

`GotoXY()` は 1 オリジンなので、X / Y 座標が +1 されています。

#### #$DF

文字 `0xDF` は Shift_JIS の半角カナ半濁点 (`ﾟ`) です。CP/M-80 版の Turbo Pascal は 8bit 文字が通らないので、ソースコード中にカナや漢字は使えません [^2]。ですが、`#$` を使った制御文字で記述する事は可能です。

**See also:**

 - [6章 CLOCKとバッテリバックアップ・メモリ - 6部 標準周辺機器のアクセス (MSX Datapack wiki化計画)](http://ngs.no.coocan.jp/doc/wiki.cgi/datapack?page=6%BE%CF+CLOCK%A4%C8%A5%D0%A5%C3%A5%C6%A5%EA%A5%D0%A5%C3%A5%AF%A5%A2%A5%C3%A5%D7%A1%A6%A5%E1%A5%E2%A5%EA)
 - [8bit 文字 (CP/M 80 版) - Turbo Pascal 3.0 の使い方 (Qiita)](https://qiita.com/ht_deko/items/ec212f5cc17cbe5f718b#8bit-%E6%96%87%E5%AD%97-cpm-80-%E7%89%88)

### DHT_KNJ.PAS

センサー部分は動いたので、漢字表示にチャレンジしてみます。

BASIC プログラム中の `CALL KANJI` と `CALL ANK` は `KMODE` 相当なので、漢字ドライバの拡張 BIOS をコールすれば実現できます。別途、`EXTBIOS.LIB` が必要です。

 - [MDL-LIB version 2.2 の使い方 (Qiita)](https://qiita.com/ht_deko/items/6f15aee0bd25b550e4df)

:::note info
・BASIC で初回実行時の `CALL KANJI` は漢字ドライバの読み込み＆漢字モード変更です。
・BASIC で２回目以降実行時の `CALL KANJI` や `CALL ANK` は漢字モード変更です。
・MSX-DOS2 での `KMODE` 実行は漢字モード変更です。漢字ドライバの読み込みを行うコマンドは存在しません。
:::

漢字ドライバが組み込まれると TPA (Transient Program Area) が減少するのですが、デフォルトのままコンパイルすると、TPA が減少する前の情報を参照してしまうため、漢字モードで実行形式ファイル (`*.COM`) を実行するとメモリ不足のエラーになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/a5c84405-2239-e98a-b746-2c7e5d71978a.png)

BASIC で事前に `CALL KANJI` を実行しなかった場合には、コンパイラオプションで `End Address` を **CF42** 以下にすればメモリ不足にはなりません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/ce08804b-e203-0efe-0638-5cbd5f5901ae.png)


```pascal:DHT_KNJ.PAS
program DHT_KNJ;
{$I MDLLIB.LIB}
{$I EXTBIOS.LIB}
{$I SYSUTILS.LIB}
{$I IOT.LIB}
type
  Str8 = string[8];
var
  A: Integer;
  C, K: Byte;
  function GetTime: Str8;
  var
    i: byte;
    s: Str8;
  begin
    s := '';
    for i:=0 to 5 do
    begin
      if i in [2, 4] then
        s := ':' + s;
      LibRegs.C := i;
      SubROM($01F5);
      s := Chr($30 + LibRegs.A) + s;
    end;
    GetTime := s;
  end;
begin
  C := sys_textwidth;
  K := GetKMode;
  SetKMode(1);
  sys_textwidth := 26;
  ClrScr;
  while not KeyPressed do
  begin
    GotoXY(1, 4);
    Writeln(#$8E#$9E#$8D#$8F#$81#$40#$81#$40, GetTime);
    Writeln;
    A := IoTGetInt('device/dht/humidity');
    Writeln(#$8E#$BC#$93#$78#$81#$40#$81#$40, A, ' '#$81#$93);
    Writeln;
    A := IoTGetInt('device/dht/temperature');
    Writeln(#$89#$B7#$93#$78#$81#$40#$81#$40, A, ' '#$81#$8E);
    Writeln;
    Delay(100);
  end;
  SetKMode(K);
  sys_textwidth := C;
end.
```

実行してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/ab352249-119c-ee8c-d692-99a0e0b8d93d.png)


### 解説 (DHT_KNJ.PAS)

#### GetKMode() / SetKMode()
`EXTBIOS.LIB` にある拡張 BIOS をコールして現在の漢字モードを取得/設定するルーチンです。プログラム開始時に漢字モードを取得して、終了時に戻しています。

#### 一行文字数
漢字モード同様、プログラム開始時に一行文字数を取得して、終了時に戻しています。

**See also:**

 - [6章 内部ルーチン - 2部 システムソフトウェア (MSX Datapack wiki化計画)](http://ngs.no.coocan.jp/doc/wiki.cgi/datapack?page=6%BE%CF+%C6%E2%C9%F4%A5%EB%A1%BC%A5%C1%A5%F3)


### ファームウエアバージョン 0.15.20 以降への対応

ファームウエアバージョン 0.15.20 以降では `device/dht/humidity` や `device/dht/temperature` の返す値が**実数** (BASIC が使う BCD) となっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/e6a56523-2217-458e-a31f-dc27fdd1e4f3.png)

`IOT.LIB` に `IoTGetReal()` 関数が追加されていますので、これを使うように書き替えます。

#### DHT_ANK.PAS

```pascal:DHT_ANK.PAS
program DHT_ANK;
{ ver0.15.20 or later }
{$I MDLLIB.LIB}
{$I SYSUTILS.LIB}
{$I IOT.LIB}
type
  Str8 = string[8];
var
  A: Real;
  function GetTime: Str8;
  var
    i: byte;
    s: Str8;
  begin
    s := '';
    for i:=0 to 5 do
    begin
      if i in [2, 4] then
        s := ':' + s;
      LibRegs.C := i;
      SubROM($01F5);
      s := Chr($30 + LibRegs.A) + s;
    end;
    GetTime := s;
  end;
begin
  ClrScr;
  while not KeyPressed do
  begin
    GotoXY(1, 4);
    Writeln('TIME : ', GetTime);
    A := IoTGetReal('device/dht/humidity');
    Writeln('HUM. : ', A:3:1, '%');
    A := IoTGetReal('device/dht/temperature');
    Writeln('TEMP.: ', A:3:1, #$DF'C');
    Delay(100);
  end;
end.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/87dfb7bc-84db-7679-61e4-c1277a20f0e4.png)

#### DHT_KNJ.PAS

```pascal:DHT_KNJ.PAS
program DHT_KNJ;
{ ver0.15.20 or later }
{$I MDLLIB.LIB}
{$I EXTBIOS.LIB}
{$I SYSUTILS.LIB}
{$I IOT.LIB}
type
  Str8 = string[8];
var
  A: Real;
  C, K: Byte;
  function GetTime: Str8;
  var
    i: byte;
    s: Str8;
  begin
    s := '';
    for i:=0 to 5 do
    begin
      if i in [2, 4] then
        s := ':' + s;
      LibRegs.C := i;
      SubROM($01F5);
      s := Chr($30 + LibRegs.A) + s;
    end;
    GetTime := s;
  end;
begin
  C := sys_textwidth;
  K := GetKMode;
  SetKMode(1);
  sys_textwidth := 26;
  ClrScr;
  while not KeyPressed do
  begin
    GotoXY(1, 4);
    Writeln(#$8E#$9E#$8D#$8F#$81#$40#$81#$40, GetTime);
    Writeln;
    A := IoTGetReal('device/dht/humidity');
    Writeln(#$8E#$BC#$93#$78#$81#$40#$81#$40, A:3:1, ' '#$81#$93);
    Writeln;
    A := IoTGetReal('device/dht/temperature');
    Writeln(#$89#$B7#$93#$78#$81#$40#$81#$40, A:3:1, ' '#$81#$8E);
    Writeln;
    Delay(100);
  end;
  SetKMode(K);
  sys_textwidth := C;
end.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/fd7eb8b6-eb75-786d-1cbb-3df1d801cb2b.png)



# おわりに
漢字モードについてはもう少し調査が必要かも。最悪、バッチファイルでどうにかできるからいいかな (^^;A

[^1]: ディスクを日本語 MSX-DOS2 専用とする事で起動時に漢字ドライバを読み込ませた状態にする事は可能です (Ex. `KMODE OFF /S`)。
[^2]: [`TURBO.COM` にパッチすれば、8bit 文字が通るようになります。](https://qiita.com/ht_deko/items/ec212f5cc17cbe5f718b#8bit-%E6%96%87%E5%AD%97-cpm-80-%E7%89%88)