So I was recently reminded that I have a blog or whatever, since I met someone who actually read it (Hi Torkild!). So why not make an entry for my 40K “Fat Circuits”?
Yeah, this is not so interesting for established coders, more for the aspiring ones I think. This intro doesn’t have much advanced code anyway, but maybe a few tricks that could be of interest.
So I’ll just go through the screens here and try to explain each of them.
This has been lying around my harddrive for ages. It was actually a precursor to “Hymn to Hipparchus” although it works differently. We have 5 independently moving circles here, but Amiga only has 2 scroll registers. So we gobble up a lot of memory instead, just making 16 different copies of the circle (where each is shifted +1 pixel to the right), and the display routine selects the correct one for each bitplane. Easy, and doesn’t take much CPU (leaving 99% for the actual precalcing).
Parallax vertical bars
This one is more hassle than it looks like, to be able to have the text on different layers. It’s made in Extra Halfbrite (EHB). I considered HAM but that’s Platon’s game 🙂
For each bar we have a bob in 1 line and 6 bitplanes. We create a mask for each of them (That is, a copy of the graphic with 0 where it’s transparent, and 1 where it’s opaque. Check out “cookie cutter” concept in the Hardware manual. We’re also keeping track of speed and position in 16.16 precision. We draw them from the back to the front with double buffering. Always the same z-values thankfully. The screen is built with negative modulo so that it’s just repeating the same line – except the areas with text. So for a regular bar it’s just blitted into this onelinebuffer that repeats on every line.
Logos look like this in memory
When we get to the first text layer we have to draw the text on top of the bars. The text is made with a 5 pixel high font (and repeated on screen) so fortunately we only need to copy the bars to a 5 line area, then OR the text on top of that. Text color is the last palette entry (31) to make it easier. The 6th plane has to be treated special, since we want it to be 0 where there is text, and keep its value from the bars otherwise.
So far OK, but then we want even more bars, on top of both the text and the other bars. We make a new temporary onelinebuffer and keep putting bars into it until we reach the level of the 2nd text. After that we have to put those bars on top of all the 5 lines of the text buffer. We’ll need a whole-line mask for that, so we create one based on plane 1-5, then use that to blit the new bars on top of the first text buffer. But we also need the new bars on top of the old bars! So there’s another blit again.
So then we have to deal with the second text layer, which works pretty much the same way as the first (with a new 5-line area). Finally we put in the last bars that are on top of both texts. Works pretty much the same way except you have to put them on top both of the text buffers.
Phew, that was a lot of blitting! So it’s not particularly advanced, but there’s a lot of blitter administration. I don’t recall having seen this on Amiga before with the text layers mixed in between the bars.
Scope onna slope
Oscilloscopes are my favorite effect! So my brain keeps coming up with small variations on that theme. In this case they are rotating around a point in the center and radiating out.
Screen layout is scope dot plot area in the first plane, and the sweet 16 color background logo in plane 2-5. We can’t use the interleaved bitplanes trick here because it would be too awkward with the plotting plane.
Did I mention we use The Player 6.1? Yeah, LSP is the shit but it doesn’t have oscilloscope data support I don’t think. P61 has a subroutine P61_osc you can call for each channel, giving you both a pointer to the sampledata and a counter for how much data will be played this frame. I also wanted volume scaling (amplitude is proportional to channel volume) which you have to do yourself. Amiga sampledata is PCM, 8 bit and you can just read them from RAM and use them. Reading from hardware registers won’t work, in case you wondered.
So the easiest case for an oscilloscope plot is a straight up horizontal line, where you increase x by 1 for each sample data and plot a pixel with y-value according to the sample value. Not at all complicated if you know where to read the samples from, and this was seen in demos as far back as 1989 (maybe 88? Ask your local demoscene historian).
So to plot a scope on a slope you need to
- Describe the slope as a delta x and delta y (in the easy example we had delta x = 1, delta y = 0, but here we want to be able to describe a full circle). For this, I just used the sinus wave for the circle (it has 256 signed values) and used divs instruction. So we basically store 256 pairs of x/y values in 16.16 format for this.
- The amplitudes aren’t just straight offsets on the y dimension, but should be expressed as a function of the sample value and the slope. We want the amplitude offset to be perpendicular to the slope. For convenience I store 256 sets of values from 0-63 (amplitude is reduced by 4x). Pick the correct set based on the slope + 90 degrees (translates to 64 steps in the sinewave) and from that set pick the correct x/y pair based on the sample value divided by 4. Signed of course.
Add some more shenanigans for rotation etc and that’s our part.
The glue that binds
Time to talk about the overlay circle. From the scopes we fade into it and it start moving, and I’m very happy with the way that looks. It gives a sense of connection between the different parts, and lends some elegance. Technically it’s just a line picker running in the 1st bitplane where we used to have scope plots. It approximately matches the shape of the moon in the background. While it’s simple, it’s actually a lot of work getting something like this to work with all the parts. And it ties you down to using the same bitplane widths in the connected parts (or facing a lot of hassle with changing it on the fly, which I had to do for the endpart because SOME PEOPLE suddenly wanted a scroller there 😉 ).
So for precalced data we have
- 320 lines of increasing width, with pixels spreading from the middle. These are the bitplane lines that we pick from.
- 122 sets of 256 values each, describing what lines to pick for a particular circle size (The number 122 is based on the nmuber of steps between the largest and smallest circle we need to show). This was not quite trivial to code, as you need the circles to be perfectly round. I ended up using Bresenham’s circle drawing algorithm and adapting it to my needs. Yep, apparently that dude didn’t only draw lines.
- A sine wave determining the circle size for each frame. Result looks super smooth!
Fat Circuits circuitboard/text
This is just fading and blitter work, nothing special. Was included because Critikill made kickass gfx for it.
Circuit mind picture
Another gfx heavy part. Initially we planned to use Critikill’s “Winter Patrol” but he came up with this better fitting one. He and Virgill came up with this idea for the pattern with moving highlights around it, and it wasn’t that hard to code. Each pixel moves on a track that’s defined by handmade data.
Of course when fading the whole thing out I noticed that many of the pixels didn’t fade properly. This turned out to be because I made mistakes in the handmade track data, so they were travelling outside their lines! Had a lot of “fun” correcting that.
Bigger, Baller, Better
While the last part was running, we were frantically precalcing this one. Screen layout is: Plane 1-4 – the balls, plane 5 the overlay grey circle, and plane 6 (Halfbrite again) the Waikiki text.
To start with the text, there is a line picker again, and we also calculated 24 versions of the text in different widths. Very pleased that it looks good with so few frames. There’s also a simple y-scrolling thing and an attempt to make a gradient at the top and bottom. Since it’s EHB we can’t really fade it properly which would have looked better.
The rotating balls are an 8 frame animation where we also add colorcycling. There are 15 balls, one of each color, and that sequence repeats once. Of course, when we plot them (using blitter for that), the last circles will always be up top and it won’t look properly. So we plot it once starting at the bottom of the circle, then copy the top half of the frame to a safe place. For the second pass we start plotting at the top and then copy the bottom half.
Confused? Look at this image.
When precalcing, we use a screen that’s both taller and wider than the display area, just for convenience so that a circle plotted on the left side won’t wrap over and ruin the right side of the buffer.
As for memory considerations, that was pretty tough here. As the 8 frames are plotted they are squirreled away wherever there’s room. only 3 fit in chipmem at this point because of the precalc buffer, the circuitmind image being displayed, etc. So the rest go in fastmem (public mem) and overwrites stuff we’re already done with. Right before this part will be shown we copy it all back to chipmem. Still, that’s 8 frames, each of 40×256 in 4 bitplanes. Total size 327 680 bytes. Add to that the 90878 bytes of sampledata, and the 320*40 linepicker lines for the grey ball overlay, AND the 40*118 buffer for the zooming text on top of that. Yeah, it’s getting tight in here. So that’s why it won’t run on A500 if you have a df1: plugged in, in case you wondered.
It was Virgill’s idea to have some dithering on the balls. I thought that looked great. Wanted to try out some ordered dithering, never got around to it.
Mmm, more scopes. These xy scopes are recycled from Chillobits, of course. But I needed an endpart so. See point 12 in the chillobits writeup https://everydog.home.blog/2020/04/13/chillobits-writeup/ if you wonder how they’re made.
Hope you enjoyed that, cheers!
Nosferatu, 23/1 2023