2018-12-27

Extended Color Mode from BASIC v2

ECM, short for extended color mode, is a display mode of the VIC-II, the graphic chip of the Commodore 64.

Display modes are controlled by two registers of the VIC-II:

Addr|Bt7|Bt6|Bt5|Bt4|Bt3|Bt2|Bt1|Bt0|Function
----+---+---+---+---+---+---+---+---+--------------
D011|RS8|ECM|BMM|DEN|RSL|  YSCROLL  |Control reg. 1
----+---+---+---+---+---+---+---+---+--------------
D016| - | - |RES|MCM|CSL|  XSCROLL  |Control reg. 2
----+---+---+---+---+---+---+---+---+--------------

ECM is enabled by setting bit 6 of control register 1. Being a hi-res text mode, bit 5 of control register 1 for bitmap mode and bit 4 of control register 2 for multi color mode  must be clear. Bit 4 of control register 1 for enabling display must be set.
From BASIC:

5 rem ** switch to ecm from any mode
10 poke 53265,peek(53265)and(255-2^5)or(2^6 + 2^4)
15 poke 53270,peek(53270)and(255-2^4)

At power-up:

5 rem ** enable ecm from hires text mode
10 poke 53265,91

Unlike standard text mode and multi color text mode wich can display 256 different characters, ECM can only display 64 characters (or tiles).
Unless also other settings are changed after power up, ECM will use the first 64 character definitions from the standard character sets, either the uppercase / semi-graphic or the lowercase / uppercase set.
This means that the available characters will be letters (only uppercase or only lowercase), numbers, punctuation, and little more.
More specifically the available characters will be the ones with screen codes ranging from $00 to $3F, or ASC codes from $40 to $5F and from $20 to $3F. So from $20 to $5F but with the two halves inverted with respect to the screen codes.

Addr|Bt7|Bt6|Bt5|Bt4|Bt3|Bt2|Bt1|Bt0|Function
----+---+---+---+---+---+---+---+---+--------------
D021| - | - | - | - |      B0C      |Bg. color 0
----+---+---+---+---+---+---+---+---+--------------
D022| - | - | - | - |      B1C      |Bg. color 1
----+---+---+---+---+---+---+---+---+--------------
D023| - | - | - | - |      B2C      |Bg. color 2
----+---+---+---+---+---+---+---+---+--------------
D024| - | - | - | - |      B3C      |Bg. color 3
----+---+---+---+---+---+---+---+---+--------------

The background color is selected among four by the two most significative bits of the video memory cell, their value from 0 to 3 selects the color specified in one of the four color registers from $D021 onwards.

20 poke 53281,6:poke 53282,7:poke 53283,8:poke 53284,9

When working with screen codes directly, selecting the background is straightforward. With mediation of BASIC and the ASC codes the path is uphill.
A simple PRINT of characters from ASC $20 to $5F does the job for background 0:

30 for i=64 to 95:print chr$(i);:next:print
40 for i=32 to 63:print chr$(i);:next:print

Getting background 2 is as simple as activating the reverse mode.

70 print chr$(18);:for i=64 to 95:print chr$(i);:next:print
80 print chr$(18);:for i=32 to 63:print chr$(i);:next:print

Getting background 1 is easy for letters because shifted versions, ranging from ASC $61 to $7A, have screen codes with bit 6 set. The same characters are available in the ASC range $C1 to $DA, where with respect to unshifted letters they have bit 7 set. Also numbers and punctuation characters have a corresponding range with bit 7 set that generates screen codes with bit 6 set. So, overall, to get background 1 you set bit 7 of a ASC code, for any of the characters in the range $20 to $5F.
Getting background 3 is like getting background 2 with reverse mode activated.

50 for i=192 to 223:print chr$(i);:next:print
60 for i=160 to 191:print chr$(i);:next:print

90 print chr$(18);:for i=192 to 223:print chr$(i);:next:print
100 print chr$(18);:for i=160 to 191:print chr$(i);:next:print

Here is the full source code to get the 64 available characters with all four backgrounds:

10 poke 53265,91
20 poke 53281,6:poke 53282,7:poke 53283,8:poke 53284,9
30 for i=64 to 95:print chr$(i);:next:print
40 for i=32 to 63:print chr$(i);:next:print
50 for i=192 to 223:print chr$(i);:next:print
60 for i=160 to 191:print chr$(i);:next:print
70 print chr$(18);:for i=64 to 95:print chr$(i);:next:print
80 print chr$(18);:for i=32 to 63:print chr$(i);:next:print
90 print chr$(18);:for i=192 to 223:print chr$(i);:next:print
100 print chr$(18);:for i=160 to 191:print chr$(i);:next:print