CP/M68Kが動き、Cコンパイラが使えるようになったことからなにか動かしたくなった。
Hello, Worldだけではもったいない。
ただこのCコンパイラは少し古い。Web上にCソースが公開されているが、今はansi準拠がほとんど。
簡単にはコンパイル出来ないため書き換える必要があるが、大掛かりなものは面倒なので小物を探す。
そこでbcplkit-0.9.7(https://www.cl.cam.ac.uk/~mr10/)を使用することにした。
これはbpclのコンパイラで、bcplソースをINTCODEと言われるコードにコンパイルし、INTCODEをインタプリタで実行するもの。
コンパイラ自身はbcplで書かれているが、コンパイルした結果のINTCODE(syn.iやtrn.i)は用意されている。
このため、INTCODEインタプリタを用意すればbcplコンパイラが動くようになる。
このINTCODEインタプリタ(icint.c)やライブラリ(blib.c)はCで書かれたものが用意されている。
icint.cは340行程度、blib.cは170行程度であり、68KのCコンパイラで動くようにするのは
比較的容易だと思われた。
ところが簡単には動かない。
とりあえず動かすところまで出来たが、動作させるまでいろいろ注意点が出たのでまとめておく。
なおSKEDエディタには大変お世話になりました。(https://piclabo.blog.ss-blog.jp/CPM68K_SKED)
懐かしいダイヤモンドカーソルのスクリーンエディタで助かりました。
CP/M 68K用のスクリーンエディタを公開したskyriverさんに感謝します。
注意点1
bcplkit-0.9.7は当然ファイルフォーマットがunix
改行がLFになっており、cpmtoolsを使用してSDカードに書き込んだ場合CRLFでないため表示が崩れる。
またEOF(cZ)がないため、ファイルの最後にゴミがふくまれてしまう。
cpmcp で オプション -t があるが、バイナリとテキストにわけでコピーする必要があるので面倒。
そこで事前にlinux上でテキストファイルだけ nkfでLF -> CRLFにする(nkf -c --overwrite ファイル)。
またファイルの最後にcZを入れた(echo -e $'\x1a' >> ファイル)
注意 echo -e はbashの機能のようで、最初 #!/bin/sh でスクリプト組んでいたが -e$\x1a という
文字列が最後に入ってしまった。 #!/bin/bash で意図どおり動いた。
注意点2
CP/M 68のCコンパイラはKANSIじゃないので引数のタイプに注意。昔懐かしい書き方に変更(icint.c, blib.c)
int main(int argc, char *argv[])
↓
int main(argc, argv)
int argc;
char **argv;
{
プロトタイプ宣言でも(blib.h)
int getbyte(int, int); -> long getbyte();
void putbyte(int, int, int); -> void putbyte();
void initio(void); -> void initio();
その他不要な宣言#include <stdlib.h> などを削除 (68KのCコンパイラにはない)
注意点3
68KのCコンパイラはintが16bitだった。
bcplkitは32bitを想定しているがソースに「#define WORDSIZE 32」とあるので、
ここを修正すればと思っていたが、簡単ではなかった。
blib.cの中に以下のように32bitを前提としたコードがあった。
int
getbyte(int s, int i)
{
int w = M[s + i / 4];
int m = (i % 4) ^ 3;
w = w >> (8 * m);
return w & 255;
}
Cのコードだけなら修正すればと思っていたが、bcplで書かれたライブラリblib.bにも
32bit前提の箇所があった。
AND PACKSTRING(V, S) = VALOF
$( LET N = V!0 & 255
LET I = N/4
FOR P = 0 TO N DO PUTBYTE(S, P, V!P)
SWITCHON N&3 INTO
$( CASE 0: PUTBYTE(S, N+3, 0)
CASE 1: PUTBYTE(S, N+2, 0)
CASE 2: PUTBYTE(S, N+1, 0)
CASE 3: $)
RESULTIS I $)
bcplで書かれたライブラリは準備されているINTCODE(blib.i)に含まれているわけで、
Cのコードだけ変えればOKとはならない。慣れないintcodeを修正も避けたい。
仕方ないのでicint.c, blib.cのintをlongにして、32ビットで動かすことにした。
なおコンパイル用のバッチファイル c.subでコンパイルした場合、コンパイラの
中間ファイルとして拡張子が i のファイルが生成される。
(blib.cをコンパイルすると、blib.から生成されているblib.iが上書きされる)
これを防ぐため、オリジナルのblib.iはblib.orgとしておいた。
注意点4
pipでエラー
添付されていたmakefileを見ながら、intcodeのコンパイラをリンク
(リンカではなく、単にpipで結合するだけ)
pip st0.int=iclib.i,blib.org,syn.i,trn.i
Exception $03 at user address $000063C6. Aborted
Web上で類似の記載もあるが原因がさっぱりわからない。
pipの動作を追いかけるのは大変なので、Cでコピー用のプログラムを作成した。(concat.c)
concat st0.int iclib.i blib.org syn.i trn.i
concat cg0.int iclib.i blib.org cg.i
===concat.c=========================
#include <stdio.h>
int
main(argc, argv)
int argc;
char **argv;
{
FILE *fin, *fout, *fopen();
int i;
if (argc < 3) {
printf("Usage: %s [destfile] [srcfile].. \n", *argv);
return 1;
}
fout = fopen(argv[1], "w");
if (fout == NULL) {
fprintf(stderr, "%s: Can't open\n", argv[1]);
return 1;
}
for (i = 2; argv[i] != NULL; i++) {
fin = fopen(argv[i], "r");
if (fin == NULL) {
fprintf(stderr, "%s: Can't open\n", argv[i]);
return 1;
}
filecopy(fin, fout);
fclose(fin);
}
fclose(fout);
return 0;
}
filecopy(fin, fout)
FILE *fin, *fout;
{
int c;
while ((c = getc(fin)) != EOF)
putc(c, fout);
}
==============
注意点5
icint.c のコンパイル中に以下の警告が出る
(warning) more than 32767 bytes of local variables
ローカルでは駄目みたいなので、pgvec[VSIZE]をグローバルにした。
注意点6
68KのCコンパイラのバグ
icint.cインタプリタのデバッグ中に、条件分岐がおかしいことに気づいた
以下の部分(4の場合はAがtrue(0以外)の場合に分岐、5の場合はAがfalse(0)の場合に分岐)
case 4: A = !A;
case 5: if (!A) C = D; goto fetch;
やむなく68K Cで簡単なテストコードをコンパイルし途中のアセンブリコードを見ると以下のとおり。
「!」ではfalse(0)の場合は1をセットし、それ以外の場合は0をセット(clr)するようなコードであるが、
<---に示すように、longの場合は clr.l にすべきところが clrになっている。
C>type test.c
main()
{
int x;
long y;
x = 0xffff;
x = !x;
y = 0xffffffff;
y = !y;
}
C>type test.s
.globl _main
.text
_main:
~~main:
~_EnD__=8
link R14,#-10
~x=-2
~y=-6
*line 6
move #-1,-2(R14)
*line 7
move -2(R14),R0
beq L10000
clr R0 <--- 16bitの場合、16bitクリア
bra L10001
L10000:move #1,R0
L10001:move R0,-2(R14)
*line 8
move.l #$ffffffff,-6(R14)
*line 9
move.l -6(R14),R0
beq L10002
clr R0 <--- 32bitの場合も、16bitクリア clr.lにすべき
bra L10003
L10002:move #1,R0
L10003:move.l R0,-6(R14)
L1:
unlk R14
rts
.data
仕方ないので、cのソースを以下のように変更した。この方がソースも素直だよなあ。
case 4: if (A) C = D; goto fetch;
case 5: if (!A) C = D; goto fetch;
以上でcmpltest.bをコンパイルし、エラーなく実行出来た。
C>ICINT ST0.INT <CMPLTEST.B
INTCODE SYSTEM ENTERED
PROGRAM SIZE = 9756
BCPL 2
OPTIONS L7500
TREE SIZE 4772
PHASE 1 COMPLETE
EXECUTION CYCLES = 1264094, CODE = 0
C>ICINT CG0.INT <OCODE
INTCODE SYSTEM ENTERED
PROGRAM SIZE =" "3317
PROGRAM" LENGTH =" "1840
EXECUTION" CYCLES =" "977419," CODE =" "0
C">CONCAT CMPLTEST.INT ICLIB.I BLIB.ORG INTCODE
C>icint cmpltest.int
INTCODE SYSTEM ENTERED
PROGRAM SIZE = 2539
CGTESTER ENTERED
1 33 OK
2 303 OK
3 3 OK
4 44 OK
5 -1 OK
6 0 OK
7 1 OK
8 11 OK
9 101 OK
10 5 OK
11 15 OK
12 105 OK
13 1234 OK
14 5678 OK
15 75 OK
16 433 OK
17 150 OK
18 50 OK
19 3 OK
20 -3 OK
21 -3 OK
22 3 OK
23 2 OK
24 1 OK
25 26 OK
26 1 OK
27 5 OK
28 0 OK
29 -105 OK
30 -1 OK
31 0 OK
32 0 OK
33 -1 OK
34 0 OK
35 -1 OK
36 8168 OK
37 127 OK
38 8168 OK
39 127 OK
40 8 OK
41 14 OK
42 25 OK
43 6 OK
44 0 OK
45 -1 OK
46 -5171 OK
47 -105 OK
48 1000 OK
49 1000 OK
50 1234 OK
51 5678 OK
52 10000 OK
53 10000 OK
54 10000 OK
55 10001 OK
56 10001 OK
57 10200 OK
58 16 OK
59 16 OK
60 11 OK
101 -5 OK
102 -2 OK
103 105 OK
104 11 OK
105 15 OK
106 15 OK
107 16 OK
108 15 OK
109 22 OK
110 0 OK
111 15 OK
112 120 OK
201 0 OK
202 0 OK
203 369528 OK
204 385136 OK
251 21 OK
252 1 OK
253 2 OK
254 3 OK
255 4 OK
256 5 OK
257 6 OK
258 21 OK
259 21 OK
260 21 OK
261 2 OK
262 4 OK
263 6 OK
264 21 OK
265 2 OK
266 5 OK
267 21 OK
268 33400 OK
269 6 OK
270 21 OK
301 116 OK
302 116 OK
303 116 OK
304 1 OK
305 99 OK
306 21 OK
307 32 OK
308 41 OK
309 51 OK
401 0 OK
402 1 OK
403 2 OK
404 1 OK
405 102 OK
406 12 OK
407 11 OK
408 101 OK
409 102 OK
410 103 OK
411 103 OK
412 123 OK
413 123 OK
414 123 OK
119 TESTS COMPLETED, 0 FAILURE(S)
EXECUTION CYCLES = 101599, CODE = 0
Hello, Worldだけではもったいない。
ただこのCコンパイラは少し古い。Web上にCソースが公開されているが、今はansi準拠がほとんど。
簡単にはコンパイル出来ないため書き換える必要があるが、大掛かりなものは面倒なので小物を探す。
そこでbcplkit-0.9.7(https://www.cl.cam.ac.uk/~mr10/)を使用することにした。
これはbpclのコンパイラで、bcplソースをINTCODEと言われるコードにコンパイルし、INTCODEをインタプリタで実行するもの。
コンパイラ自身はbcplで書かれているが、コンパイルした結果のINTCODE(syn.iやtrn.i)は用意されている。
このため、INTCODEインタプリタを用意すればbcplコンパイラが動くようになる。
このINTCODEインタプリタ(icint.c)やライブラリ(blib.c)はCで書かれたものが用意されている。
icint.cは340行程度、blib.cは170行程度であり、68KのCコンパイラで動くようにするのは
比較的容易だと思われた。
ところが簡単には動かない。
とりあえず動かすところまで出来たが、動作させるまでいろいろ注意点が出たのでまとめておく。
なおSKEDエディタには大変お世話になりました。(https://piclabo.blog.ss-blog.jp/CPM68K_SKED)
懐かしいダイヤモンドカーソルのスクリーンエディタで助かりました。
CP/M 68K用のスクリーンエディタを公開したskyriverさんに感謝します。
注意点1
bcplkit-0.9.7は当然ファイルフォーマットがunix
改行がLFになっており、cpmtoolsを使用してSDカードに書き込んだ場合CRLFでないため表示が崩れる。
またEOF(cZ)がないため、ファイルの最後にゴミがふくまれてしまう。
cpmcp で オプション -t があるが、バイナリとテキストにわけでコピーする必要があるので面倒。
そこで事前にlinux上でテキストファイルだけ nkfでLF -> CRLFにする(nkf -c --overwrite ファイル)。
またファイルの最後にcZを入れた(echo -e $'\x1a' >> ファイル)
注意 echo -e はbashの機能のようで、最初 #!/bin/sh でスクリプト組んでいたが -e$\x1a という
文字列が最後に入ってしまった。 #!/bin/bash で意図どおり動いた。
注意点2
CP/M 68のCコンパイラはKANSIじゃないので引数のタイプに注意。昔懐かしい書き方に変更(icint.c, blib.c)
int main(int argc, char *argv[])
↓
int main(argc, argv)
int argc;
char **argv;
{
プロトタイプ宣言でも(blib.h)
int getbyte(int, int); -> long getbyte();
void putbyte(int, int, int); -> void putbyte();
void initio(void); -> void initio();
その他不要な宣言#include <stdlib.h> などを削除 (68KのCコンパイラにはない)
注意点3
68KのCコンパイラはintが16bitだった。
bcplkitは32bitを想定しているがソースに「#define WORDSIZE 32」とあるので、
ここを修正すればと思っていたが、簡単ではなかった。
blib.cの中に以下のように32bitを前提としたコードがあった。
int
getbyte(int s, int i)
{
int w = M[s + i / 4];
int m = (i % 4) ^ 3;
w = w >> (8 * m);
return w & 255;
}
Cのコードだけなら修正すればと思っていたが、bcplで書かれたライブラリblib.bにも
32bit前提の箇所があった。
AND PACKSTRING(V, S) = VALOF
$( LET N = V!0 & 255
LET I = N/4
FOR P = 0 TO N DO PUTBYTE(S, P, V!P)
SWITCHON N&3 INTO
$( CASE 0: PUTBYTE(S, N+3, 0)
CASE 1: PUTBYTE(S, N+2, 0)
CASE 2: PUTBYTE(S, N+1, 0)
CASE 3: $)
RESULTIS I $)
bcplで書かれたライブラリは準備されているINTCODE(blib.i)に含まれているわけで、
Cのコードだけ変えればOKとはならない。慣れないintcodeを修正も避けたい。
仕方ないのでicint.c, blib.cのintをlongにして、32ビットで動かすことにした。
なおコンパイル用のバッチファイル c.subでコンパイルした場合、コンパイラの
中間ファイルとして拡張子が i のファイルが生成される。
(blib.cをコンパイルすると、blib.から生成されているblib.iが上書きされる)
これを防ぐため、オリジナルのblib.iはblib.orgとしておいた。
注意点4
pipでエラー
添付されていたmakefileを見ながら、intcodeのコンパイラをリンク
(リンカではなく、単にpipで結合するだけ)
pip st0.int=iclib.i,blib.org,syn.i,trn.i
Exception $03 at user address $000063C6. Aborted
Web上で類似の記載もあるが原因がさっぱりわからない。
pipの動作を追いかけるのは大変なので、Cでコピー用のプログラムを作成した。(concat.c)
concat st0.int iclib.i blib.org syn.i trn.i
concat cg0.int iclib.i blib.org cg.i
===concat.c=========================
#include <stdio.h>
int
main(argc, argv)
int argc;
char **argv;
{
FILE *fin, *fout, *fopen();
int i;
if (argc < 3) {
printf("Usage: %s [destfile] [srcfile].. \n", *argv);
return 1;
}
fout = fopen(argv[1], "w");
if (fout == NULL) {
fprintf(stderr, "%s: Can't open\n", argv[1]);
return 1;
}
for (i = 2; argv[i] != NULL; i++) {
fin = fopen(argv[i], "r");
if (fin == NULL) {
fprintf(stderr, "%s: Can't open\n", argv[i]);
return 1;
}
filecopy(fin, fout);
fclose(fin);
}
fclose(fout);
return 0;
}
filecopy(fin, fout)
FILE *fin, *fout;
{
int c;
while ((c = getc(fin)) != EOF)
putc(c, fout);
}
==============
注意点5
icint.c のコンパイル中に以下の警告が出る
(warning) more than 32767 bytes of local variables
ローカルでは駄目みたいなので、pgvec[VSIZE]をグローバルにした。
注意点6
68KのCコンパイラのバグ
icint.cインタプリタのデバッグ中に、条件分岐がおかしいことに気づいた
以下の部分(4の場合はAがtrue(0以外)の場合に分岐、5の場合はAがfalse(0)の場合に分岐)
case 4: A = !A;
case 5: if (!A) C = D; goto fetch;
やむなく68K Cで簡単なテストコードをコンパイルし途中のアセンブリコードを見ると以下のとおり。
「!」ではfalse(0)の場合は1をセットし、それ以外の場合は0をセット(clr)するようなコードであるが、
<---に示すように、longの場合は clr.l にすべきところが clrになっている。
C>type test.c
main()
{
int x;
long y;
x = 0xffff;
x = !x;
y = 0xffffffff;
y = !y;
}
C>type test.s
.globl _main
.text
_main:
~~main:
~_EnD__=8
link R14,#-10
~x=-2
~y=-6
*line 6
move #-1,-2(R14)
*line 7
move -2(R14),R0
beq L10000
clr R0 <--- 16bitの場合、16bitクリア
bra L10001
L10000:move #1,R0
L10001:move R0,-2(R14)
*line 8
move.l #$ffffffff,-6(R14)
*line 9
move.l -6(R14),R0
beq L10002
clr R0 <--- 32bitの場合も、16bitクリア clr.lにすべき
bra L10003
L10002:move #1,R0
L10003:move.l R0,-6(R14)
L1:
unlk R14
rts
.data
仕方ないので、cのソースを以下のように変更した。この方がソースも素直だよなあ。
case 4: if (A) C = D; goto fetch;
case 5: if (!A) C = D; goto fetch;
以上でcmpltest.bをコンパイルし、エラーなく実行出来た。
C>ICINT ST0.INT <CMPLTEST.B
INTCODE SYSTEM ENTERED
PROGRAM SIZE = 9756
BCPL 2
OPTIONS L7500
TREE SIZE 4772
PHASE 1 COMPLETE
EXECUTION CYCLES = 1264094, CODE = 0
C>ICINT CG0.INT <OCODE
INTCODE SYSTEM ENTERED
PROGRAM SIZE =" "3317
PROGRAM" LENGTH =" "1840
EXECUTION" CYCLES =" "977419," CODE =" "0
C">CONCAT CMPLTEST.INT ICLIB.I BLIB.ORG INTCODE
C>icint cmpltest.int
INTCODE SYSTEM ENTERED
PROGRAM SIZE = 2539
CGTESTER ENTERED
1 33 OK
2 303 OK
3 3 OK
4 44 OK
5 -1 OK
6 0 OK
7 1 OK
8 11 OK
9 101 OK
10 5 OK
11 15 OK
12 105 OK
13 1234 OK
14 5678 OK
15 75 OK
16 433 OK
17 150 OK
18 50 OK
19 3 OK
20 -3 OK
21 -3 OK
22 3 OK
23 2 OK
24 1 OK
25 26 OK
26 1 OK
27 5 OK
28 0 OK
29 -105 OK
30 -1 OK
31 0 OK
32 0 OK
33 -1 OK
34 0 OK
35 -1 OK
36 8168 OK
37 127 OK
38 8168 OK
39 127 OK
40 8 OK
41 14 OK
42 25 OK
43 6 OK
44 0 OK
45 -1 OK
46 -5171 OK
47 -105 OK
48 1000 OK
49 1000 OK
50 1234 OK
51 5678 OK
52 10000 OK
53 10000 OK
54 10000 OK
55 10001 OK
56 10001 OK
57 10200 OK
58 16 OK
59 16 OK
60 11 OK
101 -5 OK
102 -2 OK
103 105 OK
104 11 OK
105 15 OK
106 15 OK
107 16 OK
108 15 OK
109 22 OK
110 0 OK
111 15 OK
112 120 OK
201 0 OK
202 0 OK
203 369528 OK
204 385136 OK
251 21 OK
252 1 OK
253 2 OK
254 3 OK
255 4 OK
256 5 OK
257 6 OK
258 21 OK
259 21 OK
260 21 OK
261 2 OK
262 4 OK
263 6 OK
264 21 OK
265 2 OK
266 5 OK
267 21 OK
268 33400 OK
269 6 OK
270 21 OK
301 116 OK
302 116 OK
303 116 OK
304 1 OK
305 99 OK
306 21 OK
307 32 OK
308 41 OK
309 51 OK
401 0 OK
402 1 OK
403 2 OK
404 1 OK
405 102 OK
406 12 OK
407 11 OK
408 101 OK
409 102 OK
410 103 OK
411 103 OK
412 123 OK
413 123 OK
414 123 OK
119 TESTS COMPLETED, 0 FAILURE(S)
EXECUTION CYCLES = 101599, CODE = 0