GRB Color fade

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%.

BASICINKRG R BIndex
00&5400 00 000
01&4400 00 011
02&5500 00 113
03&5c00 01 004

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

Fade out

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!).

Fade in

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 :)

Possible improvements

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.