cpm8266をMacでビルドしてみた。

CP/M シミュレーターの比較的新しい実装の[cpm8266](https://github.com/SmallRoomLabs/cpm8266)をMac(El Capitan)でビルドしてみました。cpm8266はオンボードのFlash(4M)にドライブを作り、外付けのSDなどが必要無いのでお手軽です。

ビルドツールはplatformioのtoolchainを使います。macportsなどのパッケージシステムは使わずビルドします。

z80asmとcpmtoolsが必要なので、これもビルドします。二つとも/usr/local/binにインストールしなくてもコマンドが使えます。

http://savannah.nongnu.org/projects/z80asm

http://www.moria.de/~michael/cpmtools/

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のバージョンに依存してるのかも）

https://github.com/yamori813/cpm8266/commit/1ec71a6c64c6922b7a8f9ca3f04209e76bfe58ae

あと必要なくなったのですがdisks/deskew.shの中で使われているodコマンドの-wオプションもMacでは使えません。ちなみに当初こんな風にして動くようにしました。1行16バイトなので8行を1行にして1行128バイトにしてあります。bashの計算機能はたしかに便利ですが、そもそもこういう処理をshスクリプトで書くのはちょっと向いてない気がします。

```diff
-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を使い、空の場合は小さくなるようにしたところ、問題が起きなくなりました。

[ターミナルソフト](https://qiita.com/yamori813/items/29e6bcd2f090983abb9a)で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の中身はこんな風になってます。

![esp8266.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/a920d74e-307a-cb67-3154-f3c0996d3505.png)

各ドライブは最初のIBMの8インチフロッピー(3740)の仕様で、サイズは128バイトx26セクタx77トラックで256256バイトですが、先頭の2トラック分(6656バイト)は使えません。ドライブはAからOまであります。

ちょっと試してみたいのであれば、ビルドしなくてもgithubのrelaseタグにバイナリがあります。

4M Flash張り替えのESP-01で試しましたが、こんなに小さいモジュールでCP/Mが動いて、8インチのフロッピーが15枚も入っているなんてすごい時代になったものです。とはいえCP/MはWindowsの祖先のようなもので、あんまり変わってない気もします。

![写真（2019-06-24 9.39）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/4742176a-65e1-f48d-ad89-7c8c962476c3.jpeg)

C:に入っているTurbo Pascal試してみました。

https://youtu.be/uq247zGsLjc

Turbo Pascalのエディタは^s^d^e^xのダイアモンドカーソルで^k-sで保存、^k-dで終了します。

A:に入っているXSとXRはcpm8266を作った人が作ったxmodemのプログラムなのだがXRは使えたが、XSが使えない。screenから起動したlrzszと相性が悪いのかもしれません。下記のテストプログラムは動作します。

```c
#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;
}
```

送信も作って見ました。

https://github.com/yamori813/xm

xrの受信でファイルができなくて気がついたのですが、sudo cuして実行すると、実際は_uucpのプロセスとして起動されます。このためディレクトリにこのアカウントからの書き込み権限がないとファイルが作れません。

以前作った3.3V電源にソケットつけてみた。ちっちゃいCP/Mマシンになりました。

![写真（2019-06-26 18.09） #2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/8fedddcd-b8eb-02ea-0e72-6290fa63e5f6.jpeg)

cpm8266はすごく良い実装だと思うのですが、1ドライブのディスク容量が小さいのがちょっと難です。TRS-80 Model IIばりに倍密度にできるといいのですが。

cpm8266のビルドはNOSDKかWIFIのどちらかのオプションでビルドします。WIFIは試していません。

ASCIIARTというベンチマークを見つけて実行してみたところ2:40くらいでした。リアルZ80 10MHzくらいだと思われます。