石原 博の覚書

電子工作に関する日々の覚書を記載します

CP/M-68Kでbcpl

2024-06-08 13:09:50 | 日記
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



CP/M 68K で C言語

2024-04-14 22:30:02 | 日記
せっかくCP/M 68K でCが動くので、プログラムを動かした

Web上にCのソースは色々あるが、今となってはansi準拠がほとんど。

CP/M 68K のCは古いので、いろいろ対応していないため書き換える必要がある。大掛かりなものは面倒なので小物を探す。
手頃なものがあったので、書き換えてコンパイルまでしてみたが動かない。(エラーじゃなくて固まる)

昔を思い出しながらddtでデバッグすると、メモリのないところをアクセスして止まっている。
($FFFFCA28+$C になんてメモリがない)

link A6,#$0
PC=00009582 USP=00036546 SSP=00005D26 ST=0008=>IM=0 NEG
D 00000008 00036247 00000000 00000000 00000000 00000000 00000000 00000005
A 0000C838 00000008 00008081 00000000 00000000 00008086 00036546 00036546

movem.l D7-D7/A5-A5,-(A7)
PC=00009586 USP=0003653E SSP=00005D26 ST=0008=>IM=0 NEG
D 00000008 00036247 00000000 00000000 00000000 00000000 00000000 00000005
A 0000C838 00000008 00008081 00000000 00000000 00008086 00036546 0003653E

movea.l $8(A6),A5
PC=0000958A USP=0003653E SSP=00005D26 ST=0008=>IM=0 NEG
D 00000008 00036247 00000000 00000000 00000000 00000000 00000000 00000005
A 0000C838 00000008 00008081 00000000 00000000 FFFFCA28 00036546 0003653E

subq.w #$1,$C(A5)


そういえば、コンパイル時にwarningが出てた。警告なので無視したけど。
(warning) short assigned to pointer

そういえば このCはintは16bitでポインタは32bitだった。
該当箇所のアッセンブリソースを見ると以下のとおりfopenの返り値を符号拡張している。そりゃだめだ。
fp = fopen("test.txt", "r");

move.l #L3,(sp)
move.l #L2,-(sp)
jsr _fopen
addq.l #4,sp
ext.l R0
move.l R0,-4(R14)

特にCPM400SRを使っているのでTPAが$8000以降のため、返り値が符号拡張されfgetc(fp)で、
とんでもないアドレスをアクセスして固まったものと思われる。

本来 FILE *fopen(); がstdio.hにあるべきだと思ったんだが?

とりあえずソースの最初に「File *fopen();」を入れてコンパイルすれば警告も出ず、正常に動作させることが出来た。

2024/4/15追記
stdio.hはhttp:/www.cpm.z80.de/binary.htmlからダウンロードしたCP/M-68K BINARY version1.3
のものだがおかしい。後半が消えているようだ。
同ページの MICRO EMACS for CP/M-68K の中のstdio.hには、FILE *fopen();がある。
他にもヘッダファイルが途中までのようなものがいくつかある。 なぜだろう。

CP/M68K SDカード内容整理

2024-03-20 12:53:57 | 日記
CP/M68Kが一応動き出したがプログラムのやり取りが面倒

linux側から毎回cpmtoolsを使用してSDカードに書き込みしていたが、
逆にSDカードからlinuxへの読み出しを試みた。

SDカードのイメージを dd でdiskimagecに読み出したあと、
cpmcpでファイルに戻す
>cpmcp -f 8mb diskimagec 0:* diskc

ところが「malloc(): invalid size (unsorted)」というエラーが出る。
確保した領域以上になにかしているのかなあ。素人だから良くわからない。

githubでcpmtoolsがあった(https://github.com/lipro-cpm4l/cpmtools)のでダウンロード。
unofficial forkらしい。とりあえずソースを読むがやっぱりわからない。

gdbで動きを見ようと
./configure CFLAGS="-g O0"
make
sudo make install
とすると動いた??
(apt get install では /usr/binだが、コンパイルすると/usr/local/binにインストールされる)

あれっと思い、今度はconfigureにオプションをつけずにコンパイル
./configure
make clean
make
sudo make install
すると今度も動いた???

debianのレポジトリのcpmtoolsにバグがあったのか、コンパイラのバージョン違いか
良くわからないが、まあ動いたので良しとしよう。

これでSD内のファイル整理がしやすくなる。

FDD-USB(USB_FDD@1306)が動いた

2024-03-17 12:54:47 | 日記
先日Arduinoを利用したFDD読み取りができたが、別にアマゾンで1800円程度のボードがあるのを見つけた
話のネタに注文。少し時間がかかったが到着。


動かない。

良く見ると半田ブリッジ。出荷検査してないのか? これでは動くわけないやと修正。


一応電源用のケーブルはあるが、これを使ってもモーターがちゃんと動かない(カタカタいうけど回らない)。
調べて見るとMITSUMI D353M3Dは5V 1.6W 一応USB2.0の範囲内とも言えなくもないが、
こんな細い線でモーターを動かすのは問題。

外部からドライブの電源を接続。


lsusbでは
Bus 001 Device 043: ID 0644:0000 TEAC Corp. Floppy
と表示される。

一応3.5inchフロッピーディスクを読めることが確認できた。


KIM Unoを動かした

2024-03-10 14:46:08 | 日記
たまたまKIM Unoの基板が400円というのをオークションサイトで見かけた

手持ちでArduino Prominiはあるし、タクトスイッチも以前にアマゾンで買った中華製が大量にある。
そこで作ってみることにした。
https://obsolescence.wixsite.com/obsolescence/kim-uno-details

問題はLED。仕様上は「3461BS」らしいが、手にはいりそうにない。
4桁アノードコモンのLEDを日本橋で探し、大きさが違うが足が細くて長いのが
デジットであったため、これを基板にあうように曲げて接続。


表にLED、裏にArduino Prominiで重なっているため、失敗すると面倒。
そこでLEDもAruduino Prominiもピンヘッダを利用した。
(Arduinoは細pin headerオス)


一応動くところまで確認。(ダイナミックドライブのため、写真では一部しか光ってないように見える)


ただマニュアルを見てもメモリの読み出し書き込み程度はわかるが、動作が良くわからない。
(vtlとか逆アセンブラとか記載にあるが、あのスイッチと7セグメントLEDでどうするの)

そこでinoファイルを読んでみた。

config.hは以下のとおり。STMやESP32でも動くプログラムらしい。
#define _TARGET ARDUINO_MINI
//#define _TARGET BLUEPILL_STM
//#define _TARGET ESP32_R
//#define _TARGET PERSONAL_COMPUTER

さらに、ProMiniでは全部同時には動かないとある。デフォールトでは以下のtrueだけ。
//-------------------------------------------------------------
//----- Programs to include in ROM, set any combination to TRUE
//----- (as long as target has space: Pro Mini can't have all)
//-------------------------------------------------------------
#define _INCL_DISASM FALSE
#define _INCL_MINIASM TRUE
#define _INCL_MCHESS TRUE
#define _INCL_CALCULATOR TRUE
#define _INCL_VTL02 FALSE
#define _INCL_A1BASIC FALSE
#define _INCL_ACI TRUE
#define _INCL_LIFE FALSE

ProMiniのシリアルにも文字は出てくるので、VTLや逆アセンブラはそれで動くのだなと
納得したが、それならLEDやタクトスイッチは電卓とモニタぐらいにしか使わないわけか。

各プログラムは以下のようにアドレス指定で実行して動かすようだ。
// All ROMS together do not fit in a tiny Arduino Pro Mini,
// ---> make your selection in config.h.
// If you copy to RAM, you also have to choose which programs are loaded.
//
//
// --- ROM MAP ----------------------------------------------------------------
// ROM1: KIM-1 ROM002 (monitor main code) $17C0
// ROM2: KIM-1 ROM003 (tape and RS-232 code) $1780
// disassembler: Baum & Wozniak disassember $2000
// calc: 6502 floating point library from crbond.com/calc65.htm $5000
// special I/O area for programmable calculator (part of calc@5000) $7000
// mchess: microchess from benlo.com/microchess recompiled to start at $C000
// Apple-1 Basic $E000
// vtl-02: from 6502.org $DC00
// Apple-1 ACI (cassette tape ROM) $F100
// Baum-Wozniak mini-assembler (run $FDDD) & Wozmon ($FF $FBAE
// ----------------------------------------------------------------------------


SST(シングルステップ?)はオリジナルではスライドスイッチで、
ここではタクトスイッチでトグル動作のため、シリアル接続画面で状態は見えるのだが、
それがなければ現在の状態がどうか判然としない

もう少し調べよう。