Introduction:
After releasing Enigma at Revision, I was fiddling around with different bits and pieces of code to see if anything promising would arise. When suddenly i was looking at something that became the basis for what later became Microdose
I thought the motion of the effects matched really well with the gradual fading and tone of the intro. And with the code clocking in at 157 bytes at the time, this could very well work as a 128 byte intro for Outline Online 2020
However, as Blossom already had her own entry Beat by a Girl ready for quite some time, I didn't want to enter the 128 byte competition myself at first. But it seemed this thing was looking too promising not to. So I asked Blossom if she would mind if I would create an entry as well. After looking at my work and a short pause she told me she wouldn't mind, but I'd better win.
And so the pressure was on...
But after many days of development, tweaking and polishing, i managed to finish my intro Microdose in time for the 128 byte intro competition at Outline Online.
So, How the f*#k is this even possible?
So here is an intro with custom colors, custom colors, 8 different effects and sound in only 128 bytes.
For reference, here is a full list of effects:
- Perspective Spikes
- Plasma 1
- The Eye
- Cartoon Swirl
- Rays
- Plasma 2
- Perspective Circle
- The Gears
Let me start of by saying that: Yes, it actually takes quite a bit of effort to make it all fit in such a small footprint. But here is my best attempt at pulling apart my approach...
Setting up calculations using the FPU:
The main idea of the intro is that for all the effects above, is that the code sets up that a bunch of stuff via FPU first, which will later be used in different color calculations for each effect.
fild word [bx-12] ; get x
fst st1
fmul st0,st0
fild word [bx-13] ; get y
fst st3
fmul st0,st0
faddp st1
fsqrt
fistp word [bx-8] ; write distance to al
fpatan
fimul word [si+m-100h] ; just noticed i wasted a byte here ;-)
fistp word [bx-9] ; write angle to ah
I didn't want to waste too much registers with the FPU calculations so, I decided to have 2 different calculations writing to the high and low part of the AX register, so that the rest of the registers were still available for the rest of the calculations.
Calling the effects:
Now for each pixel an effect number is calculated, which is used to calculate a jump address. The final color of each effect based on 2 functions of carefully tweaked calculations of exactly 2 bytes each. So depending on the effect, I am able to jump halfway into these functions for different results.
So say we have our effect number in the al register, this would be:
and al,0eh ; make sure the code aligns properly
add ax,fxstart
call ax
Of course the actual tweaking of how to use and stack the available data to calculate your final color is in the actual subroutine itself.
The extra effort (and bytes) is put into the order in which I call the different effects. By putting some extra care in how I would calculate the call address, I will make sure they the effects are separated by a large enough margin, without repeating the same effect too soon.
Adapting code to meet the requirements of a constant:
At some point in my FPU code, I needed a specific constant for my calculations, but of course I didn't want to waste 2 or 4 bytes on one. So one of the things that is common to do when optimizing code is to look for a similar type of constant value within the binary code of the intro itself and use that instead.
Except in this case, with the intro only taking 128 bytes instead of 256, there wasn't any piece of code I could use for this purpose. So I had to flip the script and turn this around and see if there was a piece of code that I could adapt so that it would assemble to the constant that I needed ;-)
Fortunately there was a byte of alignment code between the 2 parts of color calculation function that I could use to adapt my code. After a bit of experimentation I was able to assemble my constant with the following 2 instructions:
add [bx+si],ch
hlt
which when re-aligned assembles to:
sub al,dh
which in turn could be used in one of the effect calculations.
Using Custom Colors:
While most 128 byte intros don't opt to use a custom color palette because it easily can shave of 20 to 25 bytes of available space, I thought it was important enough for the look and feel of the intro, I want my intros to look good regardless of size constraints. Therefore I thought the trade off was worth it. Using the bios function to set custom color palette instead of VGA ports here helped to keep the extra overhead to a minimum, And by making sure the color values are calculated and aligned in a certain way, I could save some bytes in the final color calculation as described below.
Merging effect and color shade:
To give the intro more variation, I was able to split the effect number calculation and the sub-palette color calculation for each pixel into 2 different calculations that would not be aligned 1 to 1. This would make sure that later in the intro, when the same effects repeat, they would be rendered in a different set of colors.
The effect routine delivers my color values in AL with a 0..255 range, I will then put my sub palette index calculation into AH and then shift the whole of AX to the right by 4, so that the high nibble would contain the sub palette index and the lower nibble (0..15) the shade color within that sub palette.
Sound:
As for sound, both me and Blossom looked into the smallest amount of code in our 128 byte prods that could produce any kind of sound and came up with the following:
mov dx,0x330
imul ax,bp, magicnumber
aam 0xDE ; just as a playful nod to our german competitors
which basically outputs a pseudo random value to the midi data port each frame.
The AAM 0xDn part (or alternatively an AND AL, 0xDn) is not entirely random as the high nibble includes both the set note and change instrument commands. If you just want to play a single instrument, this can be changed for a AAM 0x9n or AND AL,0x9n instruction instead, where the n part acts as the second magicnumber ;-)
Blossom started writing a tool which looks for different values for magicnumber 1 as well as the lower nibble of the AAM and AND instruction. But as the deadline was nearing, we ended up not using this for our entries and go with some handcrafted values instead.
Freedos and Dosbox compatibility:
Splitting up the code for Freedos (a version that checks the escape key, but without sound) and Dosbox (version with sound, but without checking escape key and using some dosbox specific optimisation tricks) gave me a few more bytes to work with.
Color Tweaking:
So after most of the intro was done I spent a ridiculous amount of time trying to tweak the final color scheme. For the longest time I had a color-scheme of just an orange and purple shade alternating for that super television vibe which I really liked. But it kind of made the different effects stand out less than using 4 different color shades. But the 4 shade palette had a green shade that I didn't like and that I couldn't get rid of without spending a lot of bytes of code. So after a lot of back and forth... and back... and forth... I finally ended up with the 4 shade palette for the final intro.
Conclusion:
Even though it wasn't my original intention to create a 128 byte intro, I am very proud of how the intro turned out and that I was able to keep the promise I made to Blossom by winning the 128 byte intro competition at Outline Online 2020 with Blossom coming in third with her entry Beat by a Girl.
The fact that we also managed to win he 256 byte intro competition as well with our party@home coded intro called: Rush was the icing on the cake!
So there you go, I hope you've enjoyed reading this write up. For more information, you can download the intro complete with sourcecode at pouet.net
If this write up was in any way helpful to you, or there are other topics you would like to see me write about in my blog. Feel free to let me know via email (see my sourcecodes) or hit me up on discord demoscene channel.