By toms / Pulpo Corrosivo.
At first glance, doing a color fade by using a palette of 27 colors does not seem to be a good idea. However, Supersly / Les Sucres en Morceaux wrote some years ago an article (in french) about the different ways to achieve this effect with success on a CPC. Here we are going to focus on GRB fades.
The easiest way to achieve a GRB color fade is to calculate by hand the intermediate colors and to store them in memory. Considering a 6 steps fade applied on 17 colors (including the border), your datas would use 6 x 17 = 102 bytes (original palette not included), just for one fade! Imagine the amount of RAM needed for several fades in a demo or a game!
But as a 64 NOPs reader, you can’t be satisfied with this poor solution, much too greedy. So we are going to write a short routine (88 crunched bytes with Shrinkler) which will do all the work from a starting palette. It could be shorter by exploiting the holes in the GRB conversion table but I didn’t do this here for a more understandable routine. I used this kind of stuff in Daymo of the Tentacle.
GRB conversion table
On CPC, you may know that the 27 colors are made of the green – red – blue components, each of them can only have 0%, 50% or 100% values. The Green coefficient being 18, the Red one 6 and the Blue one 2, we can easily get the BASIC value (software color code) of any color with this equation: 18 x G + 6 x R + 2 x B. You can get more details on this by reading this article (still in french).
However, the CPC world is cruel: as we will deal directly with the Gate Array, we will use INKR commands (hardware color codes) which have a kind of logic but with exceptions (discussed here). So we have to use a table to convert GRB values to INKR commands.
I chose to represent a color using 2 bits per component, like this: 00 GG RR BB. For any component, 11 means 100%, 01 means 50 %, and 00 means 0%.
|BASIC||INKR||G R B||Index|
|00||&54||00 00 00||0|
|01||&44||00 00 01||1|
|02||&55||00 00 11||3|
|03||&5c||00 01 00||4|
The GRB value is the index refering to the table where we store the corresponding INKR commands. In our example, our conversion table will be located in &1000, so we will store &54 in &1000 (GRB = 0), &44 in &1001 (GRB = 1), &55 in &1003 (GRB = 3), etc. White being %00111111, this color will be the last value stored in our table which will be 64 bytes long (with some holes inside as not all indexes are occupied with INKR commands).
Here is the full GRB table conversion:
GRB_GA byte &54,&44,&00,&55,&5c,&58,&00,&5d,&00,&00,&00,&00,&4c,&45,&00,&4d byte &56,&46,&00,&57,&5e,&40,&00,&5f,&00,&00,&00,&00,&4e,&47,&00,&4f fill 16,&00 byte &52,&42,&00,&53,&5a,&59,&00,&5b,&00,&00,&00,&00,&4a,&43,&00,&4b
To get a INKR command from a GRB value, it’s a piece of cake:
ld h,GRB_GA/256 ; GRB_GA = &xx00 ld de,palette ; palette to convert ld a,(de) ld l,a ; HL now points on the corresponding INKR command
Now we have our conversion table, we can write the code to do the actual fade. At first, we will fade out from the palette to white to make a superb flash effect (with some slight changes, we could fade to black. Yeah!).
To achieve this, we will gradually mask the GRB components of each color, from right to left, using a OR. It means that we start to fade out the blue component, followed by the red one, and finally by the green one. This order is no accident and is well explained in the article previously quoted.
ld c,%00000001 ; start by the blue component ld h,GRB_GA/256 ld de,palette ld a,(de) or c ; force the blue component to be x1 ld l,a sll c ; slide C to deal with the second bit of the blue component
By applying this mask to each color and sliding it to the left at each frame, we get these color values: %00xxxxx1, %00xxxx11, %00xxx111, %00xx1111, %00×11111, and finally %00111111 (white!).
To fade in from white to the palette, it’s easy! We just have to take the reverse path by starting with a %00011111 mask and sliding it to the right at each frame.
ld c,%00011111 ; start by the green component ld h,GRB_GA/256 ld de,palette ld a,(de) or c ; leave the green component to its palette value ld l,a srl c ; slide C to deal with the second bit of the green component
Link the 2 effects
To fade out and fade in with the same piece of code, we just have to call 2 distinct subroutines: one loading C with %00000001 and sliding it to the left with SLL (fadeout), and the other loading C with %00011111 and sliding it to the right with SRL (fadein). The 2 subroutines call col_loop where we actually apply the mask with OR C, transform the GRB value into an INKR command and send it to the Gate Array. They loop until all the components are processed. Finally, here is the full routine:
org &1000 GRB_GA byte &54,&44,&00,&55,&5c,&58,&00,&5d,&00,&00,&00,&00,&4c,&45,&00,&4d byte &56,&46,&00,&57,&5e,&40,&00,&5f,&00,&00,&00,&00,&4e,&47,&00,&4f fill 16,&00 byte &52,&42,&00,&53,&5a,&59,&00,&5b,&00,&00,&00,&00,&4a,&43,&00,&4b fadein ld c,%00011111 ld h,GRB_GA/256 fadein_loop call vbl call vbl ld de,palette ld a,h ; 17 colors to fade (H = &10) call col_loop srl c jr c,fadein_loop ; loop until C >= %00000000 ret fadeout ld c,%00000001 ld h,GRB_GA/256 fadeout_loop call vbl call vbl ld de,palette ld a,h ; 17 colors to fade (H = &10) call col_loop sll c bit 6,c ; required after SLL for the exit condition jr z,fadeout_loop ; loop until C <= %00111111 ret col_loop ex af,af' ld a,(de) or c ld l,a ex af,af' ld b,&7f out (c),a outi inc e ; palette located between &xx00 and &xxff (or use INC DE) dec a jp p,col_loop ; loop until the 17 colors are processed ret main ent $ di call fadeout call fadein jr $ palette byte 1,63,0,0,0,0,4,63,5,61,60,12,29,28,13,0,1 vbl ld b,&f5 in a,(c) rra jr c,$-3 in a,(c) rra jr nc,$-3 ret
Of course, the listed values under the palette label are GRB ones (meaning they are GRB conversion table indexes – %00GGBBRR), not INKR commands! To slow down the effect, just add some extra CALL vbl.
Fade to black
If you want to fade to black instead of white, just change the OR into an AND, and invert the C values, SLL / SRL and JR Z,xxxx / JR C,xxxx. If you understood how it works, you will easily succeed :)
A small flaw to this routine: some colors become white before the others while we would like to have all of them becoming white simultaneously.
Another flaw, it only allows to fade to / from white or black (palette extremities). A massive improvement would be to allow fades to / from others colors, or to fade from one palette to another palette.