MDL-LIB version 2.2 の使い方 

# はじめに
『**MSX0 Stack**』楽しいですね。数十年ぶりに MSX 再入門を果たしました。

 - [MSX0 Stackで伝説の 8 ビット MSX パソコンが甦り、IoT 用コンピュータに (CAMPFIRE)](https://camp-fire.jp/projects/view/648742)

CP/M-80 用『**Turbo Pascal 3.0**』が MSX で使えるのは解って狂喜したのですが、イロイロ触っていると MSX 固有の機能を使うためのライブラリが欲しくなってきます。

MSX-DOS の BDOS は CP/M-80 互換なので `Bdos()` で簡単に呼び出せますし、ワークエリアは `mem[アドレス]` (や `absolute`) で参照できるのですが、肝心の BIOS の機能はインタースロットコールを使わないと呼び出せないので、少々面倒です。折角の Pascal なのに **inline** 文 (インライン機械語) をゴリゴリ書きたくはないですしね…。

驚いたことに、Turbo Pascal 用の MSX ライブラリはいくつも存在するようです。

# MDL-LIB version 2.2
**『MDL-LIB version 2.2』** は Martijn Dekker 氏によって作られた『Turbo Pascal 3.0』用 MSX ライブラリです (パブリックドメイン)。すべての BDOS や BIOS が網羅されているわけではありませんが、BDOS や BIOS をあまり意識する事なく MSX の固有機能が使えます。

 - [MDL-LIB 2.2 Pascal Bios include files (hansotten.com)](http://pascal.hansotten.com/delphi/turbo-pascal-on-cpm-msx-dos-and-ms-dos/)

MDL-LIB はドキュメントやコメントがオランダ語で書かれており、翻訳サイトに通すにしても、そのままテキストエディタで開くと文字化けしてしまうので、ドキュメントを UTF-8 に変換したものを用意しました。ドキュメントは PC で読む前提です (MSX-DOS 上で開かないでください)。

 - [MDL-LIB 2.2 repackage (ht-deko.com)](https://ht-deko.com/software/MDLLIB22_REPACKAGE.ZIP)

| フォルダ | 説明 |
|:---|:---|
| DOC | 各ライブラリのドキュメントです |
| LIB | ライブラリ (インクルードファイル) です。<br>Turbo Pascal と同じフォルダにコピーして使います。 |
| MDLDEMO | MDL-LIB のデモプログラムです |

:::note
`leesmij.txt` は英語で言うと `README.TXT` です。`inleidng.txt` は英語で言うと `INTRO.TXT` といったところでしょうか？
:::

## MDL-LIB ライブラリの概要

MDL-LIB ライブラリはインクルードファイルの形で用意されています。ファイルの拡張子は `*.lib` ですが、中身は (Turbo) Pascal のソースファイルです。

```pascal
program TEST;

{$I MDLLIB.LIB}
{$I VRAM2.LIB}
{$I LOGOROUT.LIB}

begin
  ...
end;
```

`{$I}` コンパイラ指令を用い、インクルードして使います。

:::note warn
先述のように、MDL-LIB はドキュメントやコメントがオランダ語で書かれているのですが、手続き・関数宣言の仮パラメータがオランダ語で書かれている事があります。例えば色を指定するパラメータの識別子として `kleur` が使われています。本記事内では、一瞥して理解できないないようなものは、英語の識別子 (色の例だと `Color`) で置き換えてあります。
:::


### ■ MDLLIB
最初にインクルードすべきライブラリです。他のライブラリで使われるルーチンが含まれています。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| MDLLIB.LIB | |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| Bios() | **procedure** Bios(adres: integer); | BIOS を呼び出す |
| SubRom() | **procedure**  SubRom(adres: integer); | サブ ROM を呼び出す (MSX2 以降)|
| BiosMem() | **function** BiosMem(adres: integer): byte; | サブ ROM の指定アドレスから値を読み出す (MSX2 以降) |
| CalBas() | **procedure** CalBas(adres: integer); | BASIC-ROM を呼び出す |
| WaitForInt() | **procedure** WaitForInt; | 次の割り込みまでプログラムを停止 |

#### 型

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| LibStr | **type** LibStr = string[80]; | 文字列描画用。GRAPMSX が使用 |
| Reg_pack | **type** Reg_pack=**record**<br>&nbsp;&nbsp;**case** Boolean **of**<br>&nbsp;&nbsp;&nbsp;&nbsp;True: AF, BC, DE, HL: integer;<br>&nbsp;&nbsp;&nbsp;&nbsp;False: F, A, C, B, E, D, L, H: byte;<br>**end**; | レジスタを定義したレコード |

#### 変数

ワークエリアにマッピングされたシステム変数 (`sys_～`) が定義されています。

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| LibRegs | **var** LibRegs : reg_pack absolute $0103; | レジスタ変数 |
| sys_textwidth | **var** sys_textwidth: byte absolute $F3B0; | LINLEN: 現在の画面の 1 行の幅 |
| sys_textlines | **var** sys_textlines: byte absolute $F3B1; | CRTCNT: 現在の画面の行数 |
| sys_width0 | **var** sys_width0: byte absolute $F3AE; | LINL40: SCREEN 0 のときの 1 行の幅 |
| sys_width1 | **var** sys_width1: byte absolute $F3AF; | LINL32: SCREEN 1 のときの 1 行の幅 |
| sys_whereX | **var** sys_whereX: byte absolute $F3DD; | CSRX: カーソルの X 座標 |
| sys_whereY | **var** sys_whereY: byte absolute $F3DC; | CSRY: カーソルの Y 座標 |
| sys_timer | **var** sys_timer: integer absolute $FC9E; | JIFFY: PLAY 文が内部で使用する |
| sys_scrmod | **var** sys_scrmod: byte absolute $FCAF; | SCRMOD: 現在のスクリーンモードの番号 |
| sys_forclr | **var** sys_forclr: byte absolute $F3E9; | FORCLR: 前景色 |
| sys_bakclr | **var** sys_bakclr: byte absolute $F3EA; | BAKCLR: 背景色 |
| sys_bdrclr | **var** sys_bdrclr: byte absolute $F3EB; | BDRCLR: 周辺色 (ボーダー色) |
| sys_cursor | **var** sys_cursor: boolean absolute $FCA9; | CSRSW: カーソル表示<br>(0=OFF、0 以外=ON) |
| sys_click | **var** sys_click: :boolean absolute $F3DB; | CLIKSW: キークリックスイッチ<br>(0=OFF、0 以外=ON) |
| sys_gxpos | **var** sys_gxpos: integer absolute $FCB3;| GXPOS: X 座標 |
| sys_gypos | **var** sys_gypos: integer absolute $FCB5;| GYPOS: Y 座標 |
| sys_cx | **var** sys_cx: integer absolute $FCB7;| GRPACX: グラフィックアキュムレータ (X 座標) |
| sys_cy | **var** sys_cy: integer absolute $FCB9;| GRPACY: グラフィックアキュムレータ (Y 座標) | 
| sys_atrbyt| **var** sys_atrby: byte absolute $F3F2;| ATRBYT: グラフィック使用時のカラーコード |
| sys_dppage| **var** sys_dppage: byte absolute $FAF5;| DPPAGE: ディスプレイページ番号 (MSX2 以降)|
| sys_acpage| **var** sys_acpage: byte absolute $FAF6;| ACPAGE: アクティブページ番号 (MSX2 以降)|


### ■ VRAM
VRAM と VDP を操作する基本的なルーチンが含まれています。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| VRAM1.LIB | MDLLIB.LIB |
| VRAM2.LIB (MSX2 以降用) | MDLLIB.LIB |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| ScrMode() | **procedure** ScrMode(mode: byte); | スクリーンモードを指定する |
| TextMode() | **procedure** TextMode; | 最後に使用したテキスト画面に設定する (SCREEN 0 or 1) |
| SetXY() | **procedure** SetXY(x, y: integer); | グラフィックカーソルを (x, y) に設定する|
| SetColor() | **procedure** SetColor(color: byte); | 文字色を設定する |
| Color() | **procedure** Color(forclr, bakclr, bdrclr: integer); | 前景色、背景色、境界色を設定する |
| DefColor() | **procedure** DefColor(color, R, G, B: byte); | 色を再定義する (MSX2 以降) |
| WriteVram() | **procedure** WriteVram(adres: integer; data: byte); | VRAM に 1 バイト書き込む |
| ReadVram() | **function** ReadVram(adres: integer): byte; | VRAM から 1 バイト読み出す |
| WriteVdp() | **procedure** WriteVdp(reg, data: byte); | VDP レジスタに書き込む |
| ReadVdp() | **function** ReadVdp(reg: byte): byte; | VDP レジスタから読み出す|
| ReadStatus() | **function** ReadStatus: byte; | VDP ステータスレジスタから読み出す (MSX1) |
| ReadStatus() | **function** ReadStatus(reg: byte): byte; | VDP ステータスレジスタから読み出す (MSX2 以降) |
| ReadVramPort() | **function** ReadVramPort: byte; | VDP 読み取りポートを返す |
| WriteVramPort() | **function** WriteVramPort: byte; | VDP 書き込みポートを返す |
| InitCharMode() | **procedure** InitCharMode(width, char1, char2: byte); | マルチカラー文字モードを設定する |
| TranspTable() | **function** TranspTable: integer; | VRAM アドレスのスプライトアトリビュートテーブルを返す |
| PattTable() | **function** PattTable: integer; | VRAM アドレスのスプライトパターンジェネレータテーブルを返す |
| ColorTable() | **function** ColorTable: integer; | VRAM アドレスのスプライトカラーテーブルを返す (MSX2 以降)|
| SpriteSize() | **procedure** SpriteSize(size: byte); | スプライトサイズを設定する |
| ClearSprites() | **procedure** ClearSprites; | すべてのスプライトをクリアする |

##### ReadVdp() / ReadStatus() と BASIC の VDP() の関係

| VDP レジスタ | VDP() | Read | Write|
|:---|:---|:---|:---|
| R#0..7 | VDP(0)..(7) | ReadVdp(0)..(7) | WriteVdp(0)..(7) |
| R#8..23 | VDP(9)..(24) | ReadVdp(8)..(23) | WriteVdp(8)..(23) |
| S#0 | VDP(8) | ReadStatus (MSX1)<br>ReadStatus(0) (MSX2 以降) |  |
| S#1..9 | VDP(-1)..(-9) | ReadStatus(1)..(9) (MSX2 以降) |  |

##### 色

| 値 | 色 | 値 | 色 |
|:---:|:---|:---:|:---|
| $00 | □ (透明) | $08 | <font color="#DB6559">■ 赤</font> |
| $01 | <font color="#000000">■ 黒</font> | $09 | <font color="#FF897D">■ 明るい赤</font> |
| $02 | <font color="#3EB849">■ 緑</font> | $0A | <font color="#CCC35E">■ 黄色</font> |
| $03 | <font color="#74D07D">■ 明るい緑</font> | $0B | <font color="#DED087">■ 明るい黄色</font> |
| $04 | <font color="#5955E0">■ 暗い青</font> | $0C | <font color="#3AA241">■ 暗い緑</font> |
| $05 | <font color="#8076F1">■ 明るい青</font> | $0D | <font color="#B766B5">■ 紫</font> |
| $06 | <font color="#B95E51">■ 暗い赤</font> | $0E | <font color="#CCCCCC">■ 灰色</font> |
| $07 | <font color="#65DBEF">■ 水色</font> | $0F | <font color="#000000">□ 白</font> |


##### SpriteSize()

size にはスプライトの大きさを表す値を指定します。

| size | 意味 |
|:---:|:---|
| 0 | 8x8 |
| 1 | 16x16 |
| 2 | 8x8 (縦横 2 倍拡大) |
| 3 | 16x16 (縦横 2 倍拡大) |


#### 変数

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| Base | **var** Base: **array** [0..19] **of** integer absolute $F3B3; | VRAM のテーブル |
| LeftTextPos | **var** LeftTextPos: integer absolute $F9F5; | テキスト位置 |

### ■ GRAPMSX
グラフィック・ルーチンが含まれています。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| GRAPMSX1.LIB | MDLLIB.LIB |
| GRAPMSX2.LIB (MSX2 以降用) | MDLLIB.LIB |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| Draw() | **procedure** Draw(x1, y1, x2, y2, color: integer); | 線を描画する |
| DrawTo() | **procedure** DrawTo(x, y, color: integer); | 最後の座標から線を描画する |
| Plot() | **procedure** Plot(x, y, color: integer); | 点を描画する |
| Ellipse() | **procedure** Ellipse(x, y, radius, color, startangle, endangle: integer; flattening: real); | 楕円を描画する |
| Arc() | **procedure** Arc(x, y, angle, radius, color: integer); | 円弧を描画する |
| Circle() | **procedure** Circle(x, y, radius, color: integer); | 円を描画する
| GetDotColor() | **function** GetDotColor(x, y: integer): integer; | 指定された座標の色を得る |
| Box() | **procedure** Box(x1, y1, x2, y2, color: integer); | 四角形を描画する  |
| FillBox() | **procedure** FillBox(x1, y1, x2, y2, color: integer); | 四角形を塗りつぶす |
| Logical() | **procedure** Logical(opr: byte); | 描画の際のロジカルオペレーションを指定する (MSX2 以降) |
| FillScreen() | **procedure** FillScreen(color: integer); | 画面全体を塗りつぶす |
| Pattern() | **procedure** Pattern(p : Pattern_Type); | 塗りつぶしパターンを指定する |
| FillPattern() | **procedure** FillPattern(x1, y1, x2, y2, color: integer); | 領域をパターンで塗りつぶす |
| Gwrite() | **procedure** Gwrite(x, y: integer; txt: libstr); | グラフィックス画面にテキストを書き込む |
| SetPage() | **procedure** SetPage(projection, work: byte); | 画面ページを設定する (MSX2 以降)|
| GetPic() | **procedure** GetPic(x1, y1, x2, y2: integer; **var** buffer); | 領域の画像を変数にコピーする (MSX2 以降) |
| PutPic() | **procedure** PutPic(**var** buffer; x, y: integer); | 変数に格納された画像を指定座標に描画する (MSX2 以降) |
| CopyPic() | **procedure** CopyPic(x1, y1, x2, y2, sourcepage, xd, yd, destpage:integer); | 指定ページの領域の画像を転送先ページの指定座標に描画する (MSX2 以降) |
| Gml() | **procedure** Gml(commands: libstr); | 任意の図形を描く (グラフィックマクロ言語)|

##### Logical() 
BASIC のものと同じ定数は用意されていないため、値で指定するか、定数を独自に定義します。

| BASIC | 値 | 説明 | 
|:---|:---|:---|
| PSET  | 0  | 転送する色がそのままの色で表示される。 |
| AND   | 1  | 転送する色のカラーコードと転送先の色のカラーコードとの間で論理積を行う。 |
| OR    | 2  | 転送する色のカラーコードと転送先の色のカラーコードとの間で論理和を行う。 |
| XOR   | 3  | 転送する色のカラーコードと転送先の色のカラーコードとの間で排他的論理和を行う。 |
| PRESET<br>(NOT) | 4  | 転送する色のカラーコードの各桁の 0 と 1 を反転させる。転送先のカラーコードは関係しない。 |
| TPSET | 8  | 転送する色が、そのままの色で表示される。転送する色に透明があれば透明になる。 |
| TAND  | 9  | 転送する色のカラーコードと転送先の色のカラーコードとの間で論理積を行う。転送する色に透明があれば透明になる。 |
| TOR   | 10 | 転送する色のカラーコードと転送先の色のカラーコードとの間で論理和を行う。転送する色に透明があれば透明になる。 |
| TXOR  | 11 | 転送する色のカラーコードと転送先の色のカラーコードとの間で排他的論理和を行う。転送する色に透明があれば透明になる。 |
| TPRESET<br>(TNOT) | 12 | 転送する色のカラーコードの各桁の 0 と 1 を反転させる。転送先のカラーコードは関係しない。転送する色に透明があれば透明になる。 |

#### 型

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| Pattern_Type | **type** Pattern_Type = **array** [0..7] **of** byte; | 塗りつぶしパターンを格納する |


### ■ LOGOROUT
LOGO のタートルグラフィックスルーチンが含まれています。

 - [LOGO (Wikipedia)](https://ja.wikipedia.org/wiki/LOGO)

| ファイル名 | 依存ライブラリ |
|:---|:---|
| LOGOROUT.LIB | MDLLIB.LIB, VRAM1 (または VRAM2) |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| InitLogo() | **procedure** InitLogo; | LOGO ルーチンを初期化する |
| ClearScreen() | **procedure** ClearScreen; | 画面を消去し `Home()` を実行する |
| ShowTurtle() | **procedure** ShowTurtle; | タートルを表示する |
| HideTurtle() | **procedure** HideTurtle; | タートルを非表示にする |
| TurtleThere() | **function** TurtleThere: boolean; | タートルが画面内にいるかを返す |
| TurtleDelay() | **procedure** TurtleDelay(millisec: integer); | 指定ミリ秒待つ |
| SetHeading() | **procedure** SetHeading(angle: integer); | タートルの向きを設定する (0..359) |
| Heading() | **function** Heading: integer; | タートルの向きを返す (0..359) |
| TurnLeft() | **procedure** TurnLeft(angle: integer); | タートルを反時計回りに angle の角度だけ回転させる |
| TurnRight() | **procedure** TurnRight(angle: integer); | タートルを時計回りに angle の角度だけ回転させる |
| Home() | **procedure** Home; | タートルをホームポジションに移動し、北を向かせる |
| SetPosition() | **procedure** SetPosition(x, y: integer); | タートルの位置を設定する |
| PenDown() | **procedure** PenDown; | ペンを下げる |
| PenUp() | **procedure** PenUp; | ペンを上げる|
| SetPenColor() | **procedure** SetPenColor(color: integer); | ペンの色を変える (色は MSX カラーコード) |
| ForWd() | **procedure** ForWd(dist: integer); | タートルを dist の分だけ前進させる |
| Back() | **procedure** Back(dist: integer); | タートルを dist の分だけ後退させる |

#### 定数

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| north | **const** north = 0; | タートルの向き: 北 |
| east | **const** east = 90; | タートルの向き: 東 |
| south | **const** south = 180; | タートルの向き: 南 |
| west | **const** west = 270; | タートルの向き: 西 |

#### 変数

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| Correction | **var** correction: real; | ドットの縦横比を考慮するための補正値 (デフォルト = 1) |


### ■ GAMEIO
ジョイスティック、パドル、タッチパッド、マウス (MSX2)、ライトペン (MSX2) 用のルーチンが含まれています。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| GAMEIO.LIB | MDLLIB.LIB |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| Pad() | **function** Pad(ID: byte): byte; | コントローラの状態を返す |
| Strig() | **function** Strig(ID: byte): boolean; | コントローラのボタンの状態を返す |
| Stick() | **function** Stick(ID: byte): byte; | コントローラの向きの状態を返す |


##### PAD()

・グラフィックタブレット/タッチパッド

| ポート #1 | ポート #2| 説明 | 
|:---|:---|:---|
| 0 | 4 | タッチパッドに触れていれば -1、触れていなければ 0 |
| 1 | 5 | タッチパッドに触れた時の X 座標 |
| 2 | 6 | タッチパッドに触れた時の Y 座標 |
| 3 | 7 | タッチパッドのスイッチが押されていれば -1、押されていなければ 0|

・マウス/トラックボール

| ポート #1 | ポート #2| 説明 | 
|:---|:---|:---|
| 12 | 16 | マウス/トラックボールに対する入力要求 (常に -1 を返す)|
| 13 | 17 | マウス/トラックボールの X 座標 |
| 14 | 18 | マウス/トラックボールの Y 座標 |
| 15 | 19 | 予約 (常に 0 を返す)|

・ライトペン

| MSX1 内蔵<br>または外部 | MSX2 内蔵| 説明 | 
|:---|:---|:---|
| 8 | 20 | ライトペンの準備ができていれば -1、できていなければ 0|
| 9 | 21 | ライトペンの X 座標 |
| 10 | 22 | ライトペンの Y 座標 |
| 11 | 23 | ライトペンのスイッチが押されていれば -1、押されていなければ 0|

##### Strig()

・パラメータ

| ポート #1 | ポート #2| 説明 | 
|:---|:---|:---|
| 1 | 2 | コントローラーのボタン 1 |
| 3 | 4 | コントローラーのボタン 2 |

※ 0 はスペースキーの状態を返す。

・結果
コントローラーのボタンが押されていれば -1 を、押されていなければ 0 を返す。


##### Stick()
・パラメータ

| ID |  説明 | 
|:---|:---|
| 0 | カーソルキー |
| 1 | ポート #1 のコントローラー |
| 2 | ポート #2 のコントローラー |

・結果

| 結果 |  説明 | 
|:---|:---|
| 0 | ニュートラル |
| 1 | 上 |
| 2 | 右上 |
| 3 | 右 |
| 4 | 右下 |
| 5 | 下 |
| 6 | 左下 |
| 7 | 左 |
| 8 | 左上 |

:::note
`GAME` というより、入力装置関連ですね。
:::


### ■ SOUNDS
サウンド用のルーチンが含まれています。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| SOUNDS.LIB | MDLLIB.LIB |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| WritePsg() | **procedure** WritePsg(reg, data: byte); | PSG レジスタに書き込む |
| ReadPsg() | **function** ReadPsg(reg: byte): byte; | PSG レジスタから読み出す |
| Tone() | **procedure** Tone(channel: byte; Hz: integer); | トーンを設定する (channel = 0..2, Hz >= 28) |
| Noise() | **procedure** Noise(height: byte); | ノイズを設定する (0..31)|
| Volume() | **procedure** Volume(channel, vol: byte); | 音量を設定 (channel = 0..2, vol = 0..15) |
| Effect() | **procedure** Effect(channels: SetOfChannel; Num: byte; time: integer); | チャンネルにエフェクトを設定 |
| SwitchSound() | **procedure** SwitchSound(tchannels, nchannels: SetOfChannel); | トーンとノイズをチャンネル毎に ON/OFF する |
| ResetPsg() | **procedure** ResetPsg; | PSG をリセットする |
| SetClick() | **procedure** SetClick(status: boolean); | 1 ビット I/O ポートのステータスを変更する |


##### Effect()
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/e1ad2356-6161-db4b-14e2-4f2580a7c7b5.png)

#### 型

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| SetOfChannel | **type** SetOfChannel = **set** **of** 0..2; | チャンネルの集合 |

### ■ DISKTOOL
ディスク操作用のルーチンが含まれています。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| DISKTOOL.LIB | MDLLIB.LIB |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| MsxBdos() | **procedure** MsxBdos; | MSX-BDOS を呼び出す |
| SetDrive() | **procedure** SetDrive(drive: byte); | ドライブを設定する (0=カレントドライブ) |
| LoggedDrive() | **procedure** LoggedDrive: byte; | カレントドライブ番号を返す |
| ReadSector() | **procedure** ReadSector(drive: byte; start: integer; num: byte; **var** sectoren); | ディスクからセクタを読み出す |
| WriteSector() | **procedure** WriteSector(drive: byte; start: integer; num: byte; **var** sectoren); |  セクタをディスクに書き込む |
| SearchFirst() | **function** SearchFirst(**var** FCB; **var** info): boolean; | 最初のファイルを検索する |
| SearchNext() | **function** SearchNext(**var** info): boolean; | 次のファイルを検索する |
| Dskf() | **procedure** Dskf(drive: byte): integer; | 指定したドライブ番号の空きディスク容量を KB 単位で返す |

### ■ TAPE
カセットレコーダー (CMT) 操作用のルーチンが含まれています。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| TAPE.LIB | MDLLIB.LIB |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| TapeRead() | **procedure** TapeRead(**var** buffer; len: integer; **var** success: boolean); | カセットからブロックを読み出す |
| Motor() | **procedure** Motor(status: byte); | カセットのモーターを制御する (0=停止, 1=巻き戻し, 255=モーター状態の反転) |
| Baud() | **procedure** Baud(rate: integer); | カセットのボーレートを設定する (600 刻みで 600..3600) |
| DriveOff() | **procedure** DriveOff; |  |
| OpenCasOutput() | **procedure** OpenCasOutput(name: libstr; **var** success: boolean); | 出力用に ASCII ファイルを開く |
| WriteToCas() | **procedure** WriteToCas(wrstr: libstr; **var** success: boolean); | ASCII ファイルに書き込む |
| CloseCasOutput() | **procedure** CloseCasOutput(**var** success: boolean); | 出力 ASCII ファイルを閉じる |
| OpenCasInput() | **procedure** OpenCasInput(**var** name: libstr; **var** success: boolean); | 入力用に ASCII ファイルを開く |
| ReadFromCas() | **procedure** ReadFromCas(**var** rdstr: libstr; **var** success: boolean; **var** eof: boolean); | ASCII ファイルから読み出す |

カセットからファイルを検索する例です。

```pascal
procedure FindFile(name: libstr; var success: boolean);
{ 'name' という名前の ASCII ファイルをテープから検索します。
　　見つかった場合は自動的に開きます。}
var 
  found_name: libstr;
begin
  repeat
    OpenCasInput(found_name, success);
    if success then
      if found_name <> name then
        write('Skip :') 
      else 
        write('Found:');
    writeLn(found_name);
  until (found_name = name) or (not success);
end;
```

:::note
入力用 ASCII ファイルを閉じる必要はありません。
:::

### ■ DATA
補助変数なしで 2 つの変数の内容を交換する `VarSwap()` と、配列を高速にソートできる `Sort()` が含まれています。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| DATA.LIB | MDLLIB.LIB |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| VarSwap() | **procedure** VarSwap(**var** var1; **var** var2; size: integer); | 2 つの変数の内容を交換する |
| Sort() | **procedure** Sort(**var** data; NumOfRecords, size, funcaddr: integer); | 配列を高速にソートする |


`VarSwap()` の使用例を次に示します。

```pascal
var a, b: real;
VarSwap(a, b, SizeOf(real));
```

`Sort()` の使用例を次に示します。

```pascal
var 
  tabel: array [1..100] of real;
  function Compare_real(var var1, var2: real): boolean;
  begin
    Compare_real := (var1 >= var2)
  end; { Compare_real }
begin
  ...

  Sort(tabel, 100, SizeOf(real), Addr(Compare_real));

  ...
end.
```

### ■ MISC
その他の分類しにくいルーチンです。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| MISC.LIB | MDLLIB.LIB |

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| MSXversion() | **function** MSXversion: byte; | MSX のバージョンを返す |
| Beep() | **procedure** Beep; | Beep を鳴らす |
| OnLine() | **function** OnLine: boolean; | プリンタがオンラインかどうかを返す |
| CtrlStop() | **function** CtrlStop: boolean; | 〔Ctrl〕+〔Stop〕 が押されているかどうかを返す |
| Key() | **procedure** Key(st: boolean); | ファンクションキーの表示を設定する |
| DefKey() | **procedure** DefKey(number: integer; text: LibStr); | ファンクションキーにテキストを割り当てる |
| ShowPrompt() | **procedure** ShowPrompt; | BASIC のプロンプトを表示する (MSX2 以降)|
| ResetScreen() | **procedure** ResetScreen; | BASIC の SET SCREEN で設定した値を元に画面をリセットする (MSX2 以降) |
| ClrEos() | **procedure** ClrEos; | カーソル位置から画面下までをクリアします |
| ClrY() | **procedure** ClrY(y1, y2: byte); | y1 行から y2 行までをクリアします |
| KillBuffer() | **procedure** KillBuffer; | キーボードバッファをクリアします |
| Screen() | **procedure** Screen(st: boolean); | 画面を ON/OFF します |
| SetAdjust() | **procedure** SetAdjust(x, y: integer); | 画面位置を調整します|
| FormatNumber() | **function** FormatNumber(number: real; commas, asterisk, dollar, plus, sign, exp: boolean; digits, decimals: byte): libStr; | 数値をフォーマットした文字列で返す |


##### MSXversion()

定数は用意されていません。

| 機種 | 値 |
|:---|:---|
| MSX1  | 1  |
| MSX2  | 2  |
| MSX2+  | 3  |
| MSXturboR  | 4  | 

## 追加のライブラリ

独自に追加した MDL-LIB 用のライブラリです。

### ■ MSXUTILS

個人的に必要そうなものを `MSXUTILS.LIB` として別途定義します。アーカイブには含まれていません。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| MSXUTILS.LIB | MDLLIB.LIB |

```pascal:MSXUTILS.LIB
(*
    MSXUTILS.LIB
*)

type
  TQCB = record
    POffset: byte;
    GOffset: byte;
    Backup: byte;
    QueLen: byte;
    QueAddr: integer;
  end;

const
  QCBInit: TQCB = (POffset:$ff;GOffset:0;Backup:0;QueLen:$ff;QueAddr:0);
  NullPlayData: array [0..1] of byte = ($00, $ff);

var
  sys_quetab: array [0..3] of TQCB absolute $F959;
  sys_musicf: byte absolute $FB3F;
  sys_plycnt: byte absolute $FB40;
  sys_oldkey: array [0..10] of byte absolute $FBDA;
  sys_newkey: array [0..10] of byte absolute $FBE5;
  sys_kanamd: byte absolute $FCAD; { Key layout }

{ Play MML Intermediate Code }

procedure Play(Cnt: byte);
begin
  sys_plycnt := Cnt;
  Bios($0099);
end;

{ Sense the key matrix }

function SenseKB(row: byte): byte;
begin
  libregs.A := row;
  Bios($0141);
  SenseKB := libregs.A
end;
```

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| SenseKB() | **function** SenseKB(row: byte): byte; | キースキャンを行う |
| Play() | **procedure** Play(Cnt: byte); | MML 中間形式データを再生する |

#### 型

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| TQCB | **type**<br>&nbsp;&nbsp;TQCB = **record**<br>&nbsp;&nbsp;&nbsp;&nbsp;POffset: byte;<br>&nbsp;&nbsp;&nbsp;&nbsp;GOffset: byte;<br>&nbsp;&nbsp;&nbsp;&nbsp;Backup: Byte;<br>&nbsp;&nbsp;&nbsp;&nbsp;QueLen: Byte;<br>&nbsp;&nbsp;&nbsp;&nbsp;QueAddr: Integer;<br>**end**; | キューコントロールブロックレコード |


#### 定数
| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| QCBInit | **const** QCBInit: TQCB = (POffset:\\$ff;GOffset:0;Backup:0;QueLen:\\$ff;QueAddr:0); | QCB レコード初期化用定数 |
| NullPlayData | **const** NullPlayData: array [0..1] of byte = (\\$00, \\$ff); | 空の再生データ |


#### 変数

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| sys_quetab | **var** sys_quetab: **array** [0..3] **of** TQCB absolute $F959; | QUETAB: キューテーブル|
| sys_musicf | **var** sys_musicf: byte absolute $FB3F; | KANAMD: 音楽演奏用の割り込みフラグ|
| sys_plycnt | **var** sys_plycnt: byte absolute $FB40; | MUSICF: キューされている PLAY 文の数|
| sys_oldkey | **var** sys_oldkey: **array** [0..10] **of** byte absolute $FBDA; | OLDKEY: 旧キーの状態|
| sys_newkey | **var** sys_newkey: **array** [0..10] **of** byte absolute $FBE5; | NEWKEY:  新キーの状態|
| sys_kanamd | **var** sys_kanamd: byte absolute $FCAD; | KANAMD: キー配列<br>(0=五十音配列, 0以外=JIS配列)|


#### サンプル

ドレミを鳴らします。`sys_musicf` を見て再生中でなければキューテーブルに再生データ情報を書き込んで `Play()` で鳴らします。再生データは一つだけなのでパラメータに 1 を渡しています。

```pascal:PLAYTEST.PAS
program PLAYTEST;

{$I MDLLIB.LIB}
{$I MSXUTILS.LIB}

const
  PlayData: array [0..16] of byte =
    ($00,
     $60,$1e,$88,$01,$ac,
     $60,$1e,$88,$01,$7d,
     $60,$1e,$88,$01,$53,
     $ff
    );
begin
  while sys_musicf <> 0 do;

  sys_quetab[0] := QCBinit;
  sys_quetab[0].QueAddr := Addr(PlayData);
  sys_quetab[1] := QCBinit;
  sys_quetab[1].QueAddr := Addr(NullPlayData);
  sys_quetab[2] := QCBinit;
  sys_quetab[2].QueAddr := Addr(NullPlayData);

  Play(1);
end.
```

:::note warn
MSXUTILS.LIB は予告なく変更になる事があります。
:::

### ■ EXTBIOS

拡張 BIOS 関連を `EXTBIOS.LIB` として別途定義します。アーカイブには含まれていません。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| EXTBIOS.LIB | MDLLIB.LIB |

```pascal:EXTBIOS.LIB
(*
    EXTBIOS.LIB
*)

  procedure ExtBios;
  var
    raf : integer absolute $0103;
    rbc : integer absolute $0105;
    rde : integer absolute $0107;
    rhl : integer absolute $0109;
  begin
    inline($2A/raf/       { LD HL,(raf)     }
           $E5/           { PUSH HL         }
           $F1/           { POP AF          }
           $ED/$4B/rbc/   { LD BC,(rbc)     }
           $ED/$5B/rde/   { LD DE,(rde)     }
           $2A/rhl/       { LD HL,(rhl)     }
           $CD/$CA/$FF/   { CALL EXTBIO     }
           $ED/$43/rbc/   { LD (rbc),BC     }
           $ED/$53/rde/   { LD (rde),DE     }
           $22/rhl/       { LD (rhl),HL     }
           $F5/           { PUSH AF         }
           $E1/           { POP HL          }
           $22/raf);      { LD (raf),HL     }
  end;

  function GetKMode: Byte;
  begin
    LibRegs.D := $11;
    LibRegs.E := $00;
    LibRegs.A := $FF;
    ExtBios;
    GetKMode := LibRegs.A;
  end;
  
  procedure SetKMode(Mode: Byte);
  begin
    if not (Mode in [0..4]) then
      Mode := 0;
    LibRegs.D := $11;
    LibRegs.E := $01;
    LibRegs.A := Mode;
    ExtBios;
  end;
```

#### ルーチン

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| ExtBios() | **procedure** ExtBios; | 拡張 BIOS を呼び出す |
| GetKMode() | **function**  GetKMode; | 現在の漢字モードを返す。0xFF が返った場合には漢字ドライバが読み込まれていない |
| SetKMode() | **procedure** SetKMode(Mode: byte); | 漢字モードを設定する |

:::note warn
EXTBIOS.LIB は予告なく変更になる事があります。
:::

### ■ SPRITE

スプライト関連を `SPRITE1.LIB` / `SPRITE2.LIB` として別途定義します。アーカイブには含まれていません。

| ファイル名 | 依存ライブラリ |
|:---|:---|
| SPRITE1.LIB | MDLLIB.LIB, VRAM1.LIB |
| SPRITE2.LIB | MDLLIB.LIB, VRAM2.LIB |

:::note
SPRITE2.LIB はスプライトモード 2 対応 (MSX2 以降) です。
:::

```pascal:SPRITE1.LIB
(*
    SPRITE1.LIB
*)

type
  TSpDat8 = array [0..7] of byte;
  TSpDat16 = array [0..31] of byte;
  TSpAtr =
    record
      Row: byte;
      Col: byte;
      PtnNo: byte;
      Color: byte;
    end;

var
  _SpPtnAddr: Integer;
  _SpAtrAddr: Integer;
  _SpPtnSize: byte;

procedure InitSprite(Size: byte);
begin
  _SpPtnAddr := PattTable;
  _SpAtrAddr := TranspTable;
  if (Size mod 2) = 0 then
    _SpPtnSize := 8
  else
    _SpPtnSize := 32;
  ClearSprites;
  SpriteSize(Size);
end;

function CalcSpPtnAddr(PtnNo: byte): Integer;
begin
  CalcSpPtnAddr := _SpPtnAddr + PtnNo * _SpPtnSize;
end;

function CalcSpAtrAddr(Plane: byte): Integer;
begin
  CalcSpAtrAddr := _SpAtrAddr + Plane * 4;
end;

procedure SetSpritePtn(PtnNo: byte; var Ptn);
begin
  LibRegs.BC := _SpPtnSize;
  LibRegs.DE := _SpPtnAddr + PtnNo * LibRegs.BC;
  LibRegs.HL := Addr(Ptn);
  Bios($005C);
end;

procedure PutSprite(Plane: byte; X, Y: byte; Color: byte; PtnNo: byte);
var
  Atr: TSpAtr;
begin
  Atr.Col := X;
  Atr.Row := Y;
  Atr.PtnNo := PtnNo;
  Atr.Color := Color;
  LibRegs.HL := Addr(Atr);
  LibRegs.BC := 4;
  LibRegs.DE := _SpAtrAddr + Plane * LibRegs.BC;
  Bios($005C);
end;

procedure SetSpriteAtr(Plane: byte; var Atr: TSpAtr);
begin
  LibRegs.HL := Addr(Atr);
  LibRegs.BC := 4;
  LibRegs.DE := _SpAtrAddr + Plane * LibRegs.BC;
  Bios($005C);
end;
```

```pascal:SPRITE2.LIB
(*
    SPRITE2.LIB
*)

type
  TSpDat8 = array [0..7] of byte;
  TSpDat16 = array [0..31] of byte;
  TSpAtr =
    record
      Row: byte;
      Col: byte;
      PtnNo: byte;
      Color: byte;
    end;
  TSpClrs = array [0..15] of byte;

var
  _SpPtnAddr: Integer;
  _SpAtrAddr: Integer;
  _SpClrAddr: Integer;
  _SpPtnSize: byte;

procedure InitSprite(Size: byte);
begin
  _SpPtnAddr := PattTable;
  _SpAtrAddr := TranspTable;
  _SpClrAddr := ColorTable;
  if (Size mod 2) = 0 then
    _SpPtnSize := 8
  else
    _SpPtnSize := 32;
  ClearSprites;
  SpriteSize(Size);
end;

function CalcSpPtnAddr(PtnNo: byte): Integer;
begin
  CalcSpPtnAddr := _SpPtnAddr + PtnNo * _SpPtnSize;
end;

function CalcSpAtrAddr(Plane: byte): Integer;
begin
  CalcSpAtrAddr := _SpAtrAddr + Plane * 4;
end;

function CalcSpClrAddr(Plane: byte): Integer;
begin
  CalcSpClrAddr := _SpClrAddr + Plane * 16;
end;

procedure SetSpritePtn(PtnNo: byte; var Ptn);
begin
  LibRegs.BC := _SpPtnSize;
  LibRegs.DE := _SpPtnAddr + PtnNo * LibRegs.BC;
  LibRegs.HL := Addr(Ptn);
  Bios($005C);
end;

procedure ColorSprite(Plane: byte; var Colors: TSpClrs);
begin
  LibRegs.HL := Addr(Colors);
  LibRegs.BC := 16;
  LibRegs.DE := _SpClrAddr + Plane * LibRegs.BC;
  Bios($005C);
end;

procedure PutSprite(Plane: byte; X, Y: byte; Color: byte; PtnNo: byte);
var
  Atr: TSpAtr;
begin
  Atr.Col := X;
  Atr.Row := Y;
  Atr.PtnNo := PtnNo;
  Atr.Color := Color;
  LibRegs.HL := Addr(Atr);
  LibRegs.BC := 4;
  LibRegs.DE := _SpAtrAddr + Plane * LibRegs.BC;
  Bios($005C);
end;

procedure SetSpriteAtr(Plane: byte; var Atr: TSpAtr);
begin
  LibRegs.HL := Addr(Atr);
  LibRegs.BC := 4;
  LibRegs.DE := _SpAtrAddr + Plane * LibRegs.BC;
  Bios($005C);
end;

procedure ShowSprite(Flg: Boolean);
begin
  WriteVdp(8, (ReadVdp(8) and $FD) or (Ord(not Flg) shl 1));
end;     
```

#### ルーチン

『MSX-C Library』のスプライト関連関数と似たような使い方ができます。

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| InitSprite() | **procedure** InitSprite(Size: byte); | スプライトを初期化する |
| CalcSpPtnAddr() | **function** CalcSpPtnAddr(PtnNo: byte): Integer; | 指定したパターン番号のスプライトパターンジェネレータテーブルのアドレスを返す |
| CalcSpAtrAddr() | **function** CalcSpAtrAddr(Plane: byte): Integer; | 指定したプレーン番号のスプライトアトリビュートテーブルのアドレスを返す |
| CalcSpClrAddr() | **function** CalcSpClrAddr(Plane: byte): Integer; | 指定したプレーン番号のスプライトカラーテーブルのアドレスを返す |
| SetSpritePtn() | **procedure** SetSpritePtn(PtnNo: byte; **var** Ptn); | スプライトパターンを設定する |
| ColorSprite() | **procedure** ColorSprite(Plane: byte; **var** Colors: TSpClrs); | 指定したプレーン番号のスプライトのライン毎の色を設定する |
| PutSprite() | **procedure** PutSprite(Plane: byte; X, Y: byte; Color: byte; PtnNo: byte); | 指定したプレーン番号のスプライトを表示する |
| SetSpriteAtr() | **procedure** SetSpriteAtr(Plane: byte; var Atr: TSpAtr); | 指定したプレーン番号のスプライトのスプライトの属性を設定する (`PutSprite()` と引数が異なるだけ) |
| ShowSprite() | **procedure** ShowSprite(Flg: Boolean); | スプライトの表示/非表示を切り替える |

##### PutSprite()

スプライトモード 2 (MSX2 以降 / SCREEN 4 以降) では、パラメータ `Color` で指定した色は無視されます。`ColorSprite()` を使って色を指定してください。

#### 型

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| TSpDat8 | **type**<br>&nbsp;&nbsp; TSpDat8 = **array** [0..7] **of** byte; | 8x8 スプライトパターンジェネレータテーブル用配列 |
| TSpDat16 | **type**<br>&nbsp;&nbsp; TSpDat16 = **array** [0..31] **of** byte; | 16x16 スプライトパターンジェネレータテーブル用配列 |
| TSpAtr | **type**<br>&nbsp;&nbsp;TSpAtr =<br>&nbsp;&nbsp;&nbsp;&nbsp;**record**<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Row: byte;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Col: byte;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PtnNo: byte;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Color: byte;<br>&nbsp;&nbsp;&nbsp;&nbsp;**end**; | スプライトアトリビュートテーブルレコード |
| TSpClrs | **type**<br>&nbsp;&nbsp; TSpClrs = **array** [0..15] **of** byte; | スプライトカラーテーブル用配列 |

#### 変数

| 名前 | 定義 | 説明 | 
|:---|:---|:---|
| _SpPtnAddr | **var** _SpPtnAddr: Integer; | スプライトパターンジェネレータテーブルの先頭アドレス |
| _SpAtrAddr | **var** _SpAtrAddr: Integer; | スプライトアトリビュートテーブルの先頭アドレス |
| _SpClrAddr | **var** _SpClrAddr: Integer; | スプライトカラーテーブルの先頭アドレス |
| _SpPtnSize | **var** _SpPtnSize: byte; | スプライトパターンのデータ長 |

#### サンプル

8x8 スプライトをスプライトモード 1 で拡大表示します。

```pascal:SpriteTest1.pas
program SpriteTest1;
{$I MDLLIB.LIB}
{$I VRAM1.LIB}
{$I SPRITE1.LIB}
const
  Data: TSpDat8 = (
    $3C, $7E, $D7, $D7, $FF, $7E, $24, $C3);
begin
  Color(1, 1, 1);
  ScrMode(2);

  InitSprite(2);
  SetSpritePtn(0, Data);
  PutSprite(0, $60, $60,  6, 0);
  PutSprite(1, $70, $60, 12, 0);
  PutSprite(2, $80, $60,  4, 0);

  Readln;
  ScrMode(0);
  Color(15, 4, 7);
  TextMode;
end.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/e4f7cde0-a8f5-fc59-38c6-c4bc449eeec8.png)


16x16 スプライトをスプライトモード 2 (MSX2 以降) で拡大表示します。

```pascal:SpriteTest4.pas
program SpriteTest4;
{$I MDLLIB.LIB}
{$I VRAM2.LIB}
{$I SPRITE2.LIB}
const
  Data: TSpDat16 = (
    $40, $E0, $40, $41, $43, $47, $47, $4F,
    $6F, $FF, $7D, $4C, $4E, $4F, $4F, $5F,
    $0C, $12, $17, $F2, $FA, $FC, $FC, $5E,
    $5F, $FE, $16, $06, $0E, $FE, $FE, $1F);
  Colors1: TSpClrs = (
    $02, $03, $04, $05, $06, $07, $08, $09, 
    $0A, $0B, $0C, $0D, $0E, $0F, $02, $03);
  Colors2: TSpClrs = (
    $04, $05, $06, $07, $08, $09, $0A, $0B, 
    $0C, $0D, $0E, $0F, $02, $03, $04, $05);
  Colors3: TSpClrs = (
    $06, $07, $08, $09, $0A, $0B, $0C, $0D, 
    $0E, $0F, $02, $03, $04, $05, $06, $07);
begin
  Color(1, 1, 1);
  ScrMode(4);

  InitSprite(3);
  SetSpritePtn(0, Data);
  PutSprite(0, $50, $50, $00, 0);
  ColorSprite(0, Colors1);
  PutSprite(1, $70, $50, $00, 0);
  ColorSprite(1, Colors2);
  PutSprite(2, $90, $50, $00, 0);
  ColorSprite(2, Colors3);

  Readln;
  ScrMode(0);
  Color(15, 4, 7);
  TextMode;
end.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/927d94f8-59e9-1232-a9fe-c48b7a96748b.png)

:::note warn
SPRITE1.LIB / SPRITE2.LIB は予告なく変更になる事があります。
:::

# トランジェントコマンド

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

48K システム用にコンパイルされているため、MSX0 だけでなく実機でも動作すると思います。

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

 - [MSX0_TP3/TRANSIENT (GitHub: @ht_deko)](https://github.com/ht-deko/MSX0_TP3/tree/main/TRANSIENT)
:::

## ■ M0SETCLR
前景色、背景色、ボーダー色を設定する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/7dcb57fd-4b1d-7074-ccb7-1355c7e6eca8.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/6d3624b9-79c5-ad38-5a56-7eb3b5b9a2c4.png)

```txt:Usage: 
Usage: M0SETCLR [/Fnum] [/Bnum] [/Dnum]
```

 - パラメータなしで実行すると、現在の設定を表示します。
 - スイッチは `-` でも `/` でもよく、大文字/小文字も判断しません。
 - 無効なスイッチは無視されます。
 - MSX0 固有の機能に依存していません。


## ■ TPKEYS
Turbo Pascal 用ファンクションキーの設定と表示切替を行う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/2b2315b4-2b61-ca07-602d-69218bb8bb4b.png)

```txt:Usage: 
Usage: TPKEYS [/B] [/I] [/P] [/T] [/S] [/H] [/R<:file>] [/W<:file>]
```

 - /B または /I でファンクションキーの内容を初期化 (BASIC) します。
 - /P または /T でファンクションキーの内容を Turbo Pascal 用に設定します。
 - /S でファンクションキーの内容を表示します。
 - /H でファンクションキーの内容を非表示にします。
 - /R で設定をファイル (ファイル名省略時 `FUNCKEYS.DAT`) から読み込みます。
 - /W で現在の設定をファイル (ファイル名省略時 `FUNCKEYS.DAT`) へ書き出します。
 - Turbo Pascal で使う際には TINST で行数を `23` に変更してください。
 - 複数のスイッチを指定できます (左から順に実行されます)。
 - スイッチは `-` でも `/` でもよく、大文字/小文字も区別しません。
 - 無効なスイッチは無視されます。
 - MSX0 固有の機能に依存していません。

:::note
`FUNCKEYS.DAT` の先頭 4 バイトはファイルヘッダです (編集不可)。次に 16文字x10個のデータ (160 バイト) が続きます。以後、256 バイト目 (ファイル末) までのデータはゴミです。
:::

# おわりに
Delphi 使いはニッコリかもですね。Turbo Pascal 自身が持つルーチンについては次の記事を参考にしてください。

 - [Turbo Pascal 3.0 の Pascal (Qiita)](https://qiita.com/ht_deko/items/f9f394dc2342afd2ea7b)

車輪の再発明をせず、アプリケーションのためのコードだけを書けばいいって幸せですね。MDL-LIB の作者に感謝です。

**See also:**

 - [『MSX0 Stack』の操作方法 (Qiita)](https://qiita.com/ht_deko/items/b284cb93ac34e2b77d9a)
 - [Turbo Pascal 3.0.x の使い方 (Qiita)](https://qiita.com/ht_deko/items/ec212f5cc17cbe5f718b)
 - [MSXPad を 64bit Windows で使えるようにする (Qiita)](https://qiita.com/ht_deko/items/0689cae7daf8582e8492)
 - [テクハン wiki (にゃごす)](http://ngs.no.coocan.jp/doc/wiki.cgi/TechHan)
 - [MSX Datapack wiki (にゃごす)](http://ngs.no.coocan.jp/doc/wiki.cgi/datapack)