The 128's CHAR Statement
Jim Butterfield, Associate Editor
In this article, associate editor Jim Butterfield examines the highly versatile but quirky CHAR statement in the Commodore 128's BASIC 7.0.
The CHAR statement of BASIC 7.0 can display characters on any type of screen—in 40 or 80 columns, text or high resolution. Whatever you have, CHAR will deliver the message. But the statement has its own special quirks, and at least one bug. This article explores CHAR in detail.
The format of the CHAR statement is described in the Commodore 128 System Guide as follows:
CHAR color source, x, y, string, rvs
The parameters shown in italics are optional. As a more easily understood example, this statement prints the word HELLO in column 9 of row 4:
CHAR , 9, 4, "HELLO"
We'll describe all of CHAR's parameters later in this article.
Terminology
It's customary to call the high-resolution screen a graphics screen, and call the normal display screen a text screen. This can be confusing, since the 128's character set contains both text (letters, numerals, and so on) and what you might call graphics—special symbols such as hearts or diamonds. To prevent confusion, we'll use the term bitmapped to describe the high-resolution screen and character to designate the conventional text screen.
Supplying Values
CHAR always requires that you specify the column and row where printing will occur. Remember that the rows and columns are numbered starting at 0 rather than 1. Thus, if you want to print at what you might ordinarily consider column 10, row 5, use column and row values of 9 and 4.
On the 40-column screen, the row value must not be greater than 24 and the column value must not exceed 39. This is true for both character and bitmapped modes. On the 80-column screen, the column and row values must fit within the currently defined output window. If you haven't set a window, the default output window occupies the whole screen. When you're working in 80 columns, a GRAPHIC statement switches CHAR's output over to the bitmapped screen. In this case, GRAPHIC 5 (return to 80 columns) alone won't bring it back; you must cancel the bitmapped screen with GRAPHIC 0:GRAPHIC 5 (or with GRAPHIC CLR, which eliminates the bitmapped screen altogether).
Partly because of this confusion, and partly because of a bug described later, I don't like to use CHAR on the 80-column screen. I'd much rather PRINT cursor-movement characters followed by the text I want to appear.
Color Source, Foreground, And Background
The first parameter in a CHAR statement is the color source. This value is optional. If you're in character mode (not high resolution), the number is ignored. If you are in bitmapped mode, a value of 0 selects the background color for printing, and a value of 1 selects the foreground color. Most programmers omit this parameter, which defaults to the foreground value (1).
It's hard to explain in words what it means to print in background and foreground colors. We'll do it the clearest way, with an example. From the 40-column screen, type this statement and press RETURN:
GRAPHIC 2
You'll end up with a lot of clutter on the upper part of the screen, but that's intentional in this case (it could be prevented with GRAPHIC 2,1). Now change the color scheme by typing these statements:
COLOR 0, 5 COLOR 1, 3 FOR J = 7168 TO 7407 : POKE J, 48 : NEXT J
(If you make a typing mistake, the 128 will drop out of bitmapped mode to display the appropriate error message. Entering another GRAPHIC 2 statement will get you back.)
The two COLOR statements set distinctive colors that you'll recognize when they crop up in unexpected places. Note that the COLOR 0,5 statement (the 0 specifies the background color source) sets the background to purple only on the character screen. The bitmapped screen is not affected at all. The COLOR 1,3 statement sets the foreground color to red. However, nothing appears to happen when we issue this statement or continue to type. It's odd that background affects only the character screen, and foreground, as it turns out, applies only to the bitmapped screen.
The FOR-NEXT loop sets the foreground and background colors on the first six rows of the bitmapped screen. I've chosen the colors cyan and black, and you'll see these colors sweep through the screen clutter as the loop executes. When we POKE colors, we use the color code 3 for cyan and the color code 0 for black. The value to POKE is computed as 3 * 16 + 0. If we were using a BASIC 7.0 statement such as COLOR, the corresponding color codes we'd use would be increased by 1: 4 for cyan and 1 for black.
Now we're ready to see exactly how the color code value works. Enter these lines:
CHAR 1, 2, 2, "TESTING" CHAR 0, 3, 3, "MORE TESTS"
The screen colors were originally cyan on black. The first CHAR statement prints in a new foreground color (red) without changing the black background. The second CHAR statement prints in the original foreground color (cyan), but changes the background to the same color as the character screen background (purple). To change both, print twice with a different color source each time. Here's an example using a loop:
FOR J = 0 TO 1 : CHAR J, 4, 4, "HERE ARE BOTH COLORS" : NEXT J
The rest of this article will skip the first parameter, assuming that the default value (1) will do the appropriate job.
Reverse Flag
If the reverse flag (rvs in the statement format shown above) is set to 0 or is omitted, the characters will print in the usual way—foreground color on background color. Setting the reverse flag to 1 (as in CHAR,9,9,"GREETINGS",1) doesn't exactly reverse the color usage. Instead, it prints reversed characters. This is a fine point, and it may not make a difference in your case. But if you examine the various color combinations outlined in the previous section, you'll see that there is a difference.
You might like to repeat the above exercises, using an extra, 1 at the end of each CHAR statement. You'll find that you get new combinations: black letters against a red background, and purple letters against a cyan background.
Text Versus Graphics Mode
When you're in character mode, the rules are pretty simple. Whatever print mode the computer is in—lowercase/uppercase or uppercase/graphics—will be the mode used for the output of CHAR.
You can change modes during a CHAR display. Enter NEW and GRAPHIC CLR and then try this:
CHAR , 20, 20, CHR$ (142) + "HELLO" + CHR$(14)+ "THERE"
For the 40-column screen, the entire screen makes the switch from uppercase/graphics to lowercase/ uppercase mode. The 80-column screen can have both character sets displayed at the same time.
CHAR for the bitmapped screen seems at first to support only the graphics character set. But it can easily be made to support both. Try this:
GRAPHIC 2, 1 CHAR , 10, 10, CHR$(142) + "LOOK" + CHR$(14) + "HERE"
You may go back and forth between character sets, using CHR$(14) and CHR$(142), as often as you like. But you can't use programmed cursor movements or other control characters; the two just named are the only ones supported for CHAR usage on bitmapped screens. There is, however, an extra gimmick you can use on bitmapped screens. We'll talk about it, and give a program example, in a moment.
The Bug
When you use the CHAR statement on an 80-column screen, an error in the 128's logic causes two locations high in bank 0 to be changed. Bank 0 is where BASIC program text is kept. Your program won't be hurt unless it's very big (at least 38K in size)—but CHAR will corrupt a program that reaches the danger spot.
The problem is easy to get around. Just don't use the CHAR statement to print messages to the 80-column screen.
At the time of writing, Commodore is preparing a revised operating ROM for the Commodore 128. This new ROM may eliminate the bug. To test whether the bug exists on your machine, enter and run the following short program:
200 GRAPHIC CLR 210 BANK 0 220 POKE 54784, 0 230 POKE 54785, 0 240 CHAR , 1, 1, "NOW TRY THIS"
If you have only a 40-column monitor, add the following two lines:
100 PRINT CHR$(27); "X" 300 PRINT CHR$(27); "X"
Do not add these lines if you are working in 80 columns. After you've run the program, check the contents of the two locations you POKEd to 0. You should still be set for bank 0, so just type:
PRINT PEEK(54784) PRINT PEEK(54785)
If the values have changed from 0, your 128's ROMs have the bug. In the case of a very long BASIC program, those two locations might contain part of a line, in which case the 80-column version of the CHAR statement would corrupt two bytes of your program. Stay away.
Special Feature
When the 128 is in bitmapped graphics mode, there's an extra gimmick that CHAR can use. The character set can be switched to one that you create yourself. Normal printing to the screen won't be affected; only those characters produced by the CHAR command. Location 4588 ($11EC) controls this feature.
The address of the character set used by CHAR is kept in location 4588, which normally holds the value 208. That's a page number; multiply it by 256 and you get the address of the 128's character ROM, starting at location 53248 in the bank 14 configuration.
To create a custom character set, you don't have to design all the characters of the alphabet, plus numbers, punctuation, and so on. In the example given below, we define only three characters: one to replace the letter A, and the other two for B and C.
We'll replace these three letters with pictures of little people. They must fit into the space a single character occupies, so they will be quite small. If you want bigger pictures, it's not hard to hook together two or more characters so that they jointly depict some object.
As we switch from one character to another, using the CHAR command, the figures will be displayed in slightly different postures. They will look as though they are convulsing.
The procedure is simple. First, lines 110-160 POKE the new character descriptions into memory. The new character set starts at location 2816, but we skip character 0 (which would occupy the eight bytes from 2816 to 2823) and start with character 1, the letter A. After we've defined the new A, B, and C, we do some initial printing to the graphics screen (lines 170-200), and then switch the character set at line 210. The POKE to location 4588 does this job.
Lines 220–280 print the convulsing figures. You can change the rate at which the figure changes by pressing one of the number keys. Pressing any nonnumeric key causes the program to exit from this loop. The original character set is restored in line 290.
If you want to use this feature in your own programs, remember that the computer expects to find the custom character set in bank 14. That means you will usually want to set it up at an address below 16384, since bank 14 can't see RAM above that address.
EQ 110 FOR J = 2824 TO 2847 SG 120 READ X$ : POKE J, DEC(X$) QS 130 NEXT J MX 140 DATA 18, 18, FF, 18, 3C, 3C, 66, 81 MK 150 DATA 98, D8, 7F, 18, 3C, 3D, 67, 80 JH 160 DATA 19, 1B, FE, 18, 3C, BC, E6, 01 XA 170 S = 100 : GRAPHIC 1, 1 MB 180 CHAR , 8, 3, "A CHORUS LINE" JS 190 CHAR , 7, 7, "(PRESS ANY KEY)" BG 200 CHAR , 5, 9, CHR$(14) + "(KEYS 1 - 9 FOR SPEED)" BM 210 POKE 4588, 11 FP 220 DATA AAAAA, BBBBB, AAAAA, CCCCC DH 230 IF F$ = "CCCCC" THEN RESTORE 220 MK 240 READ F$ JB 250 CHAR 1, 12, 5, F$ AP 260 FOR J = 1 TO S : NEXT J JP 270 GET X$ : IF X$ = "" GOTO 230 AJ 280 IF X$ > = "0" AND X$ < = "9" {SPACE} THEN S = (10 - VAL(X$)) * 25 : GOTO 230 GE 290 POKE 4588, 208 XR 300 GRAPHIC CLR