CP/M シミュレーターの比較的新しい実装のcpm8266をMac(El Capitan)でビルドしてみました。cpm8266はオンボードのFlash(4M)にドライブを作り、外付けのSDなどが必要無いのでお手軽です。
ビルドツールはplatformioのtoolchainを使います。macportsなどのパッケージシステムは使わずビルドします。
z80asmとcpmtoolsが必要なので、これもビルドします。二つとも/usr/local/binにインストールしなくてもコマンドが使えます。
Macでエラーになって手を入れたのは以下の点です。
- Makefileのdefineに=があると処理されないので削除
- ddのオプションのstatus=noneは無いので、makeの引数にDDFLAGSを指定して削除
- makeコマンドの引数で指定できるようにMakefileのESP8266SDKとBINSのpathを変更
- includeディレクトリを追加できるようにEXINCLUDEを追加
- cpmtoolsのdiskdefsファイルをdisksディレクトリに作ってデフォルトのibm-3740の部分をオリジナルからコピー
- diskdefsのskewを1にしてdisks/MakefileからDESKEWをコメントアウト
- disks/MakefileでNEWDISKでtrでoctalで0xe5でパディングされたバイナリファイルを作ってるところがMacでは177以上のoctalが使えなかったのでprintfとxxdに変更(mkfs.cpmを使うようにしたので、不要になった)
- main.cのos_type.hのincludeを削除(toolchainのバージョンに依存してるのかも)
あと必要なくなったのですがdisks/deskew.shの中で使われているodコマンドの-wオプションもMacでは使えません。ちなみに当初こんな風にして動くようにしました。1行16バイトなので8行を1行にして1行128バイトにしてあります。bashの計算機能はたしかに便利ですが、そもそもこういう処理をshスクリプトで書くのはちょっと向いてない気がします。
-IFS=$'\n' sector=($(od -t x1 -An -v -w128 $INFILE))
-
+#IFS=$'\n' sector=($(od -t x1 -An -v -w128 $INFILE))
+IFS=$'\n' lines=($(od -t x1 -An -v $INFILE))
+
+c=0
+for i in ${lines[@]}; do
+ if [ $(($c%8)) -eq 0 ]; then
+ sector[$(($c/8))]=$i
+ else
+ sector[$(($c/8))]+=$i
+ fi
+ c=$((++c))
+done
このdeskew.shスクリプトはskew=6でインターリーブされたデータをインターリーブされてない状態(skew=1)にするスクリプトです。
これでmakeコマンドにESP8266SDK,EXINCLUDE,ESPTOOL,ASM,CPMCP,CPMRM, DDFLAGS,ESPPORTを指定してfullでビルドすると書き込めます。
$ make ESP8266SDK=~/.platformio/packages/toolchain-xtensa/ EXINCLUDE=~/.platformio/packages/framework-arduinoespressif8266/tools/sdk/include ESPTOOL=~/.platformio//packages/tool-esptoolpy/esptool.py ASM=/foo/z80asm-1.8/z80asm CPMCP=/foo/cpmtools-2.20/cpmcp CPMRM=/foo/cpmtools-2.20/cpmrm DDFLAGS="bs=1 conv=notrunc" ESPPORT=/dev/cu.usbserial-DA00WSMM full
[CC] main.c
[CC] machine.c
[CC] uart.c
[CC] conio.c
[CC] flash.c
[CC] monitor.c
[CC] utils.c
[CC] gpio16.c
[CC] z80/z80emu.c
[AS] nosdk/startup.S
[CC] nosdk/nosdk8266.c
[LINK]
text data bss dec hex filename
12916 10 70416 83342 1458e image.elf
[ERASING FLASH]
[UPLOAD] image.elf-0x00000.bin image.elf-0x10000.bin
[MAKEDISK] DISK_A.DSK
[MAKEDISK] DISK_B.DSK
[MAKEDISK] DISK_C.DSK
[MAKEDISK] DISK_D.DSK
[MAKEDISK] DISK_E.DSK
[MAKEDISK] DISK_F.DSK
[MAKEDISK] DISK_G.DSK
[MAKEDISK] DISK_H.DSK
[MAKEDISK] DISK_I.DSK
[MAKEDISK] DISK_J.DSK
[MAKEDISK] DISK_K.DSK
[MAKEDISK] DISK_L.DSK
[MAKEDISK] DISK_M.DSK
[MAKEDISK] DISK_N.DSK
[MAKEDISK] DISK_O.DSK
[Z80ASM] BOOT.Z80
[Z80ASM] BIOS.Z80
[Z80ASM] BDOS.Z80
[Z80ASM] CCP.Z80
53+0 records in
53+0 records out
53 bytes transferred in 0.000189 secs (280679 bytes/sec)
2035+0 records in
2035+0 records out
2035 bytes transferred in 0.009046 secs (224965 bytes/sec)
3580+0 records in
3580+0 records out
3580 bytes transferred in 0.011291 secs (317073 bytes/sec)
896+0 records in
896+0 records out
896 bytes transferred in 0.003041 secs (294660 bytes/sec)
[UPLOAD] disks/DISK_A.DSK to 0x3c0000
[UPLOAD] disks/DISK_B.DSK to 0x381000
[UPLOAD] disks/DISK_C.DSK to 0x342000
[UPLOAD] disks/DISK_D.DSK to 0x303000
[UPLOAD] disks/DISK_E.DSK to 0x2c4000
[UPLOAD] disks/DISK_F.DSK to 0x285000
make: *** [writediskF] Error 2
bashでも~は使えますが、スクリプトにすると一つ目しか処理されないようです。~はもともとcsh由来なので使わない方が良いかもしれません。
なぜかDISK_F.DSKの書き込みでエラーになるのですが、Aドライブは書き込めているので起動して使えるようです。最初のうちは書き込みできてたのですが、途中からできなくなりました。ESPのデバイスは書き込みが不安定な気がします。
調べてみたところ、書き込みのときはデータを圧縮して送っていて、同じバイトが続いてある程度の大きさになると問題が起きるようです。もともと自力でディスクサイズでイメージを作っていたのですが、イメージの作成にmkfs.cpmを使い、空の場合は小さくなるようにしたところ、問題が起きなくなりました。
ターミナルソフトで9600で接続して、ESP8266のリセットボタンを押した後にリターンを2回入力するとプロンプトが出ます。
8???????=?????$??
cpm8266 - Z80 Emulator and CP/M 2.2 system version 0.4
62K CP/M v2.2 [cpm8266 v0.4 - SmallRoomLabs]
a>dir
A: ASM COM : CRC COM : CRC MAC : DDT COM
A: DDTZ COM : DUMP COM : ED COM : FILES TXT
A: LOAD COM : PIP COM : STAT COM : SUBMIT COM
A: XR COM : XS COM : XSUB COM : FREE SUB
A: ZDE COM
a>b:
b>dir
B: 180FIG COM : 8080 MAC : LINK COM : MAC COM
B: MAKESYM COM : MAKESYM DOC : MBASIC COM : OBASIC COM
B: RMAC COM : SLR180 COM : SLR180 DOC : SYNTAX HLP
B: Z80ASM COM : ZASM COM : L80 COM : M80 COM
b>MBASIC
BASIC-80 Rev. 5.21
[CP/M Version]
Copyright 1977-1981 (C) by Microsoft
Created: 28-Jul-81
32568 Bytes free
Ok
? "MORI MORI"
MORI MORI
Ok
system
b>
Flashの中身はこんな風になってます。
各ドライブは最初のIBMの8インチフロッピー(3740)の仕様で、サイズは128バイトx26セクタx77トラックで256256バイトですが、先頭の2トラック分(6656バイト)は使えません。ドライブはAからOまであります。
ちょっと試してみたいのであれば、ビルドしなくてもgithubのrelaseタグにバイナリがあります。
4M Flash張り替えのESP-01で試しましたが、こんなに小さいモジュールでCP/Mが動いて、8インチのフロッピーが15枚も入っているなんてすごい時代になったものです。とはいえCP/MはWindowsの祖先のようなもので、あんまり変わってない気もします。
C:に入っているTurbo Pascal試してみました。
Turbo Pascalのエディタは^s^d^e^xのダイアモンドカーソルで^k-sで保存、^k-dで終了します。
A:に入っているXSとXRはcpm8266を作った人が作ったxmodemのプログラムなのだがXRは使えたが、XSが使えない。screenから起動したlrzszと相性が悪いのかもしれません。下記のテストプログラムは動作します。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#define SOH (0x01)
#define STX (0x02)
#define EOT (0x04)
#define ACK (0x06)
#define NAK (0x15)
#define CAN (0x18)
#define CTRLZ (0x1a)
int main(int argc, char *argv[])
{
int fd, down;
char buf[255];
int len;
int count;
int blocks;
fd = open(argv[1], O_RDWR);
down = open(argv[2], O_CREAT|O_WRONLY|O_TRUNC, 0644);
buf[0] = NAK;
write(fd, buf, 1);
blocks = 0;
count = 0;
while(1) {
while(1) {
len = read(fd, buf, 1);
if (count == 0 && buf[0] == EOT) {
buf[0] = ACK;
write(fd, buf, 1);
printf("\nfile size %d\n", blocks * 128);
close(fd);
close(down);
return(0);
}
if (count > 2 && count < 131) {
write(down, buf, 1);
}
++count;
if(count == 128 + 4)
break;
}
printf(".");
fflush(stdout);
buf[0] = ACK;
write(fd, buf, 1);
count = 0;
++blocks;
}
return 0;
}
送信も作って見ました。
xrの受信でファイルができなくて気がついたのですが、sudo cuして実行すると、実際は_uucpのプロセスとして起動されます。このためディレクトリにこのアカウントからの書き込み権限がないとファイルが作れません。
以前作った3.3V電源にソケットつけてみた。ちっちゃいCP/Mマシンになりました。
cpm8266はすごく良い実装だと思うのですが、1ドライブのディスク容量が小さいのがちょっと難です。TRS-80 Model IIばりに倍密度にできるといいのですが。
cpm8266のビルドはNOSDKかWIFIのどちらかのオプションでビルドします。WIFIは試していません。
ASCIIARTというベンチマークを見つけて実行してみたところ2:40くらいでした。リアルZ80 10MHzくらいだと思われます。