This is the Amiga Development blog of Steffest.
I'm a webdeveloper but have zero experience in C or developing for the Amiga.
These are my notes of the journey into Amiga dev-land.
The Goal:
To create a simple game from the same source files for 3 completely different target platforms:
a stock Amiga 500, a modern Mobile Phone and the Modern Web.
You are viewing the articles in 'blog-order', meaning the most recent one is at the top.
You can also view them chronological, which makes more sense as a journey
An OldSkool demo for Amiga OCS inspired by a live performance I attended and captured of NAFT, a Belgian Live Analog Techno band. The music is partially based on, or at least HEAVILY inspired by "Paroxysm" by Naft. If you want to check out the MOD file -> here in Bassoontracker. It runs on an Amiga 500 with 512k fast ram or beyond.
Technically, it's a big step up compared to my previous production, but still: baby steps. I really wanted to use assembler, but progress was just too slow. I got to the point where I could load some graphics and put them on a custom screen in assembler. Yay! almost a demo! But then each additional step took another week of dabbling and tinkering.
Deadline Rsync was approaching, so I made the executive decision to move back to C. Hardware banging this time. I found this YouTube series "Amiga hardware programming in C" that I could wrap my head around, and it gave me some much-needed direction. Thanks Wei-ju Wu!
Another "aha moment" came when Gigabates explained what the copper actually does, and - in essence - how simple it is. He said "For graphics, you can think of the copper as timed palette changes: You just wait for a certain position on the screen, and then you can set any color index to any color, and then it will use that color from that position going forward"
"Ping! ..." Ooh!!! I can do something with that!
I started some experiments to have a patterned image on screen. the image stays static but when you start switching colors, you can target each pattern section individually and give it a unique color, circumventing the usual 32 color limit. This is of course a well known "copper chunky" trick, but I found it interesting that each "pixel" doesn't have to be square, it can be anything you want. This formed the basis of most of the effects in Naphta. (can you call them effects?)
I aimed for some sort of stained-glass visual. To keep things simple, I used horizontal patterns in 16 or 8 colors, so I could avoid swapping colors midway on a scanline. Also, I stayed away from color 0 to avoid having to reset it each line when entering the overscan areas. Yeah.... not the most hires copper-chunky, but it was a nice experiment and usefully to wrap my head around the copper.
This is the image for the soft pixel blur anims. The picture doesn't move, the animation is just the copper, setting palette colors like crazy. The "slowness" is not the copper, by the way, it's the C code that has to calculate the correct copperlist index of each position. I probably should/could precalculate that too.
I'm still largely clueless in C-land. I really should dive into BartMans toolchain and set up a proper monitor/compiler. But even now: Boy, oh Boy, oh Boy, now I FINALLY understand why everybody loves those custom Amiga chips. What a beautiful system.
Another interesting bit - Or at least I though so - was the use of the music to drive the timing of the effects, and not the other way around. I'm using the excellent PTPlayer replay library and this supports reading out the "E8" command.
This way you can let the music control which part of the code should be triggered, and you still can use all the Fxx commands you like to set the music speed. Double handy in my case as I don't have a clue how fast my code runs (I know it's sloooow though :-) ) and often routines take several frames to complete, so having an external thingie to keep the timing is nice.
And a third concept was that I got somewhat obsessed with "transparency" on Amiga. Fact is: transparency doesn't exist on Amiga OCS but ... we have bitplanes! Bitplanes are layers stacked on top of each other, and you can use that to "fake transparency" if you reduce the color count. For example this scene uses 5 bitplanes, 32 colors.
The first 3 bitplanes are the base image in 8 colors.
Then the spotlights are on bitplane 4, forming colors 9 to 16.
So if you set those colors to a slight variation of the first 8 colors, you get the impression of a transparent layer.
And we still have 1 bitplane left, so we can repeat this trick on bitplane 5, forming the colors 17 to 32, giving the impression of 2 transparent layers.
The way the display works on the Amiga is that each bitplane is "just a chunk of memory". You can just point to other memory sections if you want to display something else so you don't have to copy things around. This means that scrolling a bitplane is just "setting the pointer to the address of the row below". This is trivial if you know it, but it took me quite some time to switch my mental model of a "a screen is something you draw to" to "a screen is just a display of several memory blocks". Once you make that switch, suddenly you realise you can do some pretty cool tricks. Combined with the careful palette mapping, you then get - for example - the appearance of a semi-transparent layer that is moving on a background.
Some key takeaways for future productions:
Having a good build system is important. It's nice to be able to compile/run/test with a single click and to be able to iterate fast. Hooray for emulators!
There's something fishy with my areafill code ... it sometimes barfs on low-spec machines, and it's way too slow. I mean: if I ever want to go 3D, I have to draw triangles, right ?
I like the "progressive enhancement" approach. It still should run on a stock 500, but if a faster machine is detected, it might as well use it and swap to higher quality music/graphics.
The "add21" command that EXE2ADF adds, causes a Guru in vAmiga (and only there?, weird) -> TODO: investigate because 21k extra chipram is most welcome.
I should investigate trackloaders or find a way to load stuff from disk without blocking the main thread. Currently, I load EVERYTHING at the start which takes a stupid amount of time. Using compression tools like Shrinkler solve the disk-issue, but also cost a lot of time and memory, which maybe is not the best tradeoff.
and again: assembler is the way if you want any kind of performance...
even more important, maybe, is understanding how many cpu cycles each operation costs. an obvious one is: avoid mutiply/divide, unless it's by a factor of 2, and you can use bit-shifting. (and even bit-shifting is not free), so: I really need a way to measure these things to know what to avoid.
In general: I feel I'm just scratching the surface, still so much to discover and to learn, and making small productions like this is an excellent way to do so, so ... on to the next one!
Tools Used: Graphics: Based on live footage from the band "NAFT" Images where first sketched on paper and then produced in PhotoShop, including the Generative Fill tool, so I guess they are not AI free. So yeah: those nice smoke effects are generated, not pixelled by hand. Images were finalized and pixel optimised in Dpaint.js. Some chunky-pixel animations were taken from Giphy.
Music: The main theme and some samples where lifted from the song "Paroxysm" by Naft using various stem-splitting tools. The mod file was composed by hand using BassoonTracker: The disk version of naphta uses a lower-quality compressed mod file to make it fit on a single disk and in 512k chip ram.
Code: Home-cooked hardware banging C. Compiled with SAS-C on the Amiga (but written in modern editors)
Every single Amiga coder I talked to says the same thing: trying to write a game or a demo in system friendly C that runs on a standard A500 is batshit crazy. Apparently I'm making it unnecessarily complex for myself. I really should move to assembler.
Usually when I wander into unknown territory that looks like a steep hill, I jump on, find something to hold on to and start climbing. Assembler feels like an iceberg instead of a hill: I jump on, don't find anything I recognise to grasp and slip back down.
So: back to babysteps. Let's start with the very beginning: pick the Photon course and go with AsmTwo
Ha! First hurldle: I'm on a Mac with an AZERTY keyboard, using FS-UAE and I can't type the # char in the amiga editor. Failed at the first line of the coding tutorial :-)
AsmTwo (and AsmOne) seem like super tools IF you know how they work :-) So in the light of those babysteps:
When you start the program, you have to type a number of how much memory you want to assign (seems obvious if you know it, but I had no clue)
use "escape" to toggle between the editor and the cli
in the cli, use
"a" to assemble
"j" to jump to the first command (and execute the program)
"wo" to write your assembled binary to disk.
First tutorial completed. Yay!
waitmouse: btst #6,$bfe001 bne waitmouse rts
The integrated CoPilot in modern editors explains that
btst stands for "Bit Test", it stores the result of the test in the "Condition Code Register"
bne stands for "Branch if not equal" it tests the Condition Code Register
rts stands for "Return from SubRoutine"
Good to know, that actually makes sense. Still I'm going to sprinkly the code with comments that explain exactly what is going on.
Speaking of comments: both AsmTwo and Vasm handle source code with "//" style inline comments, but PhxAss complaints about the syntax and only assembles it when I remove all comments. Some Googling indicated that I should start comments with "# " or a pipe | , but that's also not to PhxAss' liking... not a scooby. (Update: found it: it's the ";" char, works also on Vasm/AsmTwo and gives correct highlight colors in VSCode)
And another usefull snippet: vasm won't generate amiga binaries as output by default, you have to supply some command line options:
Moving forward, both AsmTwo and Vasm seem well suited. AsmTwo might be nicer for monitoring and debugging as it's integrated. Vasm seems a better fit for my "write code on PC, compile on Amige" approach I liked so much working on Ordo.
For Vasm, I haven't figured out yet how the build using a MakeFile. For now, I just use a build script. TIP: to make that executable without having to use "execute": protect build.script +se
TIP2: avoiding "magical number" like $bfe001 seems useful to keep my head straight. $bfe001 is the hardware address for the first CIA cheip, that handles a.o. the mouse input. you can assign variables to make that more clear like this:
The graphics are fine, but from a technical point of view, it isn't much. It's 100% blitter , no sprites, no bobs, no copper.
Still so much to learn :-)
Some notes:
I'm puzzled with the memory management on the Amiga ... I know the audio and graphics chips can only access chip memory, but how do you copy from fast to chip mem? I would expect that just to be a "CopyMem" operation. This does work on kickstart 3.1 but not on kickstart 1.3. Why? To be investigated. This is why Ordo needs 1MB of chip ram to run - but the good part is: it does run on a Amiga 500, that was the goal.
I have no idea how to keep the audio and the video in sync. I guess there has to be a main loop dat waits for the VBL signal and that drives both the mod player and screen refreshes. to be investigated.
I have no idea how to integrate a decompression mechanism. Currently all data files are uncompressed raw data, hence the large filesizes and long loading time. A better approach would be to compress the data files, load those in fast ram and decompress them when needed to chip ram. To be investigated.
System Friendly C code is slow ... for example the "FillRect" function feels really sluggish, especially on kickstart 1.3. I guess at some point I do want to integrate some assembly into the mix. Maybe use C to orchestrate everything, but use assembly for the low level heavy lifting.
I'm wondering what the best way is to load graphical data and get it in a usable form: currently I load raw bitplanes into chip memory, create an "Image" with a pointer to that memory, draw that image on a RastPort so I can use it in a View. That feels like a lot of overhead. There must be a simpeler way.
Still, it was a great experience.
It was released at the RSync Demoparty in Leuven and won 1st place - yay! The audience seemed to appreciate that is was "different than usual" and it was an actual concept, telling a story.
Of course, it's "different" because I have no idea how to do all those cool effects you see in other demos, haha. But maybe that "soft area" of the demoscene is not a bad place to operate.
By the way - RSync was GREAT fun! I kind of already figured out I would like the demoscene, but sweet tangerines... it's really remarkable to find so many like minded spirits in one spot ...
Update 1: Ah - I finally figured out why there are such timing differences between systems. On most of my machines I have fBlit installed, which patches soms system functions to use the CPU instead of the blitter. Turns out I wasn't using the blitter after all on my development machines :-) Doh!
Update 2: Found a simpeler way to preload graphics data in FAST memory. I didn't realize yet that AllocRaster is very similar to AllocMemory and returns a pointer to the allocated memory. I can then just CopyMem to that pointer. The only drawback is that I have to copy each plane separatly. Example on -> https://github.com/steffest/amiga-c/blob/master/FastToChip/better.c
and for keeping the Audio and video in sync, this seems promising https://eab.abime.net/showthread.php?t=116663
New intermediate goal: creating a demo that runs on a standard Amiga 500. This means kickstart 1.3 is back in the picture.
First step: let there be music! Most of the mod-player libraries I tried won't work on KS1.3, but then I found the awesome (and very recent) PTPlayer, made by PHX in Assembler. It's not entirely system friendly, but for a demo, that'll do.
That was - again - a steep hill to climb: figuring out how the compile the library and use it from a C program. The compiler started throwing weird stuff around like FAR and NEAR data descrepencies and what not. Errr... Whut ?
But after my usual approach: keep banging on things untill it works - I ended up with something that worked and that didn't look all that bad.
Code here on Github. It's a small wrapper function that enables me to just write play_mod("somefile.mod") and be done with it. Nice.
Lessons learned:
how to compile assembler code to an object
how to link that object and call it from C code
how to load a file from unknown size into chip memory
how to clean up after myself, so I don't have to reboot the Amiga after each test because of a crash
Today I compiled my first program written in Assembler. Even though it was only a few lines, it felt like a major victory :-)
It started with a desire to understand exactly how a CPU works, with registers and addresses and bytecode instructions etc. (Maybe I'm still dreaming of writing my own emulator one day, haha )
There are lots of tutorials. The Photon one is famous. I also found this one very nice, especially for total beginners like me.
I tried several different Assemblers.
Seka Found it on Pouet Not everybody likes it though ... It's a bit cryptic, but it works. The "editor" reminds me of VIM ... but I guess that was just the state of editors back in that day. Oh how spoiled we are in this time.
Devpac 3 Found it on the Turran FTP Nice integrated package
ASM-One another popular assembler on the Amiga. It's on Aminet. Seems very similar to Seka, with the same (cryptic) CLI commands and rudimentary editor.
Crosscompilation There's a very nice Amiga Assembly extention for Visual Studio Code, made by Paul Raingeard. Really well made, with run and debugging support directly in WinUAE or FSUAE. The code gets compiled with VASM and linked with VLINK and indeed: it produces a nice Amiga 68k binary.
Ok, so it's clear that I don't have the patience anymore to use the native "IDE editors" dcirectly on the Amiga. I'm not that masochistic. writing code, I'll do on a modern machine, but maybe the compilation and linking step will remain on the amiga. (Maybe ... VASM and VLINK seems like a really nice tool that produces tiny binaries, so ...) Ah ... and there are also Amiga ports of Vasm and Vlink (they are included in the VBCC package)
Lots of the Amiga Assembly examples are not system friendly though... I mean: they are super interesting to see how exactly the Amiga works (like "write to that memory addres to change color 1" , "read that memory address to check the joystick button" ...) But I don't know if I want to go there. Doing system-friendly stuff certainly is possible, but why do that in assembler? In the end- it's of course perfectly viable to mix assembler and C, creating linkable objects in one or the other, and combine them in 1 executable.
TODO: Check out ASM-Two - seems like a nice package for basic amigas( it doesn't support 020 oir higher CPUs, but still) And this is a nice thread with valuable info. (Look who started it :-) Hi Guy! )
Confession: I'm one of those guys that like to reinvent the wheel. Because the wheel you have made yourself is always rounder than any other wheel in the world, right?
I also know that "reinventing the wheel" often is the result of 2 things
Or you don't know the existance of other wheels
Or you know they exist, but somehow they seem too heavy, or doesn't seem to fit.
So... After my little "button" class I decided to give existing Amiga GUI frameworks another chance, because I had the feeling I was trying to solve a problem that was solved at least a dozen times before.
The one Gui framework in the Amiga world that is impossible to miss is - of course - MUI: the Magical user interface.
And from a developer perspective, I now know why. It is simply really really well made.
No wonder it's the default, and it's even supported out of the box on OS4 and Morphos. So - unless you're targeting kickstart 1.3 - you would be a fool not to use it, right?
To use it as C developer, you need the C includes you can find here. All the examples includes are made to be compiled on Sas/C so - indeed - they work out of the box. How nice is that!?!
Very very very nice and super easy to use ... I still have this nagging aversion of "dependencies" in your software, but is there any OS3 user that doesn't have MUI installed? Don't think so...
Note to self: about those "includes" in C: is there any way to keep track of where they come from, maybe by putting them in seperate include folders? Now I just copy the MUI include files to my main C include folder and it magically works (See what I did there? :-) ) but what If I want to compile my program in 10 years on a new system? how would I know what includes I need exactly?
Poking around the MUI example files I found something very striking ... the "KnobsControl" class.
On the left is the MUI knobs control class, on the right is the knob and number-display I made for BassoonTracker. Isn't the resemblance striking, especially with those orange numbers in the rounded box? I honestly can't remember seeing that MUI knob control anywhere before, so let's put this down to a case of "Great minds think alike" shall we? :-)
The Amiga X5000 - most powerful Amiga you can actually buy new anno 2021. Lovely machine - certainly the fastest Amiga I ever used. it's a 2Ghz PPC processor with 2GB of Ram. Doesn't run Classic OS3 of course - so enter OS4!
Hmm... I really want to love this machine, but it saddens me to say that OS4 is a bit... erm... crap. MorphOS also runs on the machine though, and that's one snappy and polished OS!
Well, let's at least TRY to give OS4 some love. I was pleased to see my little system-friendly UI programs run out-of-the-box on it. Must be some on the fly CPU emulation to translate 68k binaries to PPC?
Yes, OS4 has Petunia and MorhOS has Trance (So I learned) Cool! With some careful planning, it doesn't seem too far fetched to produce 1 single binary that works on all these systems.
I wonder if I can use stuff like network libraries and AHI etc. -> to check out!
While I was fiddling with C again, I made an attempt to a more "structured" approach. Just a simple button-example, but the goal was to find a coding structure that works for me and that I can use as scaffolding to build upon.
I have totally no idea if this is "how you're supposed to do it" but it works, I can wrap my head around it and it feels right.
I love it! At first I was not impressed at all as it was very slow with the default raspian Linux, but then I tried DietPi - much better! (And ofcourse Amiberry is also kind of cool) I did have to fiddle a lot to get everything up and running though.
My main problem was that VNC did not work when no monitor was attached to the PI.
It worked fine when you connect a monitor, boot DietPi and disconnect te monitor, but not in complete headless mode. after a loooooooon evening of trying and googling I finally found this post and this explanation.
So it's a bug in vncserver. The workaround that did the trick for me was to set the "view only" password as the normal password using vncpasswd. When running headless, the VNC settings are in /DietPi/dietpi.txt - edit that to set the resolution and the screen ID (to 0 for easy of use): the config is in the main /DietPi/dietpi.txt Then execute a "vncserver start" and a "vncserver stop"
More: the ssh client on the Amiga is to old I guess, but telnet works (I had to install the telnet deamon on the Pi). to start Chromium with fullscreen enabled: chromium-browser --start-fulscreen (then F11, which is a bit annoying as the Amiga keyboard only goes to F10 :-))
After a hiatus I pickud up the Amiga-C-Compiler again. One of the building blocks of many of my projects is the web: doing a HTTP request, parsing results, use them. Once an application is "connected" in that way, it usually becomes so much more powerfull. That's the same for the hardware. I long thought networking on the Amiga was a gimmick, on of those "because-you-can" things that you fiddle with, get it working and hardly use after that. But after you walked the upgrade-your-amiga-to-beast-mode path, networking is the main component that makes an Amiga fun to use. Things like Samba shares, VNC, Synergy, streaming audio players, ... are just AWESOME on a high-spec amiga.
So today I finally managed to compile a program that does HTTP requests. Yay, it even handles large binary files pretty well. That was a nice deep dive on how HTTP works exactly on top of TCP/IP.
Some notes. I had to hunt for the correct include header files a lot, but I finally found them in the OS4 SDK at the Hyperion site. (It's in base.lha -> include -> netinclude) Who knew OS4 would come in handy, right? Thank you Hyperion for still providing that kind of stuff.
Extract the files somewhere and include them in the compiler like
"sc INCLUDEDIR=NETINCLUDE:"
(where NETINCLUDE: is an assign the the location where you extracted the SDK)
So the next step: My program needs an interface! At first sight that seems a bit messy on the Amiga ... you have GadTools , Boopsi, MUI classes,... Seems like a LOT of boilerplate and hoops to jump through just to get some UI elements in the screen. After all ... a button is just a rectangle with a label that should trigger some code when you click on it. I think it was part of some presentation by FarbRausch where Chaos (I think) was giving some advice on coding. If I look back on that, that talk had a great impact on the way I work now.
One advice was "build your own tools" , another advice was "build your own user interface system". That seems like a really weird advice. Why reinvent the wheel, right? The line of thinking was that - unless you build something very generic - you spend/loose a lot of time battling other peoples framework to do exactly what you want. All those "generic UI systems" come with a lot of overhead and stuff you don't need, exactly because they aim to be ... well ... generic. That's also what I did with Bassoontracker: just build your own UI library that does EXACTLY what you want without overhead. I guess that's also a nice approach for Amiga applications. In the end I think I want something like AmigaAmp, or the "Empy" skin of Eagleplayer where clearly the custom UI is a huge reason why these programs or fun to use.
It's a plugin for Visual Studio Code that offers a complete and integrated Amiga Dev toolchain out of the box. Including remote debugging and the whole shebang.
That's impressive .. and VERY comfortable I must say.
Just fire up VSCode, init a new Amiga project, hit F5 to run it and suddenly a virtual Amiga spins up, running your compiled code and offering a very cool remote debugging bridge right into your IDE. It's magic!
It uses GCC to (cross) compile.
The IDE is the most important piece of the work flow for me. It's where you write your code and it just have to feel right. For that reason, editing or writing code on the Amiga is a no go: there simply is no editor on the Amiga that even comes close to modern tools available today on windows/Mac/Linux.
My personal preference still are the JetBrains tools: IntelliJ, WebStom, CLion, PHPStorm ... These guys really understand the process of developing code. They are not free but IMHO opinion totally worth it.
The other rising star is Visual Studio Code (not to be confused with the heavy weight Visual Studio) It's biggest strenght is that it's free. It's fast, lightweight and extensible. Good job, Microsoft!
Nervertheless... crosscompiling ... for some reason I still don't like it. Mainly because I switch systems/OS all the time and I like my tools to be completely platform independent. VSCode is, but Bartmans toolchain is not (yet). Having a light virtual Amiga around with Sas/C ready to roll feels more comfortable and more "in control" then a magically crosscompile toolchain. Maybe I just have to dig in Bartmans tools some more to see how they work exactly.
Someone on the EAB board mentioned "Gamesmith Development System" Normally I don't want to use "Frameworks" because I like to dig into it myself, but seeing I was having a hard time getting any kind of graphic performance out of my own code I decided to take a look.
I'm glad I did - it looks like it is the perfect "middleware" between high level C drawing function and the raw amiga specific stuff. It's a library written in Assembler and C that exposes some basic graphic and audio functions to any other language that can load external libraries. It comes with both a C and a Assemler compiler, but it also works with Sas/C
It was a commercial product but apparently it's in the public domain now. I found the download here and the manual here.
The installation barfed on the final steps on my system, so I had to complete it manually: assign the GameSmith: path to the installation dir and rename the correct header file for use with Sas/C.
Some of the examples even are very "bunnymark" like so that looks really promising. Gamesmith includes some "link" files you can excute, but I could not get them to work. According to the Sas/C user guide you don't really need to do the compiling and linking in 2 steps, you can just use the sc command with the "link" parameter to do everyrthing in 1 step. You can use the "lib" parameter to define the include libraries. So to compile the example it would be "sc file.c link lib GameSmith:GameSmith.lib to file"
Awesome. Gamesmith includes lot's of stuff I don't really need, I guess, but the base seems really really promising: fast and easy to integrate.
I started writing my own chunky versus planar rountine but ofcourse somebody already did that. I found this totally execellent package at Aminet: GetImage: http://aminet.net/package/dev/c/GetImage This takes an image (or multiple image inside a Dpaint Brush) and converts it to a C Image data structure ready to by used. I you're starting with a .png file you can use e.g. Personal Paint to convert it to an DPaint brush.
On the amiga we can include this file and avoid having to do any (slow) chunky/planar conversions, on other platforms we can just include the original .png file. Excellent!
I often see people define arrays of large size as local vars in a function, and they are unaware that this ends up in stack space.
Indeed! I tumbled into that booby trap also. Note to self: define large data arrays as const outside any function.
It appears that GCC includes the standard C libraries libnix which defaults to V37 of Amiga libraries. This makes it pretty hard to target kickstart 1.3 amigas as v37 was included with kickstart 2.04 ... You can avoid this by using the -nostdlib flag but this opens a whole can of worms (no reference to __main, no reference to atexit ....) So as a general rule: If you want to target kickstart 1.3 or lower: avoid GCC, use VBCC or SAS/C instead.
Update: For cross-compilation with GCC, like this one , you can target kichstart 1.3: m68k-amigaos-gcc test.cpp -mcrt=nix13
Let's get the basics first: opening screens and windows, displaying something on the screen. I've made a new repo on Github collecting all small examples. If there are no building instructions you can compile them with:
Sas/C: type "mkmk" then "smake"
GCC: type "gcc -noixemul ****.c -o ****"
Let's not worry about portabilty at this point. The first goal is to get a program running that
also very cool ,to check out if I ever take on Assembler: online C to assembler tool: http://brownbot.hopto.org/
After some fiddling I decided to go with GCC 2.95 on the amiga. Would there be any performance differences in the final binary when using different compilers? To test.
Update:
It appears Sas/C produces much smaller binaries than GCC
to autogenerate the smakefile use "mkmk" in the sourcedir
to build the smakefile use "smake"
bottom line: I like Sas/C - let's go with that.
Update2:
After some time I forgot how to get a SAS/C environment up and running when you start with a fresh Amiga system, so here's a reminder for myself. SAS/C needs some assigns:
SC: - The main installation folder and assign containing the programs, libraries, headers and sample code.
SC:C - The compiler program files (added to path)
LIB: - The SAS/C and Amiga link library files
INCLUDE: - The C compiler and amiga NDUK header files
Should I setup 1 dev environment so I can compile everything one the same platform? This might seem the most logical setup but maybe not:
emscripten will always be a different compiler and a different path
I use osX, Windows en Ubuntu, depending on what I'm doing ... I want to fiddle with code on each of those platforms. javascript is the same everywhere, but C compilers are not ... keeping a dev tool chain up and running on those 3 platforms might more work then its worth - use Docker maybe?
I still have some real (physical) Amiga's ... it would be nice to code on them as well
Allthough there are some really interesting setup to compile amiga binaries on osX, widows and Linux, I ended up setting up a compiler on the amiga itself.
On the emulated amiga, I use a virtual harddisk that resides on Dropbox. So whatever system I'm using, when I fire up an emulated amiga, it will always be the exact same setup on everyplatform because the hard disk file is synced. Even more: after setup, I can just dump the content of the virtual harddisk onto a CF-card, pop it into a real amiga and BAM! everything is up and running there also (try doing THAT with a windows PC!)
All the source files are on Dropbox too, available both local and on the emulated amiga. I can still use my editor of choice (Jetbrains FTW!) and compile it on the amiga.
If I'm REALLY eager, I can even run the virtual amiga on my Android phone - with the exact same virtual harddisk - and compile from there (OK, not very practical, but it's possible)
So let's keep Amiga compiling on the Amiga. We'll worry about mobile later.
Of course, chasing the cross-platform dream is nothing new in the C-world. There are lot's of examples out there and one that is particularly close to my own personal interest is "Emerald X11" by David Tritscher.
It's plain C, has full source code and is already targeted towards multiple platfoms, including amiga. It has no javascript port yet but that's my cup of tea - Done!
Emscripten really is a very cool tool. It's well documented and very easy to setup.
The hardest part is converting endless loops in C. the Emerald X11 code has lot's stuff like.
While(){ input = readInput(); if (input>0) break; }
This doesn't work in javascript. javascript runs in a single thread and you can't use endless loops like this. Instead, everything is event driven. For games and other graphical stuff, there's usually a single function called by requestAnimationFrame to keep the UI in sync with the screen refresh rate. This means that you have to transform these c loops to functions that gets called 60 times a second using emscripten_set_main_loop. This means converting an existing C source to javascript might require some restructuring.
As I'm starting from scratch, this is just something I need to be aware of from the start.
The source of Emerald X11 is very structured. It's a perfect example how to structure your code towards multiple compile targets. There are usually the same kind of platform specific components needed in most types of software:
input
read user input like keyboard, mouse, joystick
reaad files
output
create a screen
display something on the screen
connect audio output
play an audio sample
write files
The goal is to wrap each of those components in a generic function that we can swap with another implementation when compiling for a platform.
in pseudo javascript:
Don't write
function movePlayer(){ var canvas = document.getElementById("canvas"); var context = canvas.getContext("2d"); context.fillStyle = "black"; canvas.drawRect(0,0,800,600)
var playerImage = new Image(); playerImage.src = "playerWalk.png"; context.drawBitmap(playerImage,0,0,32,32,x,y,32,32);
var audio = document.getElementById("audio"); audio.src = "audio/walk.wav"; audio.play(); }
but wrap everything in generic function like
function movePlayer(){ clearScreen(); drawSprite(SPRITE_PLAYER_WALK,x,y,32,32); playAudio(SAMPLE_WALK); }
So you can write the entire game logic in C, controlled by a single "timer" function and do the platform specific implementation per platform. An obvious no-brainer really - but still good to get clear from the start.
The web part of the project is covered. let's focus on the Amiga!
I don't know anything about C. Compiling a C program for me in the past was something like
install toolchain:
google
type "brew something something"
type "sudo brew something something"
wait
wait in awe, wondering why on earth it has to pull in 4 GB of stuff to compile a few lines of code.
cd into src directory
type "make"
cross fingers and hope it works
if it doesn't work - go find someone who knows this stuff.
However ... as my interesting in all things computing expands, and one goes of to explore new systems, new OSses, new hardware, retro hardware, then one also discovers the itch to write some code for that platform.
I'm a web developer, the browser is my natural habitat and javascript is king, queen and army. Javascript runs on everything right! Almost: it runs on any modern platform but it's complety useless on retro platforms like my first true love: the Amiga.
Of course, die-hard amiga developers work directly in assembly, but let's keep it somewhat portable and develop a new skillset that is usefull for the future. Enter C - the one universal language that is truely present on all platforms. As you can transpile C into javascript too these days, that seems like the perfect fit. (Ok, I know maybe Pascal could also be a fit, but ... maybe later)
The goal:
To create a simple game from the same source files for these completely different target platforms, both retro and modern.
Amiga, because it's the coolest platform EVER. preferably as low as kickstart 1.3, but we'll settle for 3.1 for now.
Mobile, because it's the primary and ubiquitous platform. Android first, maybe iOS too if it's not too much trouble.
Web, because it's heaven: the one platform that's available to all, instantly, always, for free.
This blog is a log of my findings. To document the process from a total C-noob to a true cross-platform single-source app.