In my initial research for this blog, I stumbled on this blog post by Reaktor. Back then I was able to grasp the essence of it but I found it too complicated in some areas and too shallow in other areas. Now, that I understand a lot more, I realized that that post was wonderful! I just couldn’t appreciate in-depth back then as I can now.
One important takeaway of the Reaktor’s post was the use of vasm, another popular assembler. It is a multi-platform assembler, which means you can get the code compiled for your in-use OS and build 68K code that runs on an Amiga.
In lesson #4 I explained how to set up your environment via FS-UAE and Asm One. As I walk my way through in understanding this world built 30 years ago when I was a little snort playing Buggy Boy and Silk Worm, I realize that there are other setups that can be used as an alternative or sidelined to the one that I illustrated in lesson 4.
There’s no right or wrong, it is a combo of preferences and measured by your own objectives. In lesson #4 the goal was primarily emulating (no pun) exactly what I would have done back in the day if I had any help or knowledge on what to do.
Today, I want to leverage the benefits of modern times (cross-platform) and still build toward the targeting machine that shaped my youth.
Cross-compiling is a very simple concept: instead of compiling the source code and creating binaries for the architecture you are running the compiler on, you create binaries for a different architecture. In this case, the host architecture is macOS and the target architecture is AmigaOS.
Let’s start our journey
Create a folder “amitools”, enter that folder, and perform the following steps:
wget -q “http://sun.hasenbraten.de/vasm/release/vasm.tar.gz”
tar xvf vasm.tar.gz
make CPU=m68k SYNTAX=mot
It downloads the source, builds it, and makes it ready to go. I have touched on the same subject in lesson #2 so if you need more details on this exact step, check that out.
An assembler builds your code for the machine, while a disassembler translates the machine code into… well assembler :) — which ironically is supposed to be “better” for your cholesterol but as in everything in life, don’t abuse.
Frank, the author behind vasm has built a ton of amazing software projects and one that he has contributed to is a disassembler. Which gives you great insights into how a program works. Let’s get that with similar steps:
wget -q “http://sun.hasenbraten.de/~frank/projects/download/vdam68k.tar.gz”
tar xvzf vdam68k.tar.gz
It depends on where you want to take your exploring. However, if by any chance at some point you want to access to system library or someone else library you will need the NDK to the lease.
NDD or Native Development Kit is an old fashion word that in today’s jargon is SDK.. yep! The latest and greatest is copyrighted by the same crazy people that contributed to participating in the collapse of Amiga, however, if you feel adventurous grab the archive version from here.
All those tools have a sense of coolness when you use them for the first time and then they get tedious after a while because of the many switches and nuances you have to pay attention to. So I built a few Bash functions to make my life easier on the mechanics of this retro discovery.
They are located here. Look for toolchain.sh and don’t forget to use chmod +x toolchain.sh on it or it won’t be executable.
I have that script placed into my .bashrc so that it is accessible at all times. You can have a standalone script. Your call.
I used Sublime as editor because, well because it is səˈblīm :-) by default the editor doesn’t offer Motorola 68K support (linting), however, it is very easy to add that and for free. Use this instruction to get that done. If you already have Sublime and Package Control installed you can simply search for M68 and it will show up in the list.
At this point you have installed:
- Sublime parser for 68K assembler
- Script to easily execute toolchain commands
- Built and compiled the assembler vasm
- Built and compiled the disassembler vda
Let’s celebrate this moment with the build of a quick program to make sure that everything works fine.
Create a new file called mousewait.asm and write inside the following code:
btst #6, $bfe001
Don’t be too lazy and write it yourself instead than copy and pasting. For several good reasons. As an example, if you put an accidental space after the command on the second line you will get a compiler error. Things that you learn the hard way. The code is here.
To compile the code using the toolchain Bash code provide in the Github just type
You will get a truly Amiga assembled binary that you can test its validity with the Unix command file.
The output will speak for itself. The command reads the mapping of the binary and it will tell you that it is indeed an Amiga binary. Isn’t cross-platform a joy?!
If you are super eager of taking a look at what the disassembled code looks like then you can type
For some truly “what the heck is that” moment.
Our multi-platform momentum doesn’t end here. What if you want to run the code without kicking in FS-UAE or any other emulator you might use.
In that case, welcome to amitools a set of amazing multi-platform (or almost) tools that allow you to do that and more. One of the key lovely aspects of that toolchain is vamos which is a catchy name for Virtual Amiga Os meaning that from the comfort of your terminal you can execute Amiga binaries. There’s a catch though and the author of the tool puts it best
vamos is a tool that allows to run AmigaOS m68k binaries directly on your Mac. It emulates the AmigaOS by providing implementations for some functions of the Exec and DOS library. It will run typical console binaries that do not rely on user interface (intuition) or graphics stuff. Its main focus is to run old compilers and assemblers to have some sort of “cross” compilers. This approach will not run any applications or games using direct hardware register access – for this a machine emulator like P-UAE is the tool you will need…
I highlighted in bold some key aspects to set the expectations on what it can emulate. Now you might have questions. How do I use vamos and what is Exec? For vamos, look for lesson #11 where we will be exploring Docker and other means to run our Amiga code outside of FS-UAE.
For what is Exec, well… entering the machine…
When you code in a high-level language you usually refer to functions of an external library in your code and the compiler takes care of embedding. The code of the library is either included in the code of your program or loaded into memory at runtime (shared libraries), but in both instances, the function call is, at the machine language level, just a jump to a different address in memory.
If you have been scouring the Internet in searching for clues (hey Matrix boy) for Amiga code, you might have noticed that some source code out there has some “magic numbers” in it. Well, because the original creators of the Amiga promised (and kept the promise) to not change the base address among different versions,
When the Amiga OS loads a library in memory the Exec master library analyses its structure and creates the so-called jump table. This is nothing more than an array that lists the addresses of the functions exposed by the library. This is a very simple and effective way to let the OS free to load the library anywhere in memory (relocation).
The Exec master library is not different, but this library is loaded as part of the bootstrap process, and the base address is always stored in the memory location
0x4. To use one of Exec’s functions, then, we just need to issue a
jsr <address> (
<address> is the current position in memory of the function we want to call. Since we don’t know the absolute address, being the library is dynamically loaded, we use the library’s jump table to retrieve the base address and get the function address as a fixed offset from the former.
Many Amiga programmers know the magic addresses by heart hence the magic numbers. For example, the
OpenLibrary function can be found at the address
-552 relative to the library base, while
CloseLibrary is at
-414. To call the
OpenLibrary function, then, you need the following code:
move.l 4.w,a6 ; a6 = base address of Exec
jsr -552(a6) ; OpenLibrary()
As we learned in lesson #8, when dealing with address registers… The first instruction moves the value contained at address 0x4 into the a6 register. This way the register will contain the base address of Exec. Then it jumps to the subroutine whose address is 552 bytes before that base address. So, if a6 contains an address like 0x20000 the code jumps to 0x1fdd8 (0x20000 - 552).
The jump table plays a major role here. When the Amiga OS loads a library in memory the Exec master library analyses its structure and creates the so-called jump table. This is nothing more than an array that lists the addresses of the functions exposed by the library. This is a very simple and effective way to let the OS free to load the library anywhere in memory (relocation).
And for that unveil and curiosity in Lesson #10 we will be digging into how to analyze and debug our own code to figure out when something doesn’t work, that we wrote, and HOW something that someone else wrote, works. Yeah, you got it, right. That sentence needs a debugger :-)
As I mentioned in the intro of this retro exploration I would ad-hoc once in a while list references because there’s so much out there that it is not possible to discriminate between original and assumptions based on copy and paste “ah it works” moments.
However, for this particular lesson, I am very grateful to Leonardo Giordani, Tuomas Järvensivu, and Harri Salokorpi who have posted in several fashions and venues a wealth of really useful info that I used to put the pieces together for more mere mortal wannabe weekend warrior Amiga programmers. Thank you, folks!