I'm currently busy developing an Amiga game in C and wanted to give others the possibility to have an easy start into this topic. I believe that C can be used as an alternative to assembler even on the Motorola 68000. I can undergird this argument by presenting some footage of the game in development.
It turns out there are quite some good tolchains for Amiga development out here. I want to give credit to those I use.
- The toolchain68k from haarer is a very good one. I currently use this as NDOS toolchain for developing without using AmigaDOS which saves some valuable memory for game development.
- The amiga-gcc from bebbo is another very good example. It targets developing for AmigaDOS and I use this one for the harddisk port of my game.
So what is the issue then? I was thinking about if C++ could be used for developing for the Amiga and this project here gives some benchmarks in terms of speed and size about this topic. While bebbos's toolchain is working with C++ code, it turns out that the toolchain68k of haarer doesn't produce a working libstdc++ and now working global constructors. While g++ is working in general (range-loops are funcitonal) I'm not satisfied with the results as I wanted to use the C++11 potential during development.
So I decided to dive a little bit deeper and try to create my own m68k-elf-gcc toolchain.
- Delivered with Bootloader and ADF construction kit for direct building of ADFs
- Working C++ examples with C++11 style code.
- Downsizing of newlib to save memory
- Removal of floating point support of scanf and printf (I haven't found a purpose for it during development. Keep in mind that float and double datatypes are still usable and only take up space if used in the project.)
- Downsizing of libstdc++ to save memory
- Deactivation of exceptions. (Unwind code takes up much space. Exceptions leads to abort() in this case)
- Usage of stream type operators (like std::cout) is highly discouraged as it wastes about 300kB of memory. For systems with lots of FastRAM this might not be a big issue. But for A500 development the newlib-nano implementation of printf is much better suited. In The example project those can be enabled optionally to see the difference.
Besides the mentionend toolchains above, I'd like to give credit and thanks to those projects
- DDE5-BootLoader.S is a BootLoader and Floppy driver by Photon.
- make-adf.py and sum-bootblock.py is from this bootloader project.
- A linux machine as Windows support won't be tested by me. But feel free to inform me about successes.
- kick31.rom and kick13.rom must be placed in the example folder for execution. I can't supply this for copyright reasons.
- If you like to execute the AmigaDOS version of the example
- Copy a valid Libs folder to harddrive from an AmigaDOS 3.1 installation. I can't supply this for copyright reasons.
- You need to install the bebbo toolchain mentioned above as this project only produces an ELF kit.
- You need to install some build-essentials
- sudo apt-get install wget bzip2 git make tar flex bison diffutils texinfo gcc g++ libgmp-dev libmpc-dev libmpfr-dev libisl-dev
- TODO this list might be incomplete. Please create an issue in github if you are having problems.
- For testing you also need an Amiga emulator. fs-uae is my currently favoured solution
- sudo apt-get install fs-uae
First you might to edit the executed script as you might want to use a different folder for installation!
cd toolchain
./buildToolchain.sh
cd example
./build.sh -DAMIGACROSS=1 -DBUILD_FOR_AMIGADOS=0
cd example
./build.sh -DAMIGACROSS=1 -DBUILD_FOR_AMIGADOS=1
cd example
./build.sh -DAMIGACROSS=0
This is a very small example. You need a COM port emulation. Execute this in one terminal
scripts/serialout.sh
Now execute this in another terminal
fs-uae a1200.fs-uae
This is a very small example. You need a COM port emulation. Execute this in one terminal
scripts/serialout.sh
Now execute this in another terminal
fs-uae a1200hd.fs-uae
AmigaDOS should pop up and then execute "m". I went for this short name as it could be type very fast.
Allocate 50 int arrays of size 4 and manage them
c array of malloc'd pointers 5784 us 100%
c array of new'd pointers 7045 us 122%
c array of unique pointers 7621 us 132%
reserve c++ vector of unique pointers 9034 us 156%
presized c++ vector of unique pointers 8577 us 148%
c array of shared pointers 7009 us 121%
c++ vector of unique pointers 10146 us 175%
c++ list of unique pointers 15569 us 269%
c++ array of unique pointers 7669 us 133%
Allocate 50 int arrays of size 64 and manage them
c array of malloc'd pointers 6442 us 100%
c array of new'd pointers 7723 us 120%
c array of unique pointers 10361 us 161%
reserve c++ vector of unique pointers 12083 us 188%
presized c++ vector of unique pointers 11438 us 178%
c array of shared pointers 7329 us 114%
c++ vector of unique pointers 13262 us 206%
c++ list of unique pointers 19122 us 297%
c++ array of unique pointers 10550 us 164%
Construct strings from 4 integers
sprintf 3887 us
c++ stringstream build 5753 us
Strings ok
Iterate over 1000 ints and sum them up
arraysum - for i++ 1441 us 100% results verified
arraysum - for ++i 1441 us 100% results verified
arraysum - range loop 1441 us 100% results verified
vectorsum - range loop 1442 us 100% results verified
vectorsum - std::for_each 1441 us 100% results verified
vectorsum - iterator 1441 us 100% results verified
vectorsum - std::accumulate 1442 us 100% results verified
listsum - range loop 2000 us 139% results verified
listsum - iterator 1716 us 119% results verified
Allocate 50 int arrays of size 4 and manage them
c array of malloc'd pointers 5784 us 100%
c array of new'd pointers 7045 us 122%
c array of unique pointers 7621 us 132%
reserve c++ vector of unique pointers 9034 us 156%
presized c++ vector of unique pointers 8577 us 148%
c array of shared pointers 7009 us 121%
c++ vector of unique pointers 10146 us 175%
c++ list of unique pointers 15569 us 269%
c++ array of unique pointers 7669 us 133%
Allocate 50 int arrays of size 64 and manage them
c array of malloc'd pointers 6442 us 100%
c array of new'd pointers 7723 us 120%
c array of unique pointers 10361 us 161%
reserve c++ vector of unique pointers 12083 us 188%
presized c++ vector of unique pointers 11438 us 178%
c array of shared pointers 7329 us 114%
c++ vector of unique pointers 13262 us 206%
c++ list of unique pointers 19122 us 297%
c++ array of unique pointers 10550 us 164%
Construct strings from 4 integers
sprintf 3887 us
c++ stringstream build 5753 us
Strings ok
Iterate over 1000 ints and sum them up
arraysum - for i++ 1441 us 100% results verified
arraysum - for ++i 1441 us 100% results verified
arraysum - range loop 1441 us 100% results verified
vectorsum - range loop 1442 us 100% results verified
vectorsum - std::for_each 1441 us 100% results verified
vectorsum - iterator 1441 us 100% results verified
vectorsum - std::accumulate 1442 us 100% results verified
listsum - range loop 2000 us 139% results verified
listsum - iterator 1716 us 119% results verified
These values are assumed to be faster as AmigaDOS correctly sets up the Caching of the 68020
Allocate 50 int arrays of size 4 and manage them
c array of malloc'd pointers 4802 us 100%
c array of new'd pointers 5274 us 110%
c array of unique pointers 6967 us 145%
reserve c++ vector of unique pointers 6458 us 134%
presized c++ vector of unique pointers 6368 us 133%
c++ vector of unique pointers 7618 us 159%
c++ list of unique pointers 11264 us 235%
c++ array of unique pointers 6324 us 132%
Allocate 50 int arrays of size 64 and manage them
c array of malloc'd pointers 4765 us 100%
c array of new'd pointers 5235 us 110%
c array of unique pointers 7696 us 161%
reserve c++ vector of unique pointers 7257 us 152%
presized c++ vector of unique pointers 7181 us 151%
c++ vector of unique pointers 8323 us 175%
c++ list of unique pointers 11678 us 245%
c++ array of unique pointers 7055 us 148%
Construct strings from 4 integers
sprintf 1579 us
c++ stringstream build 3057 us
Strings ok
Iterate over 1000 ints and sum them up
arraysum - for i++ 1000 us 100% results verified
arraysum - for ++i 998 us 100% results verified
arraysum - range loop 1138 us 114% results verified
vectorsum - range loop 1139 us 114% results verified
vectorsum - std::for_each 1000 us 100% results verified
vectorsum - iterator 1000 us 100% results verified
vectorsum - std::accumulate 1000 us 100% results verified
listsum - range loop 1419 us 142% results verified
listsum - iterator 1419 us 142% results verified
The C++ stringstream and iostream classes should not be used!
The current executable is 340 kB in size if used and 42 kB if absent. I'm not yet sure why this happens but a short search on the internet reveals that this is "normal" and libstdc++ is expected to have a size in hundreds of kB. Before, it was even bigger but the removal of Unwind and exception handling resulted in some decrease.
Some people encourage the usage of libsupc++. But I've found that too much of C++ is missing this way.
So if you plan to use C++ on the Amiga and plan to develop an AmigaDOS application for a system with some FastRAM I can guess that std::cout might be an option. For NDOS development I won't recommend it as space is quite valuable and printf is still a good solution for most use cases.
If a big number of std::stringstream is handled I can highly discourage this as the runtime is nearly doubled compared to sprintf. Even this doesn't makes any sense as sprintf has the additional load of parsing the format string at runtime and C++ could template-magic this out of the way.
The iteration tests reveal that down to the assembler level the code is nearly identical for all cases expect std::list which does make sense as the memory layout is different and the later must use pointers to get to the next entry.
TODO There are some systematic differences in run-time. I'm not yet sure why this happens but investigation might be needed.
On the allocation side I'm deeply confused. Malloc seems to be the fastest. And then shared_ptr is faster than new? And even unique_ptr is slower than shared_ptr? This doesn't makes any sense and I can only assume that the state of the heap as an effect on the results. This tests might need a remake with a fresh heap for each test and mabye also with seperate allocation and deallocation measurements.
This project is provided as is. I can't held responsible for damages that it might cause on your hardware. I'll encourage to first try this on an emulated machine and only do this on real hardware if you know what you are doing.