The text screen of the Commodore 64 has a resolution of 40 by 25 characters, based on the hardware text mode of the VIC-II video chip. This is a step up from the VIC-20’s 22 characters per line, but since computers in the professional segment (Commodore PET 8000 series, CP/M, MS-DOS) usually had 80 columns, several solutions – both hardware and software – exist to allow 80 columns on a C64 as well. Let’s look at how this is done in software! At the end of this article, I present a fast and full-featured open source implementation with several different character sets.
Regular 40×25 Mode
First, we need to understand how 40 columns are done. The VIC-II video chip has dedicated support for a text mode. There is a 1000 (= 40 * 25) byte “Screen RAM”, each byte of which contains the character to be displayed at that location in the form of an index into the 2 KB character set, which contains 8 bytes (for 8×8 pixels) for each of the 256 characters. In addition, there is a “Color RAM”, which contains a 1000 (again 40 * 25) 4-bit values, which represents a color for each character on the screen.
Putting a character onto the screen is quite trivial: Just write the index of it to offset column + 40 * line into Screen RAM, and its color to the same offset in Color RAM. An application can load its own character set, but the Commodore 64 already comes with two character sets in ROM: One with uppercase characters and lots of graphical symbols (“GRAPHICS”), and one with upper and lower case (“TEXT”). You can switch these by pressing the Commodore and the Shift key at the same time.
There is no hardware support for an 80 column mode, but such a mode can be implemented in software by using bitmap mode. In bitmap mode, all 320 by 200 pixels on the screen can be freely addressed through an 8000 byte bitmap, which contains one bit for every pixel on the screen. Luckily for us, the layout of the bitmap in memory is not linear, but reminds of the encoding of text mode: The first 8 bytes in Bitmap RAM don’t describe, as you would expect, the leftmost 64 pixels on the first line. Instead, they describe the top left 8×8 block. The next 8 bytes describe the 8×8 block to the right of it, and so on.
This is the same layout as the character set’s: The first 8 bytes correspond to the first character, the next 8 bytes to the second character and so on. Drawing an 8×8 character onto a bitmap (aligned to the 8×8 grid) is as easy as copying 8 consecutive bytes.
This is what an 8×8 font looks like in memory:
0000 ··████·· 0008 ···██··· 0010 ·█████·· 0001 ·██··██· 0009 ··████·· 0011 ·██··██· 0002 ·██·███· 000a ·██··██· 0012 ·██··██· 0003 ·██·███· 000b ·██████· 0013 ·█████·· 0004 ·██····· 000c ·██··██· 0014 ·██··██· 0005 ·██···█· 000d ·██··██· 0015 ·██··██· 0006 ··████·· 000e ·██··██· 0016 ·█████·· 0007 ········ 000f ········ 0017 ········
For an 80 column screen, every character is 4×8 pixels. So we could describe the character set like this:
0000 ········ 0008 ········ 0010 ········ 0001 ··█····· 0009 ··█····· 0011 ·██····· 0002 ·█·█···· 000a ·█·█···· 0012 ·█·█···· 0003 ·███···· 000b ·███···· 0013 ·██····· 0004 ·███···· 000c ·█·█···· 0014 ·█·█···· 0005 ·█······ 000d ·█·█···· 0015 ·█·█···· 0006 ··██···· 000e ·█·█···· 0016 ·██····· 0007 ········ 000f ········ 0017 ········
Every 4×8 character on the screen is either in the left half or the right half of an 8×8 block, so drawing an 4×8 character is as easy as copying the bit pattern into the 8×8 block – and shifting it 4 bits to the right for characters at odd positions.
In bitmap mode, it is only possible to use two out of the 16 colors per 8×8 block, because there are only 1000 (40 * 25) entries for the color matrix. This is a problem, since we need three colors per 8×8: Two for the two characters and one for the background. We will have to compromise: Whenever a character gets drawn into an 8×8 block and the other character in the block has a different color, that other character will be changed to the same color as the new character.
Scrolling on a text screen is easy: 960 bytes of memory have to be copied to move the character indexes to their new location. In bitmap mode, 7680 bytes have to be copied – 8 times more. Even with the most optimized implementation (73ms, about 3.5 frames), scrolling will be slower, and tearing artifacts are unavoidable.
Creating a 4×8 character set that is both readable and looks good is not easy. There has to be a one-pixel gap between characters, so characters can effectively only be 3 pixels wide. For characters like “M” and “N”, this is a challenge.
These are the character sets of four different software solutions for 80 columns:
COLOR 80 by Richvale Telecommunications
SCREEN-80 by Compute’s Gazette
Highspeed80 by CKtwo
- Highspeed80 and SCREEN-80 have capitals of a height of 7 pixels (more detail, but very tight vertical spacing, making it hard to read), while COLOR 80 uses only 5 pixels (more square like the original 8×8 font, but less detail). 6 pixels, as used by 80COLUMNS, seems like a good compromise.
- 80COLUMNS has the empty column at the left, which makes single characters in reverse mode more readable, since most characters have their stems at the left.
- Except for Highspeed80, the graphical symbols are very similar between the different character sets.
- All four character sets use the same strategy to distinguish between “M” and “N”.
The “EDITOR” is the part of C64’s ROM operating system (“KERNAL”) that handles printing characters to the screen (and interpreting control characters), as well as converting on-screen contents back into a PETSCII string – yes, text input on CBM computers is done by feeding the keyboard input directly into character output, and reading back the screen contents when the user presses the return key. This way, the user can use the cursor keys to navigate to existing text anywhere on the screen (even to the output of previous commands), edit it, and have the system interpret it as input when pressing return.
The C64’s EDITOR can only deal with 40 columns (but has a very nice feature that allows using two 40 character lines as one virtual 80 character input line), and has no idea how to draw into a bitmap, so a software 80 characters solution basically has to provide a complete reimplementation of this KERNAL component.
The KERNAL provides user vectors for lots of its functionality, so both character output, and reading back characters from the screen can be hooked (vectors IBSOUT at $0326 and IBASIN at $0324). In addition to drawing characters into the bitmap, the character codes have to be cached in a 80×25 virtual Screen RAM, so the input routine can read them back.
The PETSCII code contains control codes for changing the current color, moving the cursor, clearing the screen, turning reverse on and off, and switching between the “GRAPHICS” and “TEXT” character sets. The new editor has provide code to interpret these. There are two special cases though: When in quote mode (the user is typing text between quotes) or insert mode (the user has typed shift+delete), most special characters show up as inverted graphical characters instead of being interpreted. This way, control characters can be included e.g. in strings in BASIC programs.
There are two functions though that cannot be intercepted through vectors: Applications (and BASIC programs) change the screen background color by writing the color’s value to $d020, since there is no KERNAL function or BASIC command for it, and the KERNAL itself switches between the two character sets (when the user presses the Commodore and the Shift key at the same time) by directly writing to $d018. The only way to intercept these is by hooking the timer interrupt vector and detecting a change in these VIC-II registers. If the background color has changed, the whole 1000 byte color matrix for bitmap mode has to be updated, and if the character set has changed, the whole screen has to be redrawn.
I looked at all existing software implementations I could find and concluded that “80COLUMNS” (by an unknown author) had the best design and was the only one to implement the full feature set of the original EDITOR. I reverse-engineered it into structured, easy to read code, added Ilker Ficicilar’s fast scrolling patch as well as my own minor cleanups, fixes and optimizations.
The project requies cc65 and exomizer to build. Running make will produce 80columns-compressed.prg, which is about 2.2 KB in size and can be started using LOAD/RUN.
The source contains several character sets (charset.s, charset2.s etc.) from different 80 column software solutions, which can be selected by changing the reference to the filename in the Makefile.
The object code resides at $c800-$cfff. The two character sets are located at $d000-$d7ff. The virtual 80×25 Screen RAM (in order to read back screen contents) is at $c000-$c7ff. The bitmap is at $e000-$ff40, and the color matrix for bitmap mode is at $d800-$dbe8. All this lies beyond the top of BASIC RAM, so BASIC continues to have 38911 bytes free.
In order to speed up drawing, the character set contains all characters duplicated like this:
0000 ········ 0008 ········ 0010 ········ 0001 ··█···█· 0009 ··█···█· 0011 ·██··██· 0002 ·█·█·█·█ 000a ·█·█·█·█ 0012 ·█·█·█·█ 0003 ·███·███ 000b ·███·███ 0013 ·██··██· 0004 ·███·███ 000c ·█·█·█·█ 0014 ·█·█·█·█ 0005 ·█···█·· 000d ·█·█·█·█ 0015 ·█·█·█·█ 0006 ··██··██ 000e ·█·█·█·█ 0016 ·██··██· 0007 ········ 000f ········ 0017 ········
This way, the drawing code only has to mask the value instead of shifting it. In addition, parts of character drawing and all of screen scrolling are using unrolled loops for performance.
Contributions to the project are very welcome. It would be especially interesting to add new character sets, both existing 4×8 fonts from other projects (including hinted TrueType fonts!), and new ones that combine the respective strengths of the existing ones.
Reducing the size of characters to 4×6 would allow a text mode resolution of 80×33 characters. Novaterm 10 has an implementation. At this resolution, logical characters don’t end at vertical 8×8 boundaries any more, making color impossible, and the drawing routine a little slower. It would be interesting to add an 80×33 mode as a compile time option to “80columns”.
10 thoughts on “80 Columns Text on the Commodore 64”
It occurred to me that it would be interesting to use the REU’s DMA function to implement scrolling; it might speed the process up by a factor of 8 or so, at the price of freezing the CPU completely while it’s happening.
I took a stab at this (using x64 -reu) but I’m not that familiar with REU programming and it never seemed to actually initiate a DMA transfer, so I’ve set that idea to the side again.
I used to implement such a 80 cols feature for using the C-64 as a terminal for the 680×0 based Gepard computer. We sold one unit of it – most customers could afford an Apple ][ with a 80 cols card for that. The 3×7 chars were hard to read, especially if you used a lousy TV as your monitor.
Also, about scrolling – wasn’t it possible to scroll faster by using a circular screen buffer, moving the start pointer ahead? Or was that only possible with other systems?
Really nice, especially part about integration with BASIC editor. Here’s an example of 6×8 characters on side borders: http://csdb.dk/release/?id=152470
Very nice and it works out of the box!
Remember you need the compressed image to run easily via vice emulator. The command to push the program in a disk image is something like
c1541.exe -attach ./test.d64 -write 80columns-compressed.prg 80columns
It would be nice to increase the basic size by 1kB to 39935 BASIC BYTES FREE by moving the start of basic down 1kB to $0401. But doing so breaks loading the exomizer-compressed CHARSETx files, because they are hard coded to $0801 as the start of BASIC. Is there some way around this, without forcing use of a forked exomizer?
There’s also “Flash-80 Demo” by “Powdered Toast Man” that neatly sidesteps the usage of bitmap mode by using two character sets and two sets of screen RAM, flipping between them every frame. I presume color handling wouldn’t be quite as nice with this concept (as it would still require rewriting color RAM every frame, unlike merely pointing the VIC-II at different screen RAM for odd and even columns). Of course, the downside to this is that the screen will flicker at 50/60Hz, unlike the bitmap-based software modes.
I attempted this 1988 with a new idea; 4 pixel characters for w or m. You never see two W’s in a row, and since we read more by length and first/last letter, it doesn’t affect readability. Time-interlacing text mode is a great idea, but 3 colors per cell can be done with wide multi color sprites in the background, with characters in reverse. As for scrolling, there’s two tricks for that; not scrolling and wrapping around to the top, or using VIC tricks to change memory pointers. I’m not sure if I fully worked this out yet.
Lol Repose. Somebody thinking.
I’ve wondered about how people would do this year’s back. Lately, I wondered about two things that might help (apart from sprites). One, using interlace and the second screen using the shifted position. It would not line up with the correct position, but there would be a recognisable difference. The second thing I was wondering, was there any possible half pixel screen shift in scrolling? Which would enable the two screens to have each 320 unique shifted positions per line, helping it be readable.
Commodore really should have put in a few more features like two colours per character position, 80 column mode (even if only text using a nibble per character, and if it used a 256 tile based character bit map). That would have made the early version of the chipset suitable for business. Adding a 256 colour pallet. Adding two bytes for 80 column and interlacing 50 line allows for 160*100 16 colour block graphics. The resulting 16KB of minium display memory allowing for 160*200 16 colour out of 256 color pallet, and 320*200 4 colour. When combined with 16 sprites , each with 1, 3 or 15 colour pallet (depending on resolution) and additive colour settings like on MSX sprites, it could have been quite a machine. 32KB of display memory, and 640 graphics mode and 16 sprites at that resolution, would have doubled the colour, and really put it in the zone for the future, with memory banking and faster 65xx, and 16 bit version, which could competed against a megadrive like machine, and be like a lower end ST. The high level limit of these 8 bit machines were 64k graphics (320*200 256 colour, 4 colour VGA, double on a future 16 bit bank) and general ram banks. But, we got the Plus 4, and then Commodore 128, instead, both were not ideal, though the 128 was nice, and with 80 column, useful as a business machine.
Unfortunately there is no half pixel scrolling.
It could had been possible without that much extra circuits in the VIC-II chip to add a 640 pixes wide hires mode, but that mode could only had used the color ram for colors, i.e. a fixed background color for all the screen and one fixed foreground color for each 16*8 block (the same size as a regular character), and that mode would also had had to use badlines for every visible line on screen. Combining that with software that writes characters to a 16000 bytes large hires bitmat screen would had been painfully slow and in practice more or less just a gimmick except for displaying some sort of diagrams.
The palette of 16 colors is due to the data bus for the VIC chip being 12 bits wide, 8 bits for the regular memory and 4 bits for the color ram. To have 256 colors for the regular character foreground would had required a 16 bit bus with an 8 bit wide color ram, which would had required a larger chip package and whatnot, which would had been too expensive. It might had been slightly more reasonable to have more colors for the colors that use colors registers within the VIC chip but that would probably also had cost too much and would aslo had resulted in a weird situation where you could use more colors for some things but in particular not as a foreground color for text.
Having a mode where sprites would combine with the regular screen to get additional colors would probably had been the easiest, but at the time I don’t think anyone at the time even had that idea. It would not had been cheap enough to just randomly implement.
Not sure about how MSX does but I think what you refer to is an MSX 2 feature, not present in the original TMS9918/9928/9929 VDP used in the TI 99/4A, Colecovision, Spectravideo SVI-318/328 and MSX-1.
Remember that the VIC-II (and SID) were in development for quite a while so some of the more basic desing decisions would had been made a while before the chips were ready for mass production.
Also compare with the Amiga chipset that were produced in 1985 which in order to save chip space only had 32 color registers although it had six bitplanes, and the sixth bitplane were used for extra half brite (lader addition missing in the earliest chips), the HAM mode and for double playfield. The bus were fast enough to be able to use 8 bit planes (as it could do 4 in the 640 pixels wide mode), so if it would had been cheaper to produce larger chips the original Amiga chipset could had had 256 colors, but the actual chip sizes would had been too large and thus too expensive to produce. And this is a few years later, which probably explains a bit about why the VIC-II only had 16 colors and whatnot.
In hindsight though it would had been great if the VIC-II would had been improved to handle a faster memory bus to be able to display twice the horizontal resolution, ie. 80 column mode and 640*200, instead going for what the C128 ended up with. That would also had made it possible for the C128 to have a PET compatibility mode with the same memory map as a PET (except the I/O chips, which afaik were rarely used by most software anyways as the I/O differed between different PET models). That in turn would sort of had created an extra last breath of life for existing PET software. However the C128 (together with for example the Amstrad CPC) were one of the last 8-bit computers and pouring more development resources in it and delaying it further would just had increased the cost and made it a less attractive computer. (It usually ends up in the shadow of the C64 but iirc it sold about 5 million units which is way more than some other well regarded computers of the time).