2020-06-18

Extended Color Mode from BASIC v2, V3.5, V7

ECM, short for extended color mode, is a display mode of the VIC-II (C128 and C64) and of the TED (Plus/4 and C16).

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

VIC2|TED |Bt7|Bt6|Bt5|Bt4|Bt3|Bt2|Bt1|Bt0|Function
----+----+---+---+---+---+---+---+---+---+--------------
D011|FF06| - |ECM|BMM|DEN|RSL|  YSCROLL  |Control reg. 1
----+----+---+---+---+---+---+---+---+---+--------------
D016|FF07| - | - |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 ** c128 / c64 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)
5 rem ** plus/4 / c16 switch to ecm from any mode
10 poke 65286,peek(65286)and(255-2^5)or(2^6+2^4)
15 poke 65287,peek(65286)and(255-2^4)

At power-up:

5 rem ** c128 / c64 enable ecm from hires text mode
10 poke 53265,91
5 rem ** plus/4 / c16 enable ecm from hires text mode
10 poke 65286,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.

The background color codes are stored in four registers:

VIC2|TED |Bt7|Bt6|Bt5|Bt4|Bt3|Bt2|Bt1|Bt0|Function
----+----+---+---+---+---+---+---+---+---+--------------
D021|FF15| - | B0L (TED) |      B0C      |Bg. color 0
----+----+---+---+---+---+---+---+---+---+--------------
D022|FF16| - | B1L (TED) |      B1C      |Bg. color 1
----+----+---+---+---+---+---+---+---+---+--------------
D023|FF17| - | B2L (TED) |      B2C      |Bg. color 2
----+----+---+---+---+---+---+---+---+---+--------------
D024|FF18| - | B3L (TED) |      B3C      |Bg. color 3
----+----+---+---+---+---+---+---+---+---+--------------

In basic v2 the color registers must be modified explicitly:

20 poke 53281,6:poke 53282,7:poke 53283,8:poke 53284,9
Basic v3.5 lets you change backgrounds 0 and 1 with the color statement, but you need to modify backgrounds 2 and 3 through the registers:

20 color 0,2:color 3,8:poke 65303,88:poke 65304,57
Basic v7 lets you change only background 0 with the color statement:

20 color 0,12:poke 53282,7:poke 53283,8:poke 53284,9

The background color is selected among the four available 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 / $FF15 onwards.

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, Commodore 128 version:

10 poke 53265,91
20 color 0,12: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
Output:

C128

Commodore Plus/4 and 16 version:

10 poke 65286,91
20 color 0,2:color 3,8:poke 65303,88:poke 65304,57
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
Output:

Plus/4 - C16

Commodore 64 version:

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

C64