RunCPM (Z80 CP/M 2.2 エミュレータ)

RunCPM は Arduino DUE, Teensy 3.5 / 3.6 及び ESP32 で動作する Z80 CP/M 2.2 エミュレータです。



CP/M とは?

CP/M (Control Program for Microcomputer) は Digital Research 社が開発した、MS-DOS の前身とも言える OS です。CP/M 2.2 の動作要件は以下の通りです。

プロセッサ Zilog Z80 または Intel 8080
メモリ 20KB 以上
ディスク装置 1 台以上 16 台以下

See Also:



必要なハードウェア

RunCPM の動作には次のボードあるいはプロセッサを搭載したボードが必要です。

その他に必要なものは以下の通りです。


Arduino DUE の場合

Arduino DUE では SD カードシールドを使う事ができます。3.3V 対応で CS が 4 ピンに接続されているものの使用が想定されているようです。イーサーネットシールド2のように microSD カードスロットがコンボになっているシールドもありますが、5V 専用シールドを DUE で使うとトラブルの元なのでやめておきましょう。

SD カードシールドを使わない場合には (micro) SD カードモジュールが必要になります。

シールドを使わない場合の DUE での接続は以下の通りです。

micoSD SPI 接続 (SS は 4 ピン)
LED ビルトイン LED (D13)

SD カードの動作が不安定であれば MISO をプルアップしてください。


Teensy 3.5 / 3.6 の場合

Teensy で用意するのは microSD カードだけです。


ESP32 の場合

ESP32 の場合、ボードの構成によって必要なものが異なります。例えば CQ 出版の IoT Express / MkII だと必要なのは microSD カードのみです。

ESP32-DevKitC での接続は以下の通りです。microSD カードモジュールによってはボードの下を配線した方がスッキリする事もあります。

micoSD VSPI 接続
LED IO22 に接続 (任意に変更可能)

画像では ESP32-DevKitC での接続例となっていますが、ビルトイン LED が存在する ESP32 ボードの場合には LED の接続は不要です。

See Also:



動作確認

RunCPM は以下から DL できます。本記事は ver 4.4 を対象としています。バージョン情報は globals.h を確認してください。

適当な場所に解凍しておきます。

別途 SDFat ライブラリが必要です。[スケッチ | ライブラリをインクルード |ライブラリを管理...] から SDFat を探してインストールします。


SD カードの準備

SD カードに、A, B, C... のようにそれぞれのドライブ用フォルダを作成します (最大 16 ドライブなので P: まで作れる)。そしてその中に "0" というフォルダをそれぞれ作ります。

  1. SD カードのルートフォルダに RunCPM の CCP フォルダにある CCP-DR.60K を配置します (すべての CCP ファイルをコピーしても構いません)。ちなみに CCP (Console Command Processor) は MS-DOS の COMMAND.COM に相当します。
  2. SD カードの \A\0 フォルダに RunCPM の DISK フォルダにある A.ZIP を解凍したものを配置します。
\
│  CCP-DR.60K
│
├─A
│  └─0
│          CLEAN.SUB
│          DDT.COM
│          DISKDEF.LIB
  (中略)
│          CCP.SUB
│          CCPZ.SUB
│          CCPZ.Z80
│
├─B
│  └─0
└─C
    └─0

このようになるハズです。\B\0 や \C\0 にはお好きな CP/M 用ファイルを格納してください。CP/M 8266 のディスクも使えます。

See Also:


■ 修正方法

ボード共通の設定

1. SDFat ライブラリ ver 2.0.x では SdFatConfig.h を次のように編集する必要があります。

// ARM boards usually have plenty of memory
#define SDFAT_FILE_TYPE 1

2. SDFat ライブラリの SdFatSoftSpiEX() または SdFatEX() を使用するボードでは、SdFatConfig.h を次のように編集する必要があります。

#define ENABLE_EXTENDED_TRANSFER_CLASS 1
#define ENABLE_SOFTWARE_SPI_CLASS 1

SdFatConfig.h は通常 %UserProfile%\Documents\Arduino\libraries\SdFat\src にあります。

ボード 修正の必要性
Arduino DUE
Teensy 3.5 / 3.6 / 4.0
ESP32×
STM32 (F4)
Adafruit Grand Central M4×

3. RunCPM.ino のヘッダファイル指定を、ターゲットのボード (プロセッサ) に書き換えます。

#include "hardware/teensy.h"
ボード ヘッダファイル
Arduino DUEdue.h
Teensy 3.5 / 3.6 / 4.0teensy.h
ESP32esp32.h
STM32 (F4)stm32.h
Adafruit Grand Central M4agcm4.h

ヘッダファイルは RunCPM のプロジェクトフォルダの hardware サブフォルダの中にあります。

Arduino DUE の場合

基本的にヘッダファイル (due.h) を修正する必要はありませんが、指定の SD カードシールドでない場合には、CS ピンを変更する必要があるかもしれません。

#ifndef DUE_H
#define DUE_H

SdFatEX SD;
#define SDINIT 4
#define LED 13
#define LEDinv 0
#define BOARD "ARDUINO DUE"
#define board_due
#define board_analog_io
#define board_digital_io


#endif

Teensy 3.5 / 3.6 / 4.0 の場合

ヘッダファイル (teensy.h) を修正する必要はありません。

ESP32 の場合

ESP32 ではボードに合わせて、ヘッダファイル (esp32.h) を を一部改変する必要があります。以下、ESP32-DevKitC での修正点です。

#ifndef ESP32_H
#define ESP32_H

SdFat SD;
//#define SDINIT 14,2,15,13 // TTGO_T1,LOLIN32_Pro
#define SDINIT 18,19,23,5 // ePaper,ESP32_DevKit
#define SDMHZ 25 // TTGO_T1,LOLIN32_Pro=25 ePaper,ESP32_DevKit=20
#define LED 22 // TTGO_T1=22 LOLIN32_Pro=5(inverted) DOIT_Esp32=2 ESP32-PICO-KIT=no led
#define LEDinv 0
#define BOARD "ESP32-DevKitC"
#define board_esp32
#define board_digital_io

uint8 esp32bdos(uint16 dmaaddr) {
        return(0x00);
}

#endif

以下、CQ 出版の IoT Express / MkII 用の変更点です。

#ifndef ESP32_H
#define ESP32_H

SdFat SD;
//#define SDINIT 14,2,15,13 // TTGO_T1,LOLIN32_Pro
#define SDINIT 18,19,23,5 // ePaper,ESP32_DevKit
#define SDMHZ 25 // TTGO_T1,LOLIN32_Pro=25 ePaper,ESP32_DevKit=20
#define LED 2 // TTGO_T1=22 LOLIN32_Pro=5(inverted) DOIT_Esp32=2 ESP32-PICO-KIT=no led
#define LEDinv 0
#define BOARD "IoT Express"
#define board_esp32
#define board_digital_io

uint8 esp32bdos(uint16 dmaaddr) {
        return(0x00);
}

#endif

STM32 (F4) の場合

ボード未所持により未確認です。

Adafruit Grand Central M4 の場合

ボード未所持により未確認です。


コンパイル&実行

配線が正しい事を確認したら SD カードをセットし、シリアルモニタ (デフォルトで 9600 bps) を開いた状態でスケッチ (RunCPM.ino) をコンパイルします。正しく実行できたら以下のようなメッセージがシリアルモニタに表示されます。

インジケーター用 LED でも起動の状態を確認できます。

  1. 起動時: LED が高速に点滅する
  2. シリアル接続可: LED が消える
  3. 起動不能: LED が点滅し続ける

RunCPM はディスプレイとしてシリアル接続の ANSI / VT100 端末エミュレータ (80x24 文字) を必要とします。Arduino IDE のシリアルモニタでは操作しにくいので、PuTTYTera Term 等で接続するといいでしょう。

DIR コマンド等、ディスクをアクセスする操作を行うと、インジケーター用 LED が点灯します。

Blue Pill を使った VT100 エミュレータを接続するのも面白いです。


CCP の変更

あまり必要はないとは思いますが、CCP を CCP-DR.60K 以外に変更する事ができます。SD カードのルートに目的の CCP ファイルをコピーした上で globals.h の定義を書き換えます。

/* Definition of which CCP to use (must define only one) */
//#define CCP_INTERNAL  // If this is defined, an internal CCP will emulated
#define CCP_DR
//#define CCP_CCPZ
//#define CCP_ZCPR2
//#define CCP_ZCPR3
//#define CCP_Z80

CCP_INTERNAL は内部 CCP を使用し、実機の CCP ファイルを使用しません。例えば CCP_INTERNAL を有効にしてコンパイルすると、以下の画像のようになります。

CCP ファイルの拡張子はメモリ量を表しており、60K と 64K の CCP が用意されています。メモリ量も globals.h の定義を書き換えて変更する事が可能です。

/* CP/M memory definitions */
#define RAM_FAST        // If this is defined, all RAM function calls become direct access (see below)
                        // This saves about 2K on the Arduino code and should bring speed improvements

#define TPASIZE 64      // Can be 60 for CP/M 2.2 compatibility or more, up to 64 for extra memory
                        // Values other than 60 or 64 would require rebuilding the CCP
                        // For TPASIZE<60 CCP ORG = (SIZEK * 1024) - 0x0C00

TPASIZE には通常 60 か 64 しか設定できません。これは対応する CCP ファイルが .60K と .64K しかないためです。任意のメモリ量に対応した CCP ファイルを作るためのツールは A: にあるようです。

See Also:



Borland Turbo Pascal

CP/M と言ったら Turbo Pascal ですよね!CP/M で動作するバージョンはオブジェクト指向ではない Pascal です (Object Pascal ではない)。


Turbo Pascal のインストール

Turbo Pascal は CP/M 8266 の disks/c から持ってくるのが手っ取り早いです。これを SD カードの \C\0 にコピーしておきます。

なお、Turbo Pascal のリファレンスマニュアルは Internet Archive で読む事ができます。


Turbo Pascal の起動

C:〔Enter〕でドライブを変更し、turbo〔Enter〕で Turbo Pascal が起動します。

初期画面がコレです。Y を押してエラーメッセージ用ファイル (TURBO.MSG) を読み込みます。

メイン画面です。ここで各種コマンドを実行します。

コマンド説明
Lカレントドライブを変更する。A~P ドライブを指定できる。
W作業ファイルを変更する。カレントの操作対象ファイル (ファイル名は 8.3 形式)。
メインファイルが指定されていなければここで指定したファイルがコンパイル対象ファイルとなる。
単一ソースで完結するプログラムであればメインファイルを指定する必要はない。
Mメインファイルを変更する。コンパイル対象ファイル (ファイル名は 8.3 形式)。
ファイル分割したプログラムや、外部テキストファイルをいじったりする場合にはコンパイルすべきプログラムファイルをメインファイルに指定する。
Eファイルを編集する (エディタモード)。メインファイルも作業ファイルも指定されていない場合には、作業ファイル名の入力が求められる。
メインファイルのみ指定されている場合にはメインファイルが作業ファイルとして読み込まれる (作業ファイルはメインファイルとなる)。
Cプログラムをコンパイルする。メインファイルも作業ファイルも指定されていない場合には、作業ファイル名の入力が求められる。
Rプログラムを実行する。メインファイルも作業ファイルも指定されていない場合には、作業ファイル名の入力が求められる。
S作業ファイルを保存する。メインファイルも作業ファイルも指定されていない場合には、ファイル名の入力が求められる。
メインファイルのみ指定されている場合にはメインファイルが作業ファイルとして読み込まれて保存される (作業ファイルは未指定のままとなる)。
X別の実行ファイル (.COM) を実行する。
Dカレントドライブのファイル一覧を表示する。
QTurbo Pascal を終了する。
Oコンパイラオプションを変更する。M はオンメモリ実行、C は .COM ファイルの生成、H は .CHN ファイルの生成。

エディタ画面です。ここでソースコードを編集します。カーソル移動とエディタ終了さえ覚えておけばどうにかなるでしょう。


機能キーボードショートカット備考
〔Ctrl〕+〔E〕
〔Ctrl〕+〔X〕
〔Ctrl〕+〔S〕
〔Ctrl〕+〔D〕
選択開始位置指定〔Ctrl〕+〔K〕〔B〕
選択終了位置指定〔Ctrl〕+〔K〕〔K〕
選択領域をコピーして貼り付け〔Ctrl〕+〔K〕〔C〕Windows での〔Ctrl〕+〔C〕, 〔Ctrl〕+〔V〕に相当
選択領域を削除して貼り付け〔Ctrl〕+〔K〕〔V〕Windows での〔Ctrl〕+〔X〕, 〔Ctrl〕+〔V〕に相当
選択領域を削除〔Ctrl〕+〔K〕〔Y〕
エディタを終了〔Ctrl〕+〔K〕〔D〕

GPIO の操作

RunCPM の BDOS (Basic Disk Operating System) には GPIO 操作用のファンクションが追加で用意されています。ちなみに BDOS は MS-DOS で言う MSDOS.SYS に相当します。

BDOS ファンクションTurbo Pascal での関数Arduino での関数説明
220 (0xDC)BDos(220, (pin shl 8) + mode) pinMode(pin, mode) ピンモードを設定します。mode に設定する値はボードによって異なります。
221 (0xDD)BDos(221, (pin shl 8)) digitalRead(pin) ピンの状態を読み出します (0=LOW, 1=HIGH)
222 (0xDE)BDos(222, (pin shl 8) + value) digitalWrite(pin, value)ピンの状態を設定します (0=LOW, 1=HIGH)。
223 (0xDF)BDosHL(223, (pin shl 8)) analogRead(pin) ピンの状態を読み出します (0~1023)。
224 (0xE0)BDos(224, (pin shl 8) + value) analogWrite(pin, value) ピンの状態を設定します (0~255)。ESP32 では使えません。
250 (0xFA)BDosHL(250) ホスト OS を返します (1=Arduino, 4=Teensy, 5=ESP32)。

BDOS ファンクションコールを使うには、Turbo Pascal の Bdos() 手続き/関数と、

BDos() 手続き/関数

procedure Bdos(Func[, Param]); 
function Bdos(Func[, Param]): Byte; 

Bdos() 手続きは、CP/M の BDOS ルーチンを呼び出すために使用します。

引数 Func 及び Param は整数式です。Func は呼び出されたルーチンに渡され、C レジスタにロードされます。 Param はレジスタペア DE にロードされます (省略可能)。

手続きは CALL 5 で BOOS を呼び出します。

Bdos() 関数は Bdos() 手続きと同様ですが、BOOS によって返された A レジスタの値を返します。

BDosHL() 関数を使います。

BDosHL() 関数

function BdosHL(Func[, Param]): Integer; 

この関数は上記の Bdos() 関数と全く同じですが、結果は HL レジスタのペアで返された値になります。

例えば、サンプルプログラム LED.PAS を実行すると任意の GPIO ピンを HIGH / LOW できます。LED が繋がれていればそれを ON / OFF できます。但しこのサンプルプログラムは ESP32 では正しく動作しません。

{* Turn a LED on or off *}

program led;

const
  INPUT = 0;
  OUTPUT = 1;

var
  pin, value: Integer;

begin
  write('Enter pin number: ');
  readln(pin);
  write('Enter value (0 = LOW, 1 = HIGH): ');
  readln(value);
  Bdos(220, (pin shl 8) + OUTPUT);
  Bdos(222, (pin shl 8) + value);
end.

何故なら、Arduino-ESP32 では INPUT は 1 で OUTPUT は 2 と定義されているからです。

//GPIO FUNCTIONS
#define INPUT             0x01
#define OUTPUT            0x02
#define PULLUP            0x04
#define INPUT_PULLUP      0x05
#define PULLDOWN          0x08
#define INPUT_PULLDOWN    0x09

なので、ESP32 では以下のようになります。

{* Turn a LED on or off *}

program led;

const
  INPUT = 1;
  OUTPUT = 2;

var
  pin, value: Integer;

begin
  write('Enter pin number: ');
  readln(pin);
  write('Enter value (0 = LOW, 1 = HIGH): ');
  readln(value);
  Bdos(220, (pin shl 8) + OUTPUT);
  Bdos(222, (pin shl 8) + value);
end.

LED.PAS を実行 (Run) して、ピン番号と HIGH / LOW を指定すれば...

Turbo Pascal から LED (IO27) を ON / OFF できます。

以下の ARDUINO.INC を用意しておけば、

{* ARDUINO.INC *}

const
  INPUT        = 0{ESP32: 1}
  OUTPUT       = 1{ESP32: 2}
  INPUT_PULLUP = 2{ESP32: 5}

  LOW  = 0;
  HIGH = 1;

  procedure pinMode(pin, mode: Byte);
  begin
    Bdos(220, (pin shl 8) + mode);
  end{of procedure pinMode}

  function digitalRead(pin: Byte): Byte;
  begin
    digitalRead := BDos(221, (pin shl 8));
  end{of procedure digitalRead}

  procedure digitalWrite(pin, value: Byte);
  begin
    BDos(222, (pin shl 8) + value);
  end{of procedure digitalWrite}

  function analogRead(pin: Byte): Integer;
  begin
    analogRead := BDosHL(223, (pin shl 8));
  end{of procedure analogRead}

  procedure analogWrite(pin, value: Byte);
  begin
    BDos(224, (pin shl 8) + value);
  end{of procedure analogWrite}

  function hostOS(pin, value: Byte): Integer;
  begin
    hostOS := BDosHL(225, (pin shl 8) + value);
  end{of procedure hostOS}

$I 指令 (C 言語の #include 命令みたいなもの) を使って L チカが簡潔に書けます。ESP32 の場合は ARDUINO.INC の INPUT / OUTPUT / INPUT_PULLUP の値を書き換えてください。

{* BLINK.PAS *}

{$I ARDUINO.INC}
const
  LED_PIN = 4;
begin
  pinMode(LED_PIN, OUTPUT);
  repeat
    digitalWrite(LED_PIN, HIGH);
    Delay(1000);
    digitalWrite(LED_PIN, LOW);
    Delay(1000);
  until KeyPressed;
end.

ファイルを分割している場合にはメインファイルにコンパイルすべきファイルを指定する事をお忘れなく。

こうしておけば作業ファイルが BLINK.PAS であっても ARDUINO.INC であっても BLINK.PAS がコンパイルされます。

See Also:


エディタのキーバインドを変更したい

ダイアモンドカーソルじゃなく、カーソルキーでカーソル移動したいですよね。そんな場合には TINST (Terminal Installation) でカスタマイズします。

〔C〕キーを押してキーバインドを変更します。設定項目は 45 ありますが、最低限以下の設定ができれば OK だと思います。

CURSOR MOVEMENT
1: Character left 〔←〕
2: Alternative
3: Character right 〔→〕
4: Word left 〔Ctrl〕+〔←〕
5: Word right 〔Ctrl〕+〔→〕
6: Line up 〔↑〕
7: Line down 〔↓〕
8: Scroll down 〔Ctrl〕+〔↑〕
9: Scroll up 〔Ctrl〕+〔↓〕
10: Page up
11: Page down
12: To left on line
13: To right on line
14: To top of page
15: To bottom of page
16: To top of file
17: To end of file
18: To begining of block
19: To end of block
20: To last cursor position
INSERT & DELETE:
21: Insert mode on/off
22: Insert line
23: Delete line
24: Delete to end of line
25: Delete right word
26: Delete character under cursor〔Delete〕
27: Delete left character 〔Back Space〕
28: Alternative
BLOCK COMMANDS:
29: Mark block begin
30: Mark block end
31: Mark single word
32: Hide/display block
33: Copy block
34: Move block
35: Delete block
36: Read block from disk
37: Write block to disk
MISC. EDITING COMMANDS:
38: End edit
39: Tab
40: Auto tab on/off
41: Restore line
42: Find
43: Find & replace
44: Repeat last find
45: Control character prefix

TINST で S 押して変なの選んじゃった!

ANSI に戻すのです。6〔Enter〕N〔Enter〕とタイプします。

Choose one of the following terminals:

  1) ADDS 20/25/30          17) Osborne 1
  2) ADDS 40/60             18) Otrona Attache
  3) ADDS Viewpoint-1A      19) Qume
  4) ADM 3A                 20) RC-855 (ITT)
  5) Ampex D80              21) Soroc 120/Apple CP/M
  6) ANSI                   22) Soroc new models
  7) DEC Rainbow, 8 bit     23) SSM-UB3
  8) Hazeltine 1500         24) Tandberg TDV 2215
  9) Hazeltine Esprit       25) Teleray series 10
 10) Kaypro with hilite     26) Teletex 3000
 11) Kaypro, no hilite      27) Texas Instruments
 12) Lear-Siegler ADM-20    28) Visual 200
 13) Lear-Siegler ADM-31    29) Wyse WY-100/200/300
 14) Liberty                30) Zenith
 15) Microbee VDU           31) None of the above
 16) Morrow MDT-20          32) Delete a definition

Which terminal? (Enter no. or ^Q to exit):  6〔Enter〕
Do you want to modify this definition before installation?  (Y/N)?  N


                    Hardware dependent information

Operating frequency of your microprocessor in MHz (for delays): 20 Change to:〔Enter〕

Turbo Pascal の画面サイズを変更するには "Do you want to modify this definition before installation? (Y/N)?" で Y を選び、以下のように設定します。

Terminal Settings
Terminal type: ANSI Change to: ____________________
Send an initialization string to the terminal? (Y/N)? Y Change to: Y CrtInit() 手続きで実行される
Command: Change to:
Send a reset string to the terminal (Y/N)? Y Change to: Y CrtExit() 手続きで実行される
Command: <ESC> [ 0 m (27 91 48 109) Change to:
CURSOR LEAD-IN command: <ESC> [ (27 91) Change to: GoToXY() 手続きで実行される
CURSOR POSITIONING COMMAND to send between line and column: ; (59) Change to:
CURSOR POSITIONING COMMAND to send after both line and column: H (72) Change to:
Column first (Y/N)? N Change to: N
OFFSET to add to LINE: 1 Change to:
OFFSET to add to COLUMN: 1 Change to:
Binary address (Y/N)? N Change to: N
Number of ASCII digits (2 or 3): 2 Change to:
CLEAR SCREEN command: <ESC> [ 2 J (27 91 50 74) Change to: ClrScr() 手続きで実行される
Does CLEAR SCREEN also HOME cursor (Y/N)? N Change to: N
HOME command: <ESC> [ f (27 91 102) Change to:
DELETE LINE command: <ESC> [ 1 M (27 91 49 77) Change to: DelLine() 手続きで実行される
INSERT LINE command: <ESC> [ 1 L (27 91 49 76) Change to: InsLine() 手続きで実行される
ERASE TO END OF LINE command: <ESC> [ K (27 91 75) Change to: ClrEol() 手続きで実行される
START HIGHLIGHTING command: <ESC> [ 1 m (27 91 49 109) Change to: LowVideo() 手続きで実行される
END HIGHLIGHTING command: <ESC> [ 0 m (27 91 48 109) Change to: NormVideo() 手続きで実行される
Number of rows (lines) on your screen: 24 Change to:行数
Number of columns on your screen: 80 Change to:桁数
Delay after CURSOR ADDRESS (0-255 ms): 0 Change to:
Delay after CLEAR, DELETE and INSERT (0-255 ms): 0 Change to:
Delay after ERASE TO END OF LINE and HIGHLIGHT (0-255 ms): 0 Change to:

ここで "Is this definition correct? (Y/N)?" と尋ねてくるので、設定に間違いがなければ Y を押します。

Hardware dependent information
Operating frequency of your microprocessor in MHz (for delays): 20 Change to:  

最後の質問はプロセッサの周波数なのですが、最大でも 20MHz までしか指定できないため、Delay() 関数が機種依存します。パラメータに 1000 を指定しても 1 秒であるとは限りません。


余談

なお、CP/M 版 Turbo Pascal は DL できませんが、MS-DOS 用の Turbo Pascal は未だに DL 可能です。子孫の Delphi も Community Edition という無償版がリリースされています。

See Also:



Microsoft BASIC (MBASIC / BASIC-85)

Microsoft の BASIC も動きます。


Microsoft BASIC の起動

A: ドライブに入っているので、mbasic〔Enter〕とタイプすれば起動します。CP/M 8266 の disks/b にも入っています。

※ CP/M に戻るには system〔Enter〕とタイプします。


GPIO の操作

RunCPM の BDOS には GPIO 操作用のファンクションが追加で用意されています。

サンプル LED.BAS を実行すると任意の GPIO ピンを HIGH / LOW できます。LED が繋がれていればそれを ON / OFF できます。但しこのサンプルプログラムは ESP32 では正しく動作しません。理由は Turbo Pascal の項で書いた通りです。

...

920 REM ### Turn a LED on or off ###
930 REM
940 X=USR0(PEEK(ADR+74)*256+1)
950 X=USR2(PEEK(ADR+74)*256+PEEK(ADR+75))
960 REM
970 REM ### Read analog value from the pin ###
980 REM
990 REM X=USR3(PEEK(ADR+74)*256) : PRINT X

940 行目の + 1 を + 2 に変更すれば ESP32 で動作します。機械語ルーチンを PEEK / POKE / USR (CALL) するコードなので MBASIC からの GPIO 操作は敷居が高いですね。

100 DATA 229,94,1,1,0,9,86,14,220,205,5,0,225,201
110 DATA 229,1,1,0,9,86,14,221,205,5,0,225,119,229,62,0,1,1,0,9,119,225,201
120 DATA 229,94,1,1,0,9,86,14,222,205,5,0,225,201
130 DATA 229,1,1,0,9,86,14,223,205,5,0,84,93,225,115,229,1,1,0,9,114,225,201
140 DATA 0,0,-1
150 BD$="" : READ A : WHILE A>-1 : BD$=BD$+CHR$(A) : READ A : WEND
160 ADR=PEEK(VARPTR(BD$)+1)+PEEK(VARPTR(BD$)+2)*256
170 DEF USR0=ADR : DEF USR1=ADR+14 : DEF USR2=ADR+37 : DEF USR3=ADR+51
180 INPUT "Pin";P : INPUT "Value";V
190 POKE ADR+74,P : POKE ADR+75,V
200 X=USR0(PEEK(ADR+74)*256+1)
210 X=USR2(PEEK(ADR+74)*256+PEEK(ADR+75))

REM (注釈) 行を削れば 12 行ではあるのですが...。

See Also:



HI-TECH SoftWare HI-TECH C

HI-TECH C は HI-TECH SoftWare の C コンパイラです。


HI-TECH C のインストール

HI-TECH C の CP/M バージョンは HI-TECH SoftWare 社からフリーウェアとして公開されていましたが、Microchip に買収された後でダウンロードできなくなりました。しかしながら Internet Archive には残っており、そこからダウンロードする事は可能です。

z80v309.exe が目的のコンパイラです。DOS 形式の自己解凍 EXE になっていますが、64bit Windows では自己解凍 (EXE を実行しての解凍) できません。その場合には LHAZ 等で解凍しましょう。ファイルを解凍できたら micoSD の \J\0 あたりにコピーしておきます。何故 J: ドライブを使うのかと言えば、CP/M 8266 の disks で J: は空になっているからです。


HI-TECH C の使い方

ソースコードを書いて、C.COM でコンパイルします。テキストエディタは CP/M 標準の ED か、

スクリーンエディタ te が使えます。どちらも A: ドライブにあります。

See Also:


GPIO の操作

RunCPM の BDOS には GPIO 操作用のファンクションが追加で用意されています。

サンプル LED.C をコンパイルして実行すると任意の GPIO ピンを HIGH / LOW できます。LED が繋がれていればそれを ON / OFF できます。但しこのサンプルプログラムは ESP32 では正しく動作しません。理由は Turbo Pascal の項で書いた通りです。

/* Turn a LED or on off */

#include <cpm.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    int pin, value;

    if (argc != 3) {
        printf("Syntax: led pin value (0 = low, 1 = high)");
        return 1;
    }

    pin = atoi(argv[1]);
    value = atoi(argv[2]);

    bdos(220, (pin << 8) + 1);
    bdos(222, (pin << 8) + value);
}

赤字の 1 を 2 に変更すれば ESP32 で動作します。

コンパイルは "c -v ファイル名" で行います。

コンパイルが正常に終了すると、LED.COM という実行ファイルが生成されているはずです。この実行ファイルの第一パラメータとしてピンを、第二パラメータとして状態を渡せばピンを HIGH/LOW できます。

 

面倒臭い!

面倒臭い?そんな時は Turbo Pascal を使えばいいのです!L を押してドライブを J: に変更し、W を押して作業ファイルを指定します。

そして E を押せばご覧の通りです...誰も *.PAS しか編集できないとは言っていませんよねぇ?

コンパイルはメイン画面で X を押して "c -v ファイル名" を入力します。コンパイルが完了すると Turbo Pascal のメニューに戻ります。

コンパイルが終わったら、再度 X を押して "LED 27 1" と入力すると、27ピンが HIGH になります。実行が完了すると Turbo Pascal のメニューに戻ります。

アラ便利! 「スクリーンエディタを買うとタダで Pascal が付いてくる」 と言われただけの事はありますね (w

See Also:



CP/M Assembler

CP/M Assembler は A: ドライブに入っています。アセンブラ関連コマンドは以下の通りです。

ASMCP/M アセンブラ
LOADHEX 形式 -> COM 形式変換
DDTデバッガ

CP/M Assembler の使い方

ソースコードを書いて、ASMでアセンブルします。拡張子部分は特別な意味を持つので指定してはいけません。アセンブルが終わると HEX ファイルが生成されます。

LOAD で HEX 形式ファイルを COM 形式の実行ファイルに変換します。HEX ファイルの拡張子は指定してはいけません。

DDT で、HEX 形式あるいは COM 形式の実行ファイルをデバッグ実行します。


GPIO の操作

RunCPM の BDOS には GPIO 操作用のファンクションが追加で用意されています。サンプル LED.ASM...はちょっと解りにくいので、改変したもので試してみます。

; Turn on a LED wired to pin 4
org  100h   ;start address
mvi  c, 220 ;pinMode
mvi  d, 4   ;digital pin number
mvi  e, 1   ;mode (0 = INPUT, 1 = OUTPUT)
call 5      ;call BDOS
mvi  c, 222 ;digitalWrite
mvi  d, 4   ;digital pin number
mvi  e, 1   ;value (0 = low, 1 = high)
call 5      ;call BDOS
ret         ;exit to CP/M

アセンブルして実行すると GPIO ピン (コードでは 4 ピン) を HIGH / LOW できます。LED が繋がれていればそれを ON / OFF できます。但しこのサンプルプログラムは ESP32 では正しく動作しません。理由は Turbo Pascal の項で書いた通りです。

こちらも Turbo Pascal から作業するといいかもしれませんね。

See Also:




ESP8266 (ESP-WROOM-02) への対応 (~ver3.5)

RunCPM は ESP8266 (ESP-WROOM-02) に対応していませんが、結論から言うとわずかな修正で ESP-WROOM-02 でも動作させる事が可能です。


必要なハードウェア

動作に必要な部品は ESP32 (ESP32-WROOM32) に準じます。

ESPr Developer での接続は以下の通りです。インジケーター LED にはビルトイン LED を使っていますが、外付けの LED の方がいいかもしれません。microSD カードモジュールによってはボードの下を配線した方がスッキリする事もあります。

micoSD HSPI 接続
LED ビルトイン LED (IO1) を使用

ソースコードの修正

修正手順は以下の通りです。修正を施しても他のボードでの動作に影響を及ぼしません。

  1. RunCPM.ino に追記します。インジケーター LED は IO4 に接続する設定なので、ESPr Developer でビルトイン LED を使う場合には 1 を指定して下さい。SPI の CS ピンには IO15 を使います。
    #include "globals.h"
    
    #include <SPI.h>
    
    // LED related definitions
    #ifdef ESP32
      #include <mySD.h>
      #define LED 22 // TTGO_T1=22 LOLIN32_Pro=5(inverted) DOIT_Esp32=2 ESP32-PICO-KIT=no led
      #define LEDinv 0 // 0=normal 1=inverted
      #define mySDpins 13,15,2,14 // Some boards use 26,14,12,27
    #elif defined(ESP8266)
      #include <SD.h>
      #define LED 4
      #define LEDinv 0
    #else
      #include <SD.h>
      #define LED 13
      #define LEDinv 0
    #endif
    
    // Delays for LED blinking
    #define sDELAY 50
    #define DELAY 100
    
    // Pin for the SD chip select signal
    #ifdef ARDUINO_SAM_DUE
      #define SDcs 4
    #endif
    #ifdef CORE_TEENSY
      #define SDcs BUILTIN_SDCARD
    #endif
    #ifdef ESP8266
      #define SDcs 15
    #endif
  2. abstraction_arduino.h に追記します。BDOS 追加ファンクション用の設定で、これがないとコンパイルエラーになります。
    #ifdef ARDUINO_SAM_DUE
    #define HostOS 0x01
    #endif
    #ifdef CORE_TEENSY
    #define HostOS 0x04
    #endif
    #ifdef ESP32
    #define HostOS 0x05
    #endif
    #ifdef ESP8266
    #define HostOS 0x06
    #endif
  3. 同じく abstraction_arduino.h ですが、_sys_fputc() が原因でコンパイルエラーになるのでキャストします (赤字部分を追加)。
    int _sys_fputc(int ch, File f) {
            return(f.write((unsigned int)ch));
    }
  4. globals.h で CCP_INTERNAL を有効にします。
    /* Definition of which CCP to use (must define only one) */
    #define CCP_INTERNAL  // If this is defined, an internal CCP will emulated
    //#define CCP_DR
    //#define CCP_CCPZ
    //#define CCP_ZCPR2
    //#define CCP_ZCPR3
    //#define CCP_Z80
  5. 同じく globals.h で、メモリサイズを 34K に設定します。これ以上だとコンパイルできても動作が不安定になります。
    /* CP/M memory definitions */
    #define RAM_FAST       // If this is defined, all RAM function calls become direct access (see below)
                           // This saves about 2K on the Arduino code and should bring speed improvements
    
    #define SIZEK 34       // Can be 60 for CP/M 2.2 compatibility or more, up to 64 for extra memory
                           // Can be set to less than 60, but this would require rebuilding the CCP
                           // For SIZEK<60 CCP ORG = (SIZEK * 1024) - 0x0C00
    

上記修正を行ってコンパイルすると なんとか動作する CP/M となります。

正直 34K ではメモリ不足で使い物にならないと思うので、実用的に CP/M を動作させたいのであれば RunCPM ではなく CP/M 8266 の方を試してみてください。Turbo Pascal 3.0 の動作には最低 48K 必要なのですヨ。

CP/M 8266 の場合、ディスクイメージはフラッシュメモリに格納されるので、microSD カードスロット等の外付けパーツは不要です。ディスク IO も高速なので ESP-WROOM-02 (ESP8266) の場合には CP/M 8266 がオススメです。

See Also:



Seeed Wio terminal への対応

RunCPM は Wio terminal に対応していませんが、結論から言うとわずかな修正で Wio terminal でも動作させる事が可能です。RunCPM が対応している Adafruit Grand Central M4 の MCU は SAMD51 なのですが、Wio terminal の MCU もまた SAMD51 なのです。

必要なハードウェア

他に必要なものはありません。microSD カードくらいでしょうか?

micoSD 内蔵
LED ビルトイン LED (青) を使用

ソースコードの修正

修正手順は以下の通りです。修正を施しても他のボードでの動作に影響を及ぼしません。

1.hardware/wioterm.h を用意する。

#ifndef WIOTERM_H
#define WIOTERM_H

#define USE_SDIO 0
SdFat SD;
#define SDINIT SDCARD_SS_PIN
#define LED LED_BUILTIN
#define LEDinv 0
#define BOARD "Seeed Wio Terminal"
#define board_wioterm

#endif

2. abstraction_arduino.h に Wio Terminal 用の定義を追記する。

#ifndef ABSTRACT_H
#define ABSTRACT_H

#ifdef PROFILE
#define printf(a, b) Serial.println(b)
#endif

#if defined ARDUINO_SAM_DUE || defined ADAFRUIT_GRAND_CENTRAL_M4 || SEEED_WIO_TERMINAL
#define HostOS 0x01
#endif

3. RunCPM.ino のヘッダファイル指定を書き換える。

#include "hardware/wioterm.h"

4.SD カード用初期化処理を書き換える。

#if defined board_agcm4
  _puts("Initializing Grand Central SD card.\r\n");
  if (SD.cardBegin(SDINIT, SD_SCK_MHZ(50))) {

    if (!SD.fsBegin()) {
      _puts("\nFile System initialization failed.\n");
      return;
    }
#elif defined board_teensy40 
  _puts("Initializing Teensy 4.0 SD card.\r\n");
  if (SD.begin(SDINIT, SD_SCK_MHZ(25))) {
#elif defined board_esp32
  _puts("Initializing ESP32 SD card.\r\n");
  SPI.begin(SDINIT);
  if (SD.begin(SS, SD_SCK_MHZ(SDMHZ))) {
#else
  _puts("Initializing SD card.\r\n");
  if (SD.begin(SDINIT)) {
#endif

  _puts("Initializing SD card.\r\n");
#if defined(board_agcm4) || defined(board_wioterm)
  if (SD.cardBegin(SDINIT, SD_SCK_MHZ(50))) {
    if (!SD.fsBegin()) {
      _puts("\nFile System initialization failed.\n");
      return;
    }
#elif defined board_teensy40 
  if (SD.begin(SDINIT, SD_SCK_MHZ(25))) {
#elif defined board_esp32
  SPI.begin(SDINIT);
  if (SD.begin(SS, SD_SCK_MHZ(SDMHZ))) {
#else
  if (SD.begin(SDINIT)) {
#endif

※ SDFat ライブラリの設定ファイル (SdFatConfig.h) を編集する必要はありません。

See Also:


スタンドアロン CP/M マシン

robo8080 氏がスタンドアロン動作する RunCPM を公開されています。

※画像は Wio Terminal 用 RunCPM (USB キーボード版 / CardKB 版) です。

モジュール CardKB USB Bluetooth (SPP) HTTP
M5Stack M5Stack_RunCPM_vt100_CardKB
ATOMIC TFカードキット AtomicTF_RunCPM_BTSerial AtomicTF_RunCPM_WebTerm
Wio Terminal Wio_RunCPM_vt100_CardKB Wio_RunCPM_vt100_UsbKB
(CardKB 版と M5Stack 版をマージしています)

 

dir コマンドが見にくい

dir コマンドを実行するとファイルリストが見にくいです。CP/M 2.x はファイルリストを 4 列表示するように CCP にハードコードされているのが原因です。4 列表示には最低 66 桁 (CCP の種類によっては 60 桁) が必要です。

この問題は内部 CCP を使うよう globals.h を書き換え、ccp.h のハードコード部 (_ccp_dir 関数内) を書き換えれば解決します。最新の Wio_RunCPM_vt100_UsbKB ではこの修正が入っており、画面文字数によって動的に列数が変化するようになっています。

 

Wio Terminal で文字が見切れる

個体差かもしれませんが、Wio Terminal で 53x30 文字表示させると文字が見切れるようです。

settings サブフォルダにある各環境用ヘッダファイルでマージンを設定すると見切れなくなります。例えば WioTerminal_???.h では次のようにマージンが設定されています。

// 横画面時: (53 or 40 - NORM_MARGINH_X) x (40 - NORM_MARGINH_Y)
#define NORM_MARGINH_X  1         // マージン文字数 (横)
#define NORM_MARGINH_Y  1         // マージン文字数 (縦)
// 縦画面時: (40 or 30 - NORM_MARGINV_X) x (40 - NORM_MARGINV_Y)
#define NORM_MARGINV_X  1         // マージン文字数 (横)
#define NORM_MARGINV_Y  1         // マージン文字数 (縦)

// フォント: 80 Columns (font: 4x8)
#include "../fonts/font4x8yk.h"                    // 4x8 ドットフォント (@ykumano)
// 横画面時: (80 - WIDE_MARGINH_X) x (40 - WIDE_MARGINH_Y)
#define WIDE_MARGINH_X  0         // マージン文字数 (横)
#define WIDE_MARGINH_Y  1         // マージン文字数 (縦)
// 縦画面時: (60 - WIDE_MARGINV_X) x (40 - WIDE_MARGINV_Y)
#define WIDE_MARGINV_X  1         // マージン文字数 (横)
#define WIDE_MARGINV_Y  1         // マージン文字数 (縦)

Turbo Pascal の行数/桁数を TINST でいじっている場合にはそちらも変更してくださいね。

See Also:


ここにある情報が役に立って、「調べる手間が省けたからオマイに飯でもおごってやるよ」 というハートウォーミングな方がいらっしゃいましたら、下のボタンからどうぞ。

メニュー: