0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PC-8801エミュレーターPC8801FabGLの拡張

0
Posted at

・はじめに

PC-8801エミュレーターPC8801FabGL(以下)のTERATERM入出力・N88-BASICのAUTO RUN・コマンド拡張の改造を行ったので、報告します。
https://github.com/basara767676/PC8801FabGL

・写真と動画

CRT画面と teraterm画面は、完全には同期は取れないです。teraterm の方で、1行改行しなかったり、カーソルの位置が1つずれたりします。やはり難しいです。teratermでのキー操作をゆっくりやれば同期が外れにくいです。 後、PCのキーボードの反応を鈍くすると同期が外れにくいです。 動画では teraterm から 10行目を削除しています。 RUN すると画面にノイズが入ります。パスコン追加でもノイズは消えない、汗。

0 .jpg
我が家のニャンコが Qiita に初登場!!!

01 IMG_20260331_155910.jpg
ブレッドボードに載っている 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のピン利用状況)

2-10 スクリーンショット 2026-03-29 203526.png

(追加ハード)

2-20 スクリーンショット 2026-03-29 202918.png
2-12 スクリーンショット 2026-03-29 202824.png

・ソフトの主な変更点

今回の拡張は、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 の上記部分を変更する
スクリーンショット 2026-03-31 110513.jpg
赤字部分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 命令はまだ入れていない等、まだ不十分な点もありますが、一応ここで一区切りを付けたいと思います。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?