Cross compiling to the Amiga with Sozobon

To start this fun voyage, I used HCC, the first usable port of Sozobon C to the Amiga I could track down.  From it’s description:

Amiga port of Sozobon, Limited’s C Compiler. Can completely compile itself, supports 32 bit ints, and optimizer can ‘registerize’ variables. Includes compiler, optimizer, tool for creating interface code for Amiga system calls, startup code, C library, include files, and library routines that work with Motorola FFP format. Uses assembler A68k, linker BLink, and provided run-time shared C library CClib.library.

From the readme

And isn’t that great?  It even supports 32 bit integers!  I had to massage things in Visual C++, as there was some weird instances of return codes missing, and the optimizer not actually mallocing it’s memory, but just blindly using pointers.  As always if you can see what is going on in a debugger it’s not too hard to make some wild guesses and get it running, and if you get lucky it may even work too…

Running the compiler

With the compiler and optimizer running (it is actually needed to run to further massage the assembly output into something the Amiga a68k assembler can read), it was time to look at an assembler.  For the heck of it, I did try a68k, and to my amazement it did actually work, once I had updated the file output call.

hcc\hcc -L hanoi.c
hcc: version 2.0 Copyright (c) 1988,1989,1991 by Sozobon, Limited.
Amiga Version 1.1 by Detlef W³rkner.
hanoi.c:

top\top -v hanoi.s h2.s
top Version 2.00 Copyright (c) 1988-1991 by Sozobon, Limited.
Amiga Version 1.1 by Detlef W³rkner.
hanoi.s:
Peephole changes (1): 8
Peephole changes (2): 1
Peephole changes (3): 0
Instructions deleted: 3
Variables registered: 0
Loop rotations : 0
Branch reversals : 0
Branches removed : 4

a68k\a68k -q100 h2.s
68000 Assembler - version 2.61 (January 11, 1990)
Copyright 1985 by Brian R. Anderson
AmigaDOS conversion copyright 1989 by Charlie Gibbs.

Assembling h2.s
PASS 1 line 59
PASS 2 line 59
End of assembly - no errors were found.
Heap usage: -w2047,80
Total hunk sizes: 94 code, 10 data, 0 BSS

linking

Wow wasn’t that fun! I haven’t seen the source code to the BLINK linker, so I just end up using a native linker, BLINK.

Towers of Hanoi

Much to my amazement, the a68k assembler functions just fine as a cross assembler, and I only had to copy the object file into the emulator, and I could happily link.

The syntax for BLINK was a little strange, mostly because I really don’t know what I’m doing.

BLink LIB:HCC.o+hanoi.o LIB LIB:HCC.lib+LIB:stubs.lib TO hanoi SC SD VERBOSE

Now to try something bigger, like the ancient 1987 vintage InfoTaskForce.  I had to add in the include files from the DICE compiler, and surprisingly, in no time, it was all compiled, and assembled the only step remaining was to run the BLINK linker.  This time it was slightly different as now we had a bunch of object files:

BLink LIB:HCC.o+fileo.o+funcso.o+infocomo.o+inito.o+inputo.o+interpo.o+ioo.o+jumpo.o+objecto.o+optionso.o+pageo.o+printo.o+propertyo.o+supporto.o+variableo.o LIB LIB:HCC.lib+LIB:stubs.lib TO infocom SC SD VERBOSE

Running that as a single line (or better in a command file) got me my executable.

Linking

And it linked without any unresolved externals.

InfoTaskForce cross compiled on Windows, linked on AmigaDOS 2.0

Running under WinUAE

And even better, it worked. Here it is running Planetfall!

I can’t imagine it being all that useful for anyone, as Sozobon C is K&R C, and well this is for the Commodore Amiga, not exactly a mainstay in this day & age.

HCC_Sozobon_win32cross.7z This link will take you to the sourceforge page, and the archive contains both source, and executables.  As mentioned I didn’t see any Amiga linker that has source code, it seems everyone use BLINK, and the team that write BLINK went on to re-write all the ‘c’ commands in AmigaDOS from BCPL/asm into C.

***

Addendum

I just discovered vlink after writing this, and now I can link a working executable under Windows 10! Since I made zero changes to vlink, and I’m not charging money, I am free to redistribute this so I’ve updated my archive, and included it.

easy68k & sozobon

Easy68k

I know this is a little weird to follow, but I thought it was somewhat interesting. Anyways I’ve been reading up on some CP/M stuff, and found some interesting m68k stuff. There is this really cool m68k simulator/test environment called easy68k. Ok so the 68000 isn’t exactly the hottest chip, but for anyone that’s used a Mac, Amiga, Atari ST, SEGA Genesis, or old SUN the m68000 was the end all be all CPU. Anyways you can download the easy68k simulator from here:

http://www.easy68k.com/

Now I’ve never been really all that good with assembly. I know one day I should learn, but until then, there is higher level languages, and of course the best ‘medium’ level language C. Back in the day the ‘sozobon’ compiler was somewhat portable, and a good & easy 16 bit C compiler. Now as far as I know there really hasn’t been any activity on this since 1991, so it’s getting hard to find, and of course the archived copies I found needed LZH.. You can find some archived copies here:

http://umich.edu/~archive/atari/Languages/Sozo2/

Since I didn’t want to go thru a big ordeal, I found an extracted copy of the sources here:

http://cd.textfiles.com/crawlycrypt1/program/compiler/sozobon/

Building the sozobon compiler

Using wget I pulled down the source code. It’s worth noting that unlike GCC this is SMALL… although it only targets the 68000 cpu. The only source that I’m using is the actual C compiler. I suppose if I were better I could map the simulator, and setup the assembler & linker to target the environment all the way… However for now I’m just interested in showing the relationship between a compiler, to the assembler. Now the source is old enough that it uses the reserved word inline in gunk.c I simply changed it to Xinline. I’ve attached a diff for those who like diff’s however as you can see it’s really simple, I just renamed them, so they don’t conflict.


--- gunk.c Fri Feb 22 05:33:34 1991
+++ gunk-fix.c Mon Mar 9 14:35:33 2009
@@ -37,7 +37,7 @@
int (rewri)(); / rewrite function */
};

-int m_unfold(), unfold(), m_cast(), cast(), m_inline(), inline();
+int m_unfold(), unfold(), m_cast(), cast(), m_inline(), Xinline();
int m_hardas(), hardas(), m_fcmp(), fcmp(), m_md_shf(), md_shf();
int m_eident(), eident(), m_incdec(), incdec(), m_fldas(), fldas();

@@ -48,7 +48,7 @@
{m_eident, eident},
{m_incdec, incdec},
{m_hardas, hardas},
- {m_inline, inline}, /* must cast before inline /
+ {m_inline, Xinline}, /
must cast before inline */
{m_fcmp, fcmp},
{m_fldas, fldas},
{0}
@@ -424,7 +424,7 @@
return 0;
}

-inline(np)
+Xinline(np)
NODEP np;
{
register NODEP nmp, cmap;
@@ -782,7 +782,7 @@
register NODEP tp;

spar1 = "%fpcmp";
- inline(np);
+ Xinline(np);

tp = copyone(np);
tp->n_left = np->n_left;

Now with that out of the way, you should be able to build with the make.unx


% make -f make.unx
gcc -DUNIX -O -c d2.c
gcc -DUNIX -O -c decl.c
gcc -DUNIX -O -c expr.c
gcc -DUNIX -O -c fix.c
gcc -DUNIX -O -c fun.c
gcc -DUNIX -O -c g2.c
gcc -DUNIX -O -c gen.c
gcc -DUNIX -O -c gsub.c
gcc -DUNIX -O -c gunk.c
gcc -DUNIX -O -c main.c
main.c:424: warning: 'optnl' was declared implicitly 'extern' and later 'static'
main.c:417: warning: previous declaration of 'optnl'
gcc -DUNIX -O -c md.c
gcc -DUNIX -O -c nodes.c
gcc -DUNIX -O -c out_st.c
gcc -DUNIX -O -c p2.c
gcc -DUNIX -O -c pre.c
gcc -DUNIX -O -c tok.c
gcc -DUNIX -O -c subs_c.c
gcc -o xhcc d2.o decl.o expr.o fix.o fun.o g2.o gen.o gsub.o gunk.o main.o md.o nodes.o out_st.o p2.o pre.o tok.o subs_c.o

Writing hello world!

OK, with the compiler built, let’s write a simple C program. I’ve setup a simple main, and calls to a ‘putc’ and an ‘exit’ that currently don’t do anything. We will have to fix that in the assembly source.. but for now it’s nice as it sets up a place holder.



char MESSAGE[]="this is a message";
void putc(c)
char *c;
{

}
void exit()
{}

void main()
{
int j;
j=0;
putc(MESSAGE);
exit();
}

Now we can compile the source file (x.c) and use the -S flag, so it only outputs an assembly source, that we can then massage to work with easy68k.


% ./xhcc -S x.c ; cat x.s
.data
.globl _MESSAGE
_MESSAGE:
.dc.b $74,$68,$69,$73,$20,$69,$73,$20,$61,$20,$6d
.dc.b $65,$73,$73,$61,$67,$65
.dc.b 0
.text
.globl _putc
_putc:
bra L1
L0:
;var 4 8 _c
L2:
unlk a6
rts
L1:
link a6,#-0
bra L0
.globl _exit
_exit:
bra L4
L3:
L5:
unlk a6
rts
L4:
link a6,#-0
bra L3
.globl _main
_main:
bra L7
L6:
;var 2 -2 _j
clr.w -2(a6)
move.l #_MESSAGE,-(sp)
jsr _putc
add.w #4,sp
jsr _exit
L8:
unlk a6
rts
L7:
link a6,#-2
bra L6
.data

As you can see from the source below the following changes were made:

-Added the ORG $1000
-Changed the formatting of the _MESSAGE into a format that easy68k’s assembler likes.
-Removed all the .globl statements.
-Renamed the _main section to START & and add the end start tags.
-Populated the exit procedure with the ‘exit’ code from easy68k’s example
-Changed putc to use the d0 register instead of d6, and added the print string code from easy68k.


ORG $1000

_MESSAGE dc.b $74,$68,$69,$73,$20,$69,$73,$20,$61,$20,$6d
dc.b $65,$73,$73,$61,$67,$65
dc.b 0

_putc:
bra L1
L0:
;var 4 8 _c
move.b #14,d0
trap #15

L2:
unlk a0
rts
L1:
link a0,#-0
bra L0

_exit:
bra L4
L3:
L5:
unlk a6
rts
L4:
move.b #9,d0
trap #15
link a6,#-0
bra L3

START:
bra L7
L6:
;var 2 -2 _j
clr.w -2(a6)
move.l #_MESSAGE,-(sp)
jsr _putc
add.w #4,sp
jsr _exit
L8:
unlk a6
rts
L7:
link a6,#-2
bra L6
end start

conclusion

Now you can run the code in the simulator, and watch it enter the ‘start’ section, call the putc with the address of _MESSAGE, return to the main, and then call the _exit procedure, which calls the sim68k exit program interrupt. I’ll leave this as an exercise to the read for any real value…. I just thought it was cool, that without really learning any assembly I was able to write a basic ‘hello world’ type program in an hour….