・はじめに
PC-8801エミュレーターPC8801FabGL(以下)のTERATERM入出力・N88-BASICのAUTO RUN・コマンド拡張の改造を行ったので、報告します。
https://github.com/basara767676/PC8801FabGL
・写真と動画
CRT画面と teraterm画面は、完全には同期は取れないです。teraterm の方で、1行改行しなかったり、カーソルの位置が1つずれたりします。やはり難しいです。teratermでのキー操作をゆっくりやれば同期が外れにくいです。 後、PCのキーボードの反応を鈍くすると同期が外れにくいです。 動画では teraterm から 10行目を削除しています。 RUN すると画面にノイズが入ります。パスコン追加でもノイズは消えない、汗。
ブレッドボードに載っている SDカード・EEPROM・LCD はまだ追加ソース入れていません。
・ハード
ORANGE-ESPerを使用。(PC-8001エミュレーター「n80FabGL」の制作で使用したハードをそのまま使用)
http://minosuke.cocolog-nifty.com/maria/2024/01/post-96d46e.html
ESP32の使えるIOを増やすために、64色を8色にしている。具体的には、ESP32とR!,B1,G1につながる抵抗端子をカットして、R0,B0,G0にはんだ付けした。
IO拡張にはMCP23S17を使用。
I2CデバイスでEEPROMとLCDも取付。
(ESP32のピン利用状況)
(追加ハード)
・ソフトの主な変更点
今回の拡張は、N88-BASICソースの一部変更・Z80のIO PORT入出力の利用・c言語による拡張コマンドの実施等で実現しています。
(FabGLの変更)
ESP32の使用できるIOを増やすために、
FabGL\src\dispdrivers\vgabasecontroller.cpp で、以下に変更。
// begin(GPIO_NUM_22, GPIO_NUM_21, GPIO_NUM_19, GPIO_NUM_18, GPIO_NUM_5, GPIO_NUM_4, GPIO_NUM_23, GPIO_NUM_15); // 64色
begin(GPIO_NUM_22, GPIO_NUM_19, GPIO_NUM_5, GPIO_NUM_23, GPIO_NUM_15); // 8色
これで、IO_4, 18, 21 が自由に使える。
(ファイルの追加と関数の追加)
teraterm の画面内を自由に動いて編集できるようにするため mcurses を使用。以下を参照。
https://nuneno.cocolog-nifty.com/blog/2017/03/arduinocurses-9.html
① 以下の辺りから tSerialDev、tscreenBase、tTermscreen を PC8801FabGL/src に追加。
https://github.com/Tamakichi/ttbasic_arduino_stm32/tree/master/libraries/TTBAS_LIB
② tscreenBase.h に以下の関数を追加する。
void moveBegin(); // (N88 BASIC EDITモード用) カーソルを その行の始めに移動
uint8_t edit88(); // (N88 BASIC用)スクリーン編集
③ tscreenBase.cpp に以下の関数を追加する。
uint8_t tscreenBase::edit88() {
uint8_t ch, ret;
ch = get_wch();
switch(ch) {
case SC_KEY_CR: // [Enter]キー *
ret = 0x0D;
break;
case SC_KEY_BACKSPACE: // [BS]キー *
movePrevChar();
delete_char();
ret = 0x08; // PC-8801 [DEL](BackSpace)
break;
case SC_KEY_IC: // [Insert]キー
Insert_char(0x20);
movePrevChar();
ret = 0x12; // PC-8801 [INS]
break;
case SC_KEY_RIGHT: // [→]キー
moveNextChar();
ret = 0x1C;
break;
case SC_KEY_LEFT: // [←]キー *
movePrevChar2();
ret = 0x1D;
break;
case SC_KEY_DOWN: // [↓]キー *
moveNextLineChar();
ret = 0x1F;
break;
case SC_KEY_UP: // [↑]キー *
movePrevLineChar();
ret = 0x1E;
break;
case SC_KEY_HOME: // [CLR]キー ([Home]を使用)
cls();
locate(0,0);
ret = 0x0C; // PC-8801 [CLR]
break;
case SC_KEY_NPAGE: // [PageDown] 表示プログラム最終行に移動
if (pos_y == height) {
edit_scrollUp();
}
else moveBottom();
locate(0, pos_y);
ret = 0xF8; // PC-8801 [PageDown]
break;
case SC_KEY_PPAGE: // [PageUP]
edit_scrollDown();
locate(0, 0);
ret = 0xF9; // PC-8801 [PageUp]
break;
case SC_KEY_END: // [END}
locate(0, pos_y);
break;
case SC_KEY_F7: // [F7] ファンクションキーを表示
ret = 0xFA;
break;
case SC_KEY_F8: // [F8] ファンクションキーを表示
ret = 0xFB;
break;
case SC_KEY_F9: // [F9] ファンクションキーを表示
ret = 0xFC;
break;
case SC_KEY_F10: // [F10] ファンクションキーを表示
ret = 0xFD;
break;
case SC_KEY_F11: // [F11] ファンクションキーを表示
ret = 0xFE;
break;
case SC_KEY_F12: // [F12] ファンクションキーを表示
ret = 0xFF;
break;
case SC_KEY_ESCAPE: // [ESC]キー ???
ret = 0x1B; // PC-8001 [ESC]
break;
default: // その他 可視可能文字は挿入表示
if (IS_PRINT(ch)) ret = ch;
break;
}
return ret;
}
// カーソルを その行の始めに移動
void tscreenBase::moveBegin() {
int16_t y = pos_y;
MOVE(y, 0);
}
edit88():N88-BASIC 1文字キー入力ルーチンでここに来て、teraterm よりの入力を実現している。
moveBegin():EDITモードで [PageDown] [PageUP] 使用時にカーソルを行の始めに戻す。
(src/pc88vm.cpp に以下を追加)
① 最初の部分に追加
拡張コマンド用
// 追加の Z80 マシン語ソース
#include "user_11_1.h"
// teraterm の画面内を自由に動いて編集できる様にする
#include "tscreenBase.h" // コンソール基本クラス
#include "tTermscreen.h" // シリアルコンソールクラス
tscreenBase* sc; // 利用デバイススクリーン用ポインタ
tTermscreen sc1; // ターミナルスクリーン
uint8_t* workarea = NULL; // 画面用動的獲得メモリ
// MCP23S17の使用
#include "Adafruit_MCP23X17.h" // MCP23S17
Adafruit_MCP23X17 mcp1;
#define CS2 21 // MCP23S17 SPI用
#define MOSI 0
#define MISO 36
#define SCLK 18
// 追加コマンドで使用する変数
int adr, i, i2, t1, tmp, f1, f2, ed_flg = 0;
unsigned char val_p[256]; // PORT DATA 格納用
unsigned int i8; // unsigned int 最大 4,294,967,295
unsigned long tm1, tm2, tm3;
//=============================
// I/O ポート 拡張命令用
// !アセンブラとc言語で使用変数を同じにしてソースの可読性を高める!
//=============================
#define _TIMER 0xA0 // CMD TM (1) :1..ON, 2..OFF, 3..値出力
#define _WAIT 0xA1 // CMD WA (1) :(1)WAIT(ms)(上位)
//#define _WAIT+1 0xA2 // CMD WA (1) :(1)WAIT(ms)(下位)
#define _MCP_MD 0xA3 // CMD MO (1),(2) : MCP MODE SET (1)ADRES..0-15
//#define _MCP_MD+1 0xA4 // CMD MO (1),(2) : MCP MODE SET (2)H/L..1/0
#define _MCP_DO 0xA5 // CMD DO (1),(2) : MCP DATA OUT (1)ADRES..0-15
//#define _MCP_DO+1 0xA6 // CMD DO (1),(2) : MCP DATA OUT (2)H/L..1/0
#define _MCP_DI 0xA7 // CMD DI (1) : MCP DATA IN (1)ADRES..0-15
#define _SER_R 0xB1 // Serial.read
#define _SER_R2 0xB2 // [PageUp][PageDown]キーの処理
#define _SER_W 0xB3 // Serial.writeの出力ポート
アセンブラとc言語で使用変数を同じにしてソースの可読性を高めている。
② PC88VM::init() に追加
拡張コマンド用
// シリアルコンソールの初期化設定
sc = &sc1;
((tTermscreen*)sc)->init(80, 24, 80, workarea); // スクリーン初期設定
sc->cls();
sc->locate(0,0);
// 拡張ハードを使用するための ESP32 GPIOの設定
pinMode(0, OUTPUT); pinMode(2, OUTPUT); pinMode(4, OUTPUT); pinMode(18, OUTPUT); pinMode(21, OUTPUT);
pinMode(34, INPUT_PULLUP); pinMode(36, INPUT_PULLUP); pinMode(39, INPUT_PULLUP);
// MCP 設定 PA,PB 出力モード, 0出力 :PA0=0,PA7=7,PB0=8,PB7=15
mcp1.begin_SPI(CS2, SCLK, MISO, MOSI, 0x20); // MCP23S17 アドレス
for (i = 0;i <= 15; i++) { // MCP PA,PB 出力モード
mcp1.pinMode(i, OUTPUT); }
mcp1.writeGPIOA(0);
mcp1.writeGPIOB(0);
③ PC88VM::readIO() に追加
拡張コマンド用
case _TIMER: { // A0H // ===== タイマー値の読み出し
value = val_p[t1];
t1++;
return value;
break;
}
case _MCP_DI: { // A7H // ===== MCP DIN
value = val_p[_MCP_DI];
return value;
break;
}
case _SER_R: // B1H // teraterm より READ
value = 0;
if (Serial.available()) {
value = sc->edit88();
ed_flg = 0; // ed_flg=1 : [PageDown] or [PageUp]キー
if ((value == 0xF8) || (value == 0xF9)) ed_flg = 1;
}
return value;
break;
case _SER_R2: // B2H // [PageUp][PageDown]キーの処理
if (ed_flg == 1) {
sc->moveBegin(); // カーソルを その行の始めに移動
ed_flg = 0;
}
return 0;
break;
④ PC88VM::writeIO() に追加
拡張コマンド用
case _TIMER: { // A0H //##### CMD TM : タイマー 1..ON, 2..OFF, 3..値出力
if (value == 1) tm1 = millis(); // === 1...ON
else if (value == 2) { // === 2...OFF
tm2 = millis(); // millis() max 4.29*10^6 約50日
tm3 = tm2 - tm1;
if (tm3 > 16777215) tm3 = 16777215; // 16777215 ( 8bit×3 = 24bit)約4.6H
i2 = 0;
f2 = 0; // 最上位から下がって最初に 0 以外の時 f2=1 にする
for(i=0;i<8;i++) {
i8 = int(tm3 / pow(10, 7-i)); // 各桁の数値 GET
tm3 = tm3 - (i8 * pow(10, 7-i)); // 上位桁を除いた残り
if ( (i8 != 0) || (f2 != 0) ) {
i8 = i8 + 0x30; // ASCIIコードに変換
val_p[i2] = i8;
f2 = 1;
i2++;
}
}
val_p[i2] = 0;
t1 = 0; // 読み取り位置カウンター t1=0 にする
}
break;
}
case _WAIT: { // A1H //##### CMD WA : ウエイト (ms) 上位8bit
tmp = value;
break;
}
case _WAIT+1: { // A2H //##### CMD WA : ウエイト (ms) 下位8bit
delay(tmp * 256 + value); //##### 最大 65535 (ms)
break;
}
case _MCP_MD: { // A3H //##### CMD MD : MCP MODE SET I/Oアドレス 0-15
tmp = value;
break;
}
case _MCP_MD+1: { // A4H //##### CMD MD : MCP MODE SET OUT..1 / IN_PU..0
if (value) mcp1.pinMode(tmp, OUTPUT);
else mcp1.pinMode(tmp, INPUT_PULLUP);
break;
}
case _MCP_DO: { // A5H //##### CMD DO : MCP DOUT I/Oアドレス 0-15
tmp = value;
break;
}
case _MCP_DO+1: { // A6H //##### CMD DO : MCP DOUT HIGH 1 / LOW 0
if (value) mcp1.digitalWrite(tmp, HIGH);
else mcp1.digitalWrite(tmp, LOW);
break;
}
case _MCP_DI: { // A7H //##### CMD DI : MCP DIN I/Oアドレス 0-15
val_p[_MCP_DI] = mcp1.digitalRead(value);
break;
}
case _SER_W: // B3H // teraterm に WRITE
if ((value >= 0x20 && value <= 0x7F) || (value >=0xA0 && value <= 0xDF)) {
sc->putch(value);
}
else if (value == 0x0D) sc->newLine();
break;
⑤ PC88VM::initMemory()に追加
// (1) AUTO RUN .d88 変更 000032EDH: 1E 24
// 241EH より追加ソース
// (2) teraterm へ送信
adr = 0x3E0D; // 1文字画面出力ルーチンを書き換え
static const char dat1[3] = {0xc3, 0x4C, 0x24};
for (i = 0; i < 3 ; i++) {
*(mN88ROM + (adr + i)) = dat1[i];
}
// (3) teraterm より受信
adr = 0x35A2; // 1文字キーインルーチンを書き換え
static const char dat2[3] = {0xc3, 0x54, 0x24};
for (i = 0; i < 3 ; i++) {
*(mN88ROM + (adr + i)) = dat2[i];
}
// (4) [PageUp][PageDown]キーの処理
adr = 0x04A7; // コマンド待ちルーチンを書き換え
static const char dat3[3] = {0xc3, 0xD7, 0x24};
for (i = 0; i < 3 ; i++) {
*(mN88ROM + (adr + i)) = dat3[i];
}
//(1) - (4)の追加ソースと (5) CMD コマンド追加ルーチンを user_xx_1.h から読み込み
adr = 0x241d; // 倍精度減算ルーチンに上書き
for (i = 0;i < 500;i++) {
*(mN88ROM + (adr + i)) = data_1[i];
}
(1) AUTO RUN 対応:PC-8831-2W.d88 を書き換え、241DH に飛ぶ。後述。
mN88ROM に N88-BASIC ROM 内容が入っているので、ここを書き換える。
(2) 1文字画面出力ルーチン(3E00H - )の最初を書き換え、244CH に飛ぶ
(3) 1文字キーインルーチン(35A2H - )の最初を書き換え、2454H に飛ぶ
(4) コマンド待ちルーチン(04A7H - )の最初を書き換え、24D7H に飛ぶ
(5) CMD コマンド追加ルーチン、241DH -
倍精度演算ルーチン(241DH - )を書き換え、(1)-(5)のソースを入れる
(PC-8831-2W.d88 を書き換え)
(オリジナル)
AF61 21 ADB0 LD HL,LB0AD ; How many アドレス
AF64 CD 5055 CALL L5550 ; 文字列出力
AF67 CD C25F CALL L5FC2
AF6A 38 EB JR C,LAF57
AF6C D7 RST 10H ; キー入力
AF6D B7 OR A
AF6E 3A 7DEC LD A,(LEC7D)
AF71 28 0C JR Z,LAF7F
AF73 CD 1E14 CALL L141E
B044 E5 PUSH HL
B045 21 E7B0 LD HL,LB0E7 ; NEC N-88 アドレス
B048 CD 5055 CALL L5550
B04B E1 POP HL
B04C 2B DEC HL
B04D 2B DEC HL
B04E 2B DEC HL
B04F 2B DEC HL
B050 CD C228 CALL L28C2
B053 21 DBB0 LD HL,LB0DB ; Bytes アドレス
B056 CD 5055 CALL L5550
B059 CD 695A CALL L5A69
B05C C3 C687 JP L87C6 ; BASIC ホット·スタート2
(変更後)※変更部分のみ
AF61 18 0B JR AF6AH
AF6A 3E 05 LD A, 05H
AF6C 00 NOP
B05C C3 1E 24 JP 241EH ; RUN"BOOT 実行ルーチンへ
バイナリーエディターで、PC-8831-2W.d88 の上記部分を変更する

赤字部分3ケ所を変更
(追加 Z80 ソース)
;=== 241DH - 2689H 621 byte 使用可能 ===
; 478 byte 使用
_TIMER EQU 0xA0 ; CMD TM (1) : 1...ON, 2...OFF, 3...値表示
_WAIT EQU 0xA1 ; CMD WA (1) :(1)WAIT(ms)(上位)
; _WAIT+1 EQU 0xA2 // CMD WA (1) :(1)WAIT(ms)(下位)
_MCP_MD EQU 0xA3 ; CMD MD (1),(2) : MCP MODE SET (1)ADRES..0-15
;_MCP_MD+1 EQU 0xA4 ; CMD MD (1),(2) : MCP MODE SET (2)H/L..1/0
_MCP_DO EQU 0xA5 ; CMD DO (1),(2) : MCP DATA OUT (1)ADRES..0-15
;_MCP_DO+1 EQU 0xA6 ; CMD DO (1),(2) : MCP DATA OUT (2)H/L..1/0
_MCP_DI EQU 0xA7 ; CMD DI (1) : MCP DATA IN (1)ADRS..0-15
_SER_R EQU 0B1H
_SER_R2 EQU 0B2H
_SER_W EQU 0B3H
SYN_ERR EQU 02H
TYPE_ERR EQU 0DH
ERROR EQU 03B3H
MSG_PRINT EQU 5550H ; (HL) ~ 00 まで
ENT_CMD EQU 0EEB6H+1 ; EEB6H CMD の JUMP先アドレス
;========= (1) CMD 拡張 & AUTO RUN ==========
ORG 241DH
NOP
;
PUSH HL
PUSH AF
;
CALL CMD_START ; CMD 拡張
LD HL, MSG1
;
; ** B05CH : JP 87C6H を書き換え JP 241EH**
;
CALL COMEXEC ; RUN"BOOT 実行
POP AF
POP HL
JP 87C6H
;
MSG1: DB "RUN",0x22,"BOOT",0x0d,0x0a,00H
;
COMEXEC: ;(HL)~文字列アドレス指定して使用
LD A, FFH
LD (E6CDH), A
LD (F003H), HL
LD (E6CEH), A
XOR A
LD (F00CH), A
LD (E6CDH), A
RET
;====== (2) RS-232Cへ送信 244CH- =================
;
; ** 3E0DH : PUSH BC, PUSH DE, PUSH HL を書き換え JP 244CH、次は 3E10H **
;
PUSH BC
PUSH DE
PUSH HL
OUT (_SER_W), A
JP 3E10H
;======= (3) RS-232C より受信 2454H- ==============
;
; ** 35A2H : JR Z,3593H, POP BC を書き換え JP 2454H、次は 35A5H **
;
JR NZ, NEXT ; キーインあり NZ=1 なら、 NEXTへ
IN A,(_SER_R) ; キーインなし Z=1 なら、 RSINチェック
CP 0 ; RSなし Z=1
JP Z, 3593H ; RSINなし Z=1 なら、3593H LOOP1 へ
;
NEXT: ;キーインありの時
CP 0FAH ; FAH なら
CALL NC,FUNC ; NC=1の時(桁借りでない)は 0FAH-0FFH
POP BC
JP 35A5H
;
FUNC: ;ファンクションキーを表示ルーチン
LD HL,V_FU1
CP 0FAH
JP Z,COMEXEC
LD HL,V_FU2
CP 0FBH
JP Z,COMEXEC
LD HL,V_FU3
CP 0FCH
JP Z,COMEXEC
LD HL,V_FU4
CP 0FDH
JP Z,COMEXEC
LD HL,V_FU5
CP 0FEH
JP Z,COMEXEC
LD HL,V_FU6
CP 0FFH
JP Z,COMEXEC
RET
;
V_FU1: DB "OUT &H51,0",0x0D,0x0A,0 ; [F7]キー
V_FU2: DB "WIDTH 80,25",0x0D,0x0A,0 ; [F8]キー
V_FU3: DB "LOAD",0x22,0x0D,0x0A,0 ; [F9]キー
V_FU4: DB "LIST",0x22,0x0D,0x0A,0 ; [F10]キー
V_FU5: DB "EDIT ",0x0D,0x0A,0 ; [F11]キー
V_FU6: DB "OUT &HB5,0",0x0D,0x0A,0 ; [F12]キー
;========= (4) [PageUp][PageDown]キーの処理 24D7H- =========
;
; ** 04A7H : CALL 0E3FH を書き換え、次は 04AAH **
;
PUSH AF
IN A,(_SER_R2) ; ed_flg=1なら カーソルを その行の始めに移動
POP AF
CALL 0ED3FH
JP 04AAH
;========== (5) 拡張 CMD ==============
CMD_START: ; 拡張 CMDの使用
PUSH HL
PUSH AF
;
LD HL,CMD ;CMDコマンドのフック書き換え
LD (ENT_CMD),HL
LD HL,MSG2 ;タイトル表示
CALL MSG_PRINT
;
POP AF
POP HL
RET
MSG2:
DB "< CMD expansion >",0DH,0AH,0
;=================================================
; CMD (追加)の定義
;=================================================
CMD_TABLE:
DB "WA",00H ;CMD WA ウエイト (ARG1) 時間(ms)
DB "TM",00H ;CMD TM タイマー測定・表示
DB "MD",00H ;CMD MD : MCP MODE SET I/Oアドレス 0-15, OUT 1, IN_PU 0
DB "DO",00H ;CMD DO MCP DOUT I/Oアドレス 0-15, HIGH 1, LOW 0
DB "DI",00H ;CMD DI MCP DIN I/Oアドレス 0-15
DB 00H ;END MARKER
;=================================================
; CMD (追加)の JUMP TABLE
;=================================================
JUMP_TABLE:
DW CMD_WA ;CMD WA ウエイト (ARG1) 時間(ms)
DW CMD_TM ;CMD TM タイマー測定・表示
DW CMD_MD ;CMD MD : MCP23S17 MODE SET I/Oアドレス 0-15, OUT 1, IN_PU 0
DW CMD_DO ;CMD DO : MCP23S17 DOUT MCP I/Oアドレス 0 - 15 , HIGH 1, LOW 0
DW CMD_DI ;CMD DI : MCP23S17 DIN MCP I/Oアドレス 0 - 15
;=================================================
; CMD (追加)の 処理
;=================================================
;-------------------------------------------------
;CMD WA ウエイト (ARG1) 時間(ms)
;-------------------------------------------------
CMD_WA:
CALL FAC2DE
LD A, D ; (上位バイト)
OUT (_WAIT), A
LD A, E ; (下位バイト)
OUT (_WAIT + 1), A
RET
;-------------------------------------------------
;CMD TM タイマー 1...ON, 2...OFF, 3...値出力
;-------------------------------------------------
CMD_TM:
CALL FAC2DE
LD A, E ; (下位バイト) 1...ON, 2...OFF, 3...値出力
CP 3
JR Z,.L1
OUT (_TIMER),A ; 1 or 2
RET
;
.L1 LD D,8 ; 8桁出力
IN A,(_TIMER) ; 3 値出力
CP 0 ; 上位の数値が 0 か?
JR Z, .L2
RST 18H ; 上位の数値が 0 でない時
.L2 DEC D
IN A,(_TIMER)
RST 18H
LD A, D
CP 0 ; 最後の桁まで出力終わったか?
JR Z, .L3
JR .L2
.L3 RET
;-------------------------------------------------
;CMD MD : MCP MODE SET (ARG1) I/Oアドレス 0-15, (ARG2) OUT 1, IN_PU 0
;-------------------------------------------------
CMD_MD:
CALL FAC2DE
LD A, E ; I/Oアドレス 0-15
OUT (_MCP_MD),A
;
LD A, (HL) ;< 相対アドレス>の次の文字をとり出し
CP ',' ;コンマかどうか
JP Z, .L1 ;合っていたら .L1へ
LD DE, SYN_ERR
JP ERROR
;
.L1 RST 10H ;< データ>を計算
CALL FAC2DE
LD A, E ; OUT 1, IN_PU 0
OUT (_MCP_MD + 1),A
RET
;-------------------------------------------------
;CMD DO : MCP DOUT (ARG1) I/Oアドレス 0-15, (ARG2) HIGH 1, LOW 0
;-------------------------------------------------
CMD_DO:
CALL FAC2DE
LD A, E ; I/Oアドレス 0-15
OUT (_MCP_DO),A
;
LD A, (HL) ;< 相対アドレス>の次の文字をとり出し
CP ',' ;コンマかどうか
JP Z, .L1 ;合っていたら .L1へ
LD DE, SYN_ERR
JP ERROR
;
.L1 RST 10H ;< データ>を計算
CALL FAC2DE
LD A, E ; HIGH 1, LOW 0
OUT (_MCP_DO + 1),A
RET
;-------------------------------------------------
;CMD DI : MCP DIN (ARG1) I/Oアドレス 0-15
;-------------------------------------------------
CMD_DI:
CALL FAC2DE
LD A, E ; I/Oアドレス 0-15
OUT (_MCP_DI),A
RET
;=================================================
; FAC を整数型にして DE レジスタに値を入れる
;=================================================
FAC2DE:
CALL 11D3H ;最初はすでにRST 10Hは行なわれています。
PUSH HL
LD A, 2
CALL 1796H ;FACを整数型にします。
POP HL
LD DE,(0EC41H) ;DE←FACします。
RET
;=================================================
;CMD命令
;=================================================
CMD:
LD C,00H ;ジャンプ用インデックスの初期値
LD DE,CMD_TABLE ;コマンドテーブルポインタ(CP)の初期値
JP WORD_JUMP
;=================================================
;テキストポインタ以降の文字列とコマンドテーブルを比較して、一致すればジャンプする
;IN C=ジャンプ用インデックスの初期値,DE=コマンドテーブルポインタ,HL=現在のテキストポインタ
;OUT HL=次のテキストポインタ
;=================================================
WORD_JUMP:
;CALL SKIP_SPC ;
.L1 PUSH HL ;TPを退避
LD A,(DE) ;ここでDEはコマンド文字列の先頭を指している
OR A ;その値が00Hならすべて不一致を意味するので
JR NZ,.L3 ;エラーを出力して終了
LD E,SYN_ERR
JP ERROR
.L3 CP (HL) ;コマンド文字とTPの内容を比較
JR Z,.EQUAL ;等しくなければ次のコマンド文字列へ
.NEXT INC DE ;コマンド文字に00Hが見つかるまでCPを進める
LD A,(DE) ;
OR A
JR NZ,.NEXT
INC DE ;00Hの次の文字にポインタをセットする
INC C ;ジャンプ用インデックス値++
POP HL ;TPを復帰
JR .L1
.EQUAL INC DE ;ここから文字が等しい場合の処理
INC HL ;TPとCPを1進める
LD A,(DE) ;CPが指す内容が文字列終端マーカーならすべて一致したことになるので
OR A ;一致処理へ進む
JR NZ,.L3 ;
POP DE ;ここから一致処理。スタックに退避していたTPを捨てる
PUSH HL ;現在のTPを退避
LD HL,JUMP_TABLE ;HL<-JUMP_TABLE+C*2=ジャンプ先
SLA C ;
LD B,0 ;
ADD HL,BC ;
LD A,(HL) ;HL<-(HL)=ジャンプ先
INC HL ;
LD H,(HL) ;
LD L,A ;
EX (SP),HL ;(SP)<-ジャンプ先,HL<-現在のTP
RET ;ここでスタックからジャンプ先が取り出される
(拡張 CMD の使い方)
CMD WA ウエイト (ARG1)... 時間(ms) 最大 65535
CMD TM タイマー 1...ON, 2...OFF, 3...値出力
CMD MD :MCP 入出力モードセット MCP MODE SET (ARG1)...MCP I/Oアドレス 0-15, (ARG2)...OUT 1, IN_PU 0
CMD DO :MCP 出力 MCP DOUT (ARG1)...MCP I/Oアドレス 0-15, (ARG2)...HIGH 1, LOW 0
CMD DI :MCP 入力命令 MCP DIN (ARG1)...MCP I/Oアドレス 0-15
終わりに
CMD 〇〇 の処理ルーチンは、以下、SD-DOS のソースを使わせてもらいました。
https://github.com/chiqlappe/SD-DOS
Z80 アセンブラは、以下を使わせてもらいました。
https://github.com/AILight/AILZ80ASM
エラー処理が不十分、I2C 命令はまだ入れていない等、まだ不十分な点もありますが、一応ここで一区切りを付けたいと思います。



