Plascii Petsma by Cruzer/Camelot has one of the nicest looking plasma effects I've seen for the C64. Since he included the source code, I was able to port it to the Z80 and TMS9918.

On top of the features in the original C64 version, I have added the following interactive features:

change the palette independent of the effect

hold a particular effect on screen indefinitely

switch immediately to a new effect

runtime generation of random effects

adjust parameters to customize an effect

In this article, I will explain how the code works. Before getting into the specific implementation details, it helps to understand how plasma effects work in general. Rather than write another explanation when others have already done it well, I'll refer you to this one, which covers the basic concepts using C code.



The implementation used here defines each plasma effect using a set of constant parameters:

SineAddsX: defb $fa,$05,$03,$fa,$07,$04,$fe,$fe SineAddsY: defb $fe,$01,$fe,$02,$03,$ff,$02,$02 SineStartsY: defb $5e,$e8,$eb,$32,$69,$4f,$0a,$41 SineSpeeds: defb $fe,$fc PlasmaFreqs: defb $06,$07 CycleSpeed: defb $ff ColorPalette: defw Pal01

Each of these parameters governs a specific aspect of the plasma calculation, which will be described in detail below. This scheme allows a huge variety of unique plasma effects to be specified in a compact format, and random effects can be easily produced by generating parameters in the appropriate ranges.

When implementing a plasma effect on an 8-bit computer, there are several challenges to overcome. First, the processor doesn't support floating point math or provide a sin function. This can be overcome using a sine table, which contains pre-computed sine values converted to 8-bit integers.

Normally, the amplitude of a sine wave ranges from -1 to 1. When converted to 8-bit integers, the amplitude ranges from -128 to 127. For ease of lookup, the sine table will contain 256 bytes representing a full period of the sine wave.

A single period of the sine wave first falls from 0 to -1, then rises back from -1 to 0, rises further from 0 to +1, then falls from 1 to 0. Each quarter of the period is the same curve, flipped vertically and/or horizontally. To save space, only the first quarter needs to be pre-computed and these values can be mirrored at runtime.



I used a python script to generate the first quarter of the sine table. The resulting data looks like this:



SineSrc: defb $81,$84,$87,$8a,$8d,$90,$93,$96 defb $99,$9c,$9f,$a2,$a5,$a8,$ab,$ae defb $b1,$b4,$b7,$ba,$bc,$bf,$c2,$c4 defb $c7,$ca,$cc,$cf,$d1,$d3,$d6,$d8 defb $da,$dc,$df,$e1,$e3,$e5,$e7,$e8 defb $ea,$ec,$ed,$ef,$f1,$f2,$f3,$f5 defb $f6,$f7,$f8,$f9,$fa,$fb,$fc,$fc defb $fd,$fe,$fe,$ff,$ff,$ff,$ff,$ff

The MakeSineTable routine builds the sine table for a complete period from the precalculated quarter period. The first 64 values are copied verbatim from the precomputed values. The next 64 values are flipped horizontally by copying them in reverse order. The last 128 values are flipped vertically by complementing them. The vertically flipped values are written twice, first in forward order, and then in reverse order to flip them horizontally and complete the period. The resulting lookup table is stored on a 256-byte boundary so that a sine value can be looked up by loading a single register with the input value.

The next challenge to overcome is representing a smooth gradient of color when the video chip only has 15 colors to work with. To do this, we use dithering to combine different ratios of the 15 colors we have available. This is the full set of 64 gradient tiles used in Produkthandler Kom Her on the C64:

The TMS9918 tile mode defines 256 tile patterns, each of which is associated with a specific foreground and background color. For palettes of 8 colors each, we can use 32 tiles per color,...