Armiga project: working some old-time magic
The Amiga is one classic computer that's quite hard to emulate, in part due to it's proprietary chipset and in other part because everything was very time-coupled. In fact, most copy protection systems where based on data density variations in the disk. However, the Amiga emulation comes from 1995, when the first version of UAE was released.
The problem with emulators, however, is the fact that you miss a great part of the original experience, most of the times you need to tailor them for your needs and most likely you'll need to get a pirated ROM for the machine.
Another tricky point of the Amiga was the disk format. By that time, PCs used Double Density disks, holding up to 720KB, but the Amiga disk format allowed to write 900KB in the same disks, but making them non-readable in PCs. However, standard PC disks could be read in Amiga drives. In fact, the Amiga lacked a proper disk controller and gave developers full access to the hardware, just providing basic primitives.
This lack of compatibility makes reading Amiga disks very hard and only one product is available in the market, but is both expensive and far from user-friendly.
So, with this background, there were two main challenges:
- Read Amiga disks
- Make zero-effort emulation
The main goal here was to make a small, affordable controller, able to read the Amiga disk format and transfer disk images to the host for execution.
The first step was finding and reading everything about the Amiga disk format, based on Modified Frequency Modulation (MFM), with 2 sides per disk, 80 tracks per side, 11 sectors per track and 512 bytes per sector. However, as it's MFM encoded, it's not that easy; what you will really be dealing with is 2us wide bitcells. Each bitcell repesent a bit and because of MFM, they are always arranged as 10, 100, 1000.
The most common approach in this point is to use a FPGA to massively supersample the signal and later on do all the maths to get the bits back. In our case, however, we thought another, smarter way was possible: detecting ones (in MFM ones are line state changes) and counting the time between two consecutive ones.
This approach is not problem-free, as you must do all the processing in less than 4us, fine tune the threshold between 4us, 6us and 8us bitcells (it really is trickier than it seems) and must have enough RAM to store at least one track.
In our case, we choose a 32bit, 50Mhz Microchip PIC for the first test and with some work, we achieve to make an ISR in less than 4us and also a clever memory packaging system that allowed us to pack a whole track in RAM before sending.
In addition, we implemented a per-track bitcell calibration system, so each track is read twice: once to find the optimal bitcel boundaries and the other to get the information. With this technique, we are even able to read disk the Amiga 500 is not able to read.
So, now that we have a track in memory, we need to send it to the host. In the first prototype, we used a RaspberryPi and the controller had a IO heaver, so it plugged straight into the Pi, but soon we decide to move from the Pi to a more powerful Dual Core board.
After many design tries, the most effective way of setting the board in the Armiga was making it expansion header inaccesible, so we decided to use the top UART for communication. After lots of tests and reading in the few documents available, we managed to make 115200bps transfers with ACKs.
As said, the Amiga is quite a tricky machine in order to be emulated and fine tuned emulators tend to use big amount or resources. However, we found a striped down version of UAE, tailored to run in the resource-sparse DreamCast: the UAE4All.
UAE4All as it was, run at about 15 fps in the Pi and had a no-so-intuitive menu, plus dozens of options we wanted to get rid of. So there were 2 major challenges:
- Optimizing the emulator
- Creating a new UI
First Amirga controller on a Raspberry Pi
Second Armiga controller iteration (much smaller) on a Cubieboard 2
First Armiga menu
Optimizing the Emulator
The first thing we wanted to optimize was rendering and that meant finding the optimal configuration in SDL to make it hardware accelerated. That configuration, however, is different in the Pi and the Cubieboard 2 (the board the Armiga is based in), so it was a work we had to do twice.
In addition, we optimized some loops and made extensive use of register-cached variables. The result: over 30 steady FPS in the Pi.
Despite these good numbers, the Armiga runs at 50Hz and uses Vsync to synchronize the chipset, so we were getting frameskip. Games were playable but wasn't perfect.
The second major breakthrough in speed came from the hand of parallelization: we split UAE in two processes, one to do the emulation and another one to scale, filter and render the framebuffer. Now, if no limits are set, the Armiga was able to render 200+ FPS in the CB2 at 720p.
As we had margin, we decided to implement some filters, like several tastes of scanline, both 4:3 (original Armiga ratio) and 16:9 and a special non-linear 16:9, which keeps the center of the screen in 4:3 and gradually stretches it as it reaches the edges.
Even with the most consuming filters, the Armiga rendered 50 FPS using around 30% of one core, but we thought we could do better, so we implemented another time-saving feature: frame change detection, so no frame is rendered until there's some change with regards to the current one. Now, 50FPS were achieved at between 15% and 30% of one core
Creating the UI
The original UAE4All UI was not too bad for the Dreamcast era, but certainly not up to today standards. In addition, it had too much stuff in it. As our goal was to make it as simple to use as it gets, all the configuration options should be removed.
With this idea in mind and without the goal to bring it to the final product, we created a new menu, as a proof of concept. Everything was done from scratch and the only library used is SDL, which basically let you draw color rectangles, images and flip buffers.
Even though it was functional, it looked a bit aged with the passing of time, so we decided to move to a more contemporary approach, simpler, cleaner and based on latest UI and UX trends. Our main inspiration was Google Material Design, in terms of icons, colors, layout and how different menus overlay each other giving the depth impression.
The main goal was to make it simple to use with both a keyboard and a gamepad, using as few buttons as possible (only 2 are used in the final version), without losing the context. The user should always know where he is and how to go backwards and forwards and, also, what these actions would mean.