Skip to content

Commit b38e251

Browse files
authored
Merge pull request #177 from juollila/amigacpm
Add support for Amiga CP/M-68k target
2 parents 730938c + be6ea34 commit b38e251

File tree

22 files changed

+1827
-3
lines changed

22 files changed

+1827
-3
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ The following targets are supported. Adding more is easy.
5353

5454
- PowerPC, on Linux.
5555

56-
- 68000, on Atari ST TOS (although most of the system calls aren't hooked up
56+
- 68000, on Atari ST TOS and Amiga CP/M-68k (although most of the system calls aren't hooked up
5757
yet so you can't do anything more than print stuff) and Linux m68k (if you
5858
can still find a machine which will run this).
5959

@@ -258,6 +258,10 @@ written by other people and are not covered by this license. This directory as
258258
a whole contains GPL software, which means that if you redistribute the entire
259259
directory, you must conform to the terms of the GPL.
260260

261+
`third_party/amigacpm` contains a ld linker script for Amiga CP/M-68k,
262+
(https://github.com/juollila/cpm68k-amiga). The linker script is in the public
263+
domain.
264+
261265
`third_party/lib6502` contains a hacked copy of the lib6502 library, which is ©
262266
2005 Ian Plumarta and is available under the terms of the MIT license. See
263267
`third_party/lib6502/COPYING.lib6502` for the full text.

build.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@
3434
]
3535
+ (["dist/msdos"] if config.has_msdos else [])
3636
+ (["dist/ataritos"] if config.has_ataritos else [])
37+
+ (["dist/amigacpm"] if config.has_amigacpm else [])
3738
+ (["dist/bbct"] if config.has_bbct else []),
3839
)

config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def enable_if(command):
1818
has_qemu68k = enable_if("qemu-m68k")
1919

2020
has_ataritos = has_gccataritos
21+
has_amigacpm = has_gccataritos
2122
has_msdos = has_nasm
2223
has_lx386 = has_gcc386
2324
has_lxthumb2 = has_gccthumb2

dist/amigacpm/build.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from build.ab import export
2+
from tools.build import tocpm
3+
4+
tocpm(name="demosub", src="./demo.sub")
5+
6+
export(
7+
name="amigacpm",
8+
items={
9+
"bin/dist/amigacpm/mandel.cow": "examples/mandel.cow",
10+
"bin/dist/amigacpm/cowgol.coh": "rt/amigacpm/cowgol.coh",
11+
"bin/dist/amigacpm/common.coh": "rt/common.coh",
12+
"bin/dist/amigacpm/cowgol.coo": "rt/amigacpm+cowgolcoo",
13+
"bin/dist/amigacpm/cowfe.com": "src/cowfe+cowfe-for-32bita2-with-amigacpm",
14+
"bin/dist/amigacpm/cowbe.com": "src/cowbe+cowbe-for-68000-with-amigacpm",
15+
"bin/dist/amigacpm/cowlink.com": "src/cowlink+cowlink-for-amigacpm-with-amigacpm",
16+
},
17+
)

doc/building.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,17 @@ $ ./helloworld
113113
Hello, world!
114114
```
115115

116+
Building of Amiga CP/M-68k binary uses m68k-atari-mint binutis and a custom `amigacpm.ld` linker
117+
script. To run the cross compiler to generate a Amiga CP/M-68k binary, do:
118+
119+
```
120+
$ bin/cowfe-for-32bita2-with-nncgen -Irt/ -Irt/amigacpm/ examples/helloworld.cow helloworld.cob
121+
$ bin/cowbe-for-68000-with-nncgen helloworld.cob helloworld.coo
122+
$ bin/cowlink-for-amigacpm-with-nncgen .obj/rt/amigacpm/+cowgolcoo/cowgol.coo helloworld.coo -o helloworld.s
123+
$ m68k-atari-mint-as helloworld.s -o helloworld.o
124+
$ m68k-atari-mint-ld -T third_party/amigacpm/amigacpm.ld -o hello.68k helloworld.o
125+
```
126+
116127
cowfe and cowbe
117128
---------------
118129

rt/amigacpm/argv.coh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
var argv_pointer: [uint8];
2+
3+
sub ArgvInit() is
4+
argv_pointer := 0x481 as [uint8];
5+
[argv_pointer + [0x480 as [uint8]] as intptr] := 0;
6+
end sub;
7+
8+
# Returns null is there's no next argument.
9+
sub ArgvNext(): (arg: [uint8]) is
10+
# No more arguments?
11+
12+
if argv_pointer == nil then
13+
arg := argv_pointer;
14+
return;
15+
end if;
16+
17+
# Skip leading whitespace.
18+
19+
var c: uint8;
20+
loop
21+
c := [argv_pointer];
22+
if c != ' ' then
23+
break;
24+
end if;
25+
argv_pointer := argv_pointer + 1;
26+
end loop;
27+
28+
arg := argv_pointer;
29+
30+
# Skip to end of word and terminate.
31+
32+
loop
33+
c := [argv_pointer];
34+
if (c == ' ') or (c == '\n') or (c == 0) then
35+
break;
36+
end if;
37+
argv_pointer := argv_pointer + 1;
38+
end loop;
39+
[argv_pointer] := 0;
40+
41+
if c == ' ' then
42+
argv_pointer := argv_pointer + 1;
43+
else
44+
argv_pointer := 0 as [uint8];
45+
end if;
46+
end sub;
47+
48+

rt/amigacpm/build.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from src.build import cowwrap
2+
3+
cowwrap(name="cowgolcoo", src="rt/common-68000/cowgol.cos")

rt/amigacpm/cowgol.coh

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
var LOMEM: [uint8];
2+
@asm "move.l #LOMEM, (", LOMEM, ")";
3+
4+
var HIMEM: [uint8];
5+
@asm "move.l #HIMEM, (", HIMEM, ")";
6+
7+
sub Exit() is
8+
@asm "move.w #0, %d0"; # BDOS system reset
9+
@asm "trap #2";
10+
end sub;
11+
12+
sub ExitWithError() is
13+
@asm "move.w #0, %d0"; # BDOS system reset
14+
@asm "trap #2";
15+
end sub;
16+
17+
sub AlignUp(in: intptr): (out: intptr) is
18+
out := in;
19+
end sub;
20+
21+
sub get_char(): (c: uint8) is
22+
@asm "move.w #1, %d0"; # BDOS console input
23+
@asm "trap #2";
24+
@asm "move.b %d0, (", c ,")";
25+
end sub;
26+
27+
sub print_char(c: uint8) is
28+
if c == 10 then
29+
@asm "move.w #13, %d1";
30+
@asm "move.w #2, %d0"; # BDOS console output
31+
@asm "trap #2";
32+
end if;
33+
34+
@asm "move.b (", c, "), %d1";
35+
@asm "and.w #255, %d1";
36+
@asm "move.w #2, %d0"; # BDOS console output
37+
@asm "trap #2";
38+
end sub;
39+
40+
sub MemSet(buf: [uint8], byte: uint8, len: intptr) is
41+
var bufend := buf + len;
42+
loop
43+
if buf == bufend then
44+
return;
45+
end if;
46+
[buf] := byte;
47+
buf := buf + 1;
48+
end loop;
49+
end sub;
50+
51+
include "common.coh";
52+
53+
54+

rt/amigacpm/file.coh

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# vim: ts=4 sw=4 et
2+
3+
record CpmFCB is
4+
dr: uint8;
5+
f: uint8[11];
6+
ex: uint8;
7+
s1: uint8;
8+
s2: uint8;
9+
rc: uint8;
10+
d: uint32[4]; # aligns also CpmFCB to even address
11+
cr: uint8;
12+
r: uint8[3];
13+
end record;
14+
15+
record FCB is
16+
bufferptr: uint8; # byte just read
17+
dirty: uint8;
18+
cpm: CpmFCB;
19+
buffer: uint8[128];
20+
end record;
21+
22+
sub fcb_set_record(fcb: [FCB], recordno: uint32) is
23+
recordno := recordno & 0x3ffff;
24+
fcb.cpm.r[0] := ((recordno >> 16) & 0x3) as uint8;
25+
fcb.cpm.r[1] := ((recordno >> 8) & 0xff) as uint8;
26+
fcb.cpm.r[2] := (recordno & 0xff) as uint8;
27+
end sub;
28+
29+
sub fcb_get_record(fcb: [FCB]): (recordno: uint32) is
30+
var r0 := (fcb.cpm.r[0] as uint32) << 16;
31+
var r1 := (fcb.cpm.r[1] as uint32) << 8;
32+
var r2 := fcb.cpm.r[2] as uint32;
33+
recordno := (r0 + r1 + r2) & 0x3ffff;
34+
end sub;
35+
36+
sub file_i_init(fcb: [FCB], filename: [uint8]) is
37+
sub fill(dest: [uint8], src: [uint8], len: uint8): (srcout: [uint8]) is
38+
loop
39+
var c := [src];
40+
if (c < 32) or (c == '.') then
41+
c := ' ';
42+
elseif (c == '*') then
43+
c := '?';
44+
else
45+
src := src + 1;
46+
end if;
47+
if (c >= 'a') and (c <= 'z') then
48+
c := c - ('a' - 'A');
49+
end if;
50+
[dest] := c;
51+
dest := dest + 1;
52+
53+
len := len - 1;
54+
if len == 0 then
55+
break;
56+
end if;
57+
end loop;
58+
srcout := src;
59+
end sub;
60+
61+
MemSet(fcb as [uint8], 0, @bytesof FCB);
62+
MemSet(&fcb.cpm.f[0] as [uint8], ' ', 11);
63+
filename := fill(&fcb.cpm.f[0], filename, 8);
64+
65+
var c: uint8;
66+
loop
67+
c := [filename];
68+
if (c < 32) or (c == '.') then
69+
break;
70+
end if;
71+
filename := filename + 1;
72+
end loop;
73+
74+
if c == '.' then
75+
filename := fill(&fcb.cpm.f[8], filename+1, 3);
76+
end if;
77+
78+
fcb_set_record(fcb, 0x3ffff);
79+
fcb.bufferptr := 127;
80+
end sub;
81+
82+
sub fcb_i_gbpb(fcb: [FCB], c: uint16) is
83+
var cpmfcb := &fcb.cpm;
84+
var dma := &fcb.buffer[0];
85+
86+
@asm "move.w #26, %d0"; # BDOS set dma
87+
@asm "move.l (", dma, "), %d1";
88+
@asm "trap #2";
89+
90+
@asm "move.w (", c, "), %d0"; # BDOS call defined in c
91+
@asm "move.l (", cpmfcb, "), %d1";
92+
@asm "trap #2";
93+
end sub;
94+
95+
sub fcb_i_blockin(fcb: [FCB]) is
96+
MemSet(&fcb.buffer[0], 0, 128);
97+
fcb_i_gbpb(fcb, 33); # READ RANDOM
98+
fcb.dirty := 0;
99+
end sub;
100+
101+
sub fcb_i_blockout(fcb: [FCB]) is
102+
if fcb.dirty != 0 then
103+
fcb_i_gbpb(fcb, 34); # WRITE RANDOM
104+
fcb.dirty := 0;
105+
end if;
106+
end sub;
107+
108+
sub fcb_i_changeblock(fcb: [FCB], newblock: uint32) is
109+
if newblock != fcb_get_record(fcb) then
110+
fcb_i_blockout(fcb);
111+
fcb_set_record(fcb, newblock);
112+
fcb_i_blockin(fcb);
113+
end if;
114+
end sub;
115+
116+
sub fcb_convert_return_code_to_error(rc: uint8): (errno: uint8) is
117+
if rc == 0xff then
118+
errno := 1;
119+
else
120+
errno := 0;
121+
end if;
122+
end sub;
123+
124+
sub FCBOpenIn(fcb: [FCB], filename: [uint8]): (errno: uint8) is
125+
file_i_init(fcb, filename);
126+
var cpmfcb := &fcb.cpm;
127+
128+
@asm "move.w #15, %d0"; # BDOS open file
129+
@asm "move.l (", cpmfcb, "), %d1";
130+
@asm "trap #2";
131+
@asm "move.b %d0, (", errno, ")";
132+
errno := fcb_convert_return_code_to_error(errno);
133+
end sub;
134+
135+
sub FCBOpenUp(fcb: [FCB], filename: [uint8]): (errno: uint8) is
136+
(errno) := FCBOpenIn(fcb, filename);
137+
end sub;
138+
139+
sub FCBOpenOut(fcb: [FCB], filename: [uint8]): (errno: uint8) is
140+
file_i_init(fcb, filename);
141+
var cpmfcb := &fcb.cpm;
142+
143+
@asm "move.w #19, %d0"; # BDOS delete file
144+
@asm "move.l (", cpmfcb, "), %d1";
145+
@asm "trap #2";
146+
147+
@asm "move.w #22, %d0"; # BDOS make file
148+
@asm "move.l (", cpmfcb, "), %d1";
149+
@asm "trap #2";
150+
@asm "move.b %d0, (", errno, ")";
151+
errno := fcb_convert_return_code_to_error(errno);
152+
end sub;
153+
154+
sub FCBClose(fcb: [FCB]): (errno: uint8) is
155+
fcb_i_blockout(fcb);
156+
var cpmfcb := &fcb.cpm;
157+
158+
@asm "move.w #16, %d0"; # BDOS close file
159+
@asm "move.l (", cpmfcb, "), %d1";
160+
@asm "trap #2";
161+
@asm "move.b %d0, (", errno, ")";
162+
errno := fcb_convert_return_code_to_error(errno);
163+
end sub;
164+
165+
sub FCBSeek(fcb: [FCB], pos: uint32) is
166+
pos := pos - 1; # seek to *previous* character
167+
var newblock := (pos >> 7) as uint32;
168+
var newptr := (pos as uint8) & 127;
169+
fcb_i_changeblock(fcb, newblock);
170+
fcb.bufferptr := newptr;
171+
end sub;
172+
173+
sub FCBPos(fcb: [FCB]): (pos: uint32) is
174+
pos := ((fcb_get_record(fcb) << 7) | (fcb.bufferptr as uint32)) + 1;
175+
end sub;
176+
177+
sub FCBExt(fcb: [FCB]): (len: uint32) is
178+
var oldblock := fcb_get_record(fcb);
179+
var cpmfcb := &fcb.cpm;
180+
181+
@asm "move.w #16, %d0"; # BDOS close file
182+
@asm "move.l (", cpmfcb, "), %d1";
183+
@asm "trap #2";
184+
185+
186+
@asm "move.w #35, %d0"; # BDOS compute file size
187+
@asm "move.l (", cpmfcb, "), %d1";
188+
@asm "trap #2";
189+
190+
len := fcb_get_record(fcb) << 7;
191+
fcb_set_record(fcb, oldblock);
192+
end sub;
193+
194+
sub fcb_i_nextchar(fcb: [FCB]) is
195+
fcb.bufferptr := fcb.bufferptr + 1;
196+
if fcb.bufferptr == 128 then
197+
fcb_i_changeblock(fcb, fcb_get_record(fcb) + 1);
198+
fcb.bufferptr := 0;
199+
end if;
200+
end sub;
201+
202+
sub FCBGetChar(fcb: [FCB]): (c: uint8) is
203+
fcb_i_nextchar(fcb);
204+
c := fcb.buffer[fcb.bufferptr];
205+
end sub;
206+
207+
sub FCBPutChar(fcb: [FCB], c: uint8) is
208+
fcb_i_nextchar(fcb);
209+
fcb.buffer[fcb.bufferptr] := c;
210+
fcb.dirty := 1;
211+
end sub;
212+
213+
include "common-file.coh";
214+

0 commit comments

Comments
 (0)