Posted: January 7, 2020
Since Java Grinder (a Java byte-code compiler) already supports the Motorola 68000 CPU with the Sega Genesis I figured it shouldn't be too hard to extend the MC68000.cxx class to support the Commodore Amiga computer. More specifically, the original Amiga 1000.
This computer is to me a technological work of art. The hardware registers for programming graphics and sound are laid out in a mostly simple way and give the ability of four channel 8 bit sound, hardware sprites, a copper coprocesor, and a blitter coprocessor that independently of the CPU is capable of copying graphical areas, drawing lines, and drawing shapes. Released in 1985, the OS had full windowing interface and could do preemptive multitasking.
The entire demo is 138k and the source code is included with the Java Grinder repository. As usual, everything assembles with naken_asm.
Related Projects @mikekohn.net
Above is a video of the demo running on an Amiga 1000. After it completes there is a video of the demo running on the emulator. I couldn't decide the best way to present it since.. well.. it's much more impressive running on the real machine, but it's kind of hard to see it when the video comes from a camera. The emulator version has a little trouble with the rotating box, it displays perfectly in the Amiga 1000 but kind of drops some sides in the emulator. The colors also look better on a real monitor since the CRT kind of blends the colors a bit while the emulator shows higher detail and more imperfections.
I kind of regret the Mandelbrots here too.. takes too long to generate and is kind of boring, but for some reason it felt required.https://youtu.be/8tHf1CpT90g
I could have shown off the hardware a bit more with the demo, but just like when I did the Playstation 2 I got kind of burnt out from learning the hardware and creating the API. Mostly the demo just shows off different possibilities of what the API can do.
The demo starts out with the typical "3 Billion Devices Run Java" image that the other demos have. I recorded myself playing Bach's Invention 13 on guitar and used the audio editing program Sox to convert it to 8000 samples per second 8 bit.
The second part of the demo puts the Amiga in double playfield mode. In this mode the demo draws the star field in an 8 color playfield while it can draw different logos in a different 8 color playfield. Anything in the logo playfield that is color 0 is transparent so the stars can be drawn without having to redraw the logo. The logos are copied to the screen memory using the Amiga's blitter. The plot routine that draws the stars is written in Java.
The music here is a 1+ minute song that I called
The third part of the demo shows 2 Copper bars moving up and down while an Amiga logo is in the middle of the screen. Four sprites that spell JAVA move around the Amiga logo. The Amiga logo is drawn with the Blitter and erased with the blitter. After the logo disappears a 2D is drawn using the Amiga.plot() function which is written in assembly. The line drawning routine is written in Java.
Finally, in the end there is a Mandelbrot.
The first song in the demo is Bach's Invention 13 which was used as the 1980's Commodore theme song. I recorded it by throwing a .mid file I downloaded into Apple's Garage Band, muting the first channel. Then I line-direct recorded myself playing the first channel on guitar. In the demo I used a different recording that I recorded mic'd.
The second song, I recorded some heavy E(flat) Phrygian sounding thing. I used Drums++ to create the drum track and recorded a bass track line direct. Then I recorded a bunch of guitar solos. My intent was to loop the heavy part and at times throw in a small cut from the solos. It ended up I was running out of memory so I only used two small pieces that were 26k each. The Bach part at the beginning was 40k.
The original recordings are here:
The Amiga's bitmap display is quite different than the way most computers do graphics. Insetad of each pixel being represented by a single byte (or 3), there are bitplanes. Each plane represents 1 bit of a color palette index. So if there is a 2 bitplane 320x200 pixel display setup, it means there are two memory regions of 8k. If 0x80 was written to the first byte of the display and 0x00 to the second, palette color 1 is used for the first pixel and 0 is used for the next 7. If 0x80 was written to byte 0 of both bitplanes, palette color 3 is used for the first pixel and 0 for the next 7.
This actually makes some things quite a bit easier than it was on the Sega Genesis which uses a more typical of a gaming system tiling system. Plotting a pixel for example was a lot simpler. Tiling does have other advantages though.
A positive of the bitplane system is seen in the Logo.java part of the demo. Since the spinning box is only white, color palette 1, to make the box spin only the first bitplane needs to be written to.
Since the Amiga hardware registers point up to 6 bitplanes to different memory regions, it's possible to do double buffering. One set of bitplanes is being drawn while the other is being displayed and on vertical blanking the bitplane pointers are switched out. The Amiga demo here doesn't use this technique.
The Copper is a coprocessor inside the Amiga that has basically 3 instructions: 1) move a value into a graphics or sound chip 2) wait for the video beam to hit an (x,y) location 3) skip the next instruction if the video beam aleady passed an (x,y) location. This allows for some pretty interesting things. For example there are 32 color palettes in the original Amiga and each can be set up to display 1 of 4096 colors. Using the Cooper, the screen can be set up with a bitmap of only 2 colors (palette colors 0 and 1) but since the Copper can change the palettes as the video beam moves down the screen, all 4096 could be displayed on the screen at one time. All this happens while the CPU is free to run other code.
The way the copper manipulates the screen in sync with the video beam actually reminds me a lot of how the Atari 2600 works. Since the Atari doesn't even have a bitmap display, screen manipulations like this have to be done in order to draw things. On the Atari this is required and has to be done by the CPU. On the Amiga this is just an extra feature and done by a completely separate processor to the CPU. Not surprising, the Amiga and Atari 2600 hardware were designed by the same person, Jay Miner.
Since hardware registers that point to sprite data and bitplane data need to be reset on vertical blank (in other words, every time the video beam is about to redraw the display), Java Grinder gives some methods for setting sprite and bitplane data.
For Java Grinder, I made some high level methods for generating Copper lists. To change the background color 3 times as the video beam moves down the screen can be as simple as:
The blitter is a coprocessor in the Amiga that can copy sections of memory or draw lines and blocks. The blitter runs independent of the CPU, so while the blitter is manipulating the picture on the screen of the computer, the CPU is free to run other code. The blitter has 3 source channels and 1 destination channel (usually the display). The source channels can be used as logic functions to do different things such as cookie cutting copies (pretty much an alpha channel).
The Java API I came up with for the blitter has the user instantiating blitter objects that are filled out in memory. When the user calls one of the "run" methods, memory is moved into the hardware registers and the blitter is told to start operating on those registers.
There's a lot more the demo could have done with the blitter, but I ended up just doing a bunch of copies, a little bit of animation, and a small point where it does a block screen clear.
One interesting feature of the Amiga that made coding this a bit ackward is separation of chip memory and fast memory. The Amiga has this hardware feature where there is a bank of memory called chip memory that both the CPU and the graphics, sound, blitter, and copper coprocessors can access. When one of the other processors is accessing RAM, the CPU can't acceses memory and goes into a wait-state. So if there is a complicated copper list, the screen is being drawn, the blitter is drawing something, etc... this can cause the CPU to pause. There is another section of memory called fast RAM that only the CPU can access. Therefore the CPU can execute code in fast RAM full speed while the other processors manipulate or read chip RAM. Unfortunately (or maybe fortunately since I needed most of the RAM for graphics / sound) the Amiga 1000 I used has no fast RAM.
For Java Grinder, when the compiled software starts up it requests from the operating system currently 100k of memory for the user to use. I had this bumped up to 250k at first, but I couldn't get all the sound files working so I lowered it to 150k. When the demo didn't run the first time I lowered it even more to 100k. I probably should have raised it back up to 150k since the problem was with the vertical blanking code, but it's currently sitting at 100k. It could probably read a static int variable to set the size according to what the software needs or there could be an initHeap(int size) method added.
Speaking of memory, another issue I had was the 68000 code in Java Grinder was written for the Sega Genesis Java code. Since the Sega always loaded the program into the same location in a ROM, things like static references pointed directly to memory locations. For the Amiga, the code needed to be relocatable so I had to override the 68000 functions that referenced statics so that they are loaded relative to the program counter (PC).
Amiga Executable Format
To make a program loadable by the Amiga OS, it needs to be a "HUNK" formatted program. I added HUNK support to naken_asm but decided to create the format for Java Grinder using dw data pieces. I did this because there is supposed to be a way to tell the HUNK file format that the executable section should be loaded into chip RAM instead of fast RAM. In the end that didn't seem to work anyway so I'm tempted to remove that and just let naken_asm create the HUNK file.
Running The Demo
All my development was done on a Linux box using FS-UAE as an emulator. As usual, getting the software running on a real computer was quite a bit of work. My first idea was to use one of these Gotek floppy drive emulators in an Amiga 500 with an external floppy drive connected and copy the file over. I got the Gotek and even flashed it with the Amiga firmware, but after opening the Amiga 500 to see how easy it would be to take the old drive out and swap the Gotek in, I decided to try first over a null modem cable.
I connected the Amiga 500 to an Ubuntu based netbook with a serial USB cable running minicom. Using a terminal program on the Amiga, I was able to transfer the software using the ZMODEM protocol from the netbook to the floppy disk in the Amiga 500. I wasn't sure what baud rate would work so I did it at 9600 baud.. took over 2 minutes for the 140k demo.
I ended up having to do this 6 or 7 times since the demo had problems the first few times I tried to run it. Biggest issue was I was unknowningly using FS-UAE in PAL mode, so when I ran it on my NTSC Amiga all the timings were off. There was also an issue with the inVerticalBlank() method.
I recorded the demo three different ways. I first recorded it with just a camera pointed at the Amiga just to prove it runs on the real machine. I think recorded it the same way I did the Playstation 2 Java demo, by doing composite out into a DVD recorder. The low resolution screen looked so terrible composite out (maybe it was a bad video cable?) that I decided to just do the second recording through the emulator.
Copyright 1997-2023 - Michael Kohn