Double-Width C64 Style Font on the Commodore VIC-20

The C64 used a fatter double-width font because the video quality of early VIC-IIs wasn't very good. Therefore they had to make the C64's font fatter than the VIC-20's. However, this fatter font look quite nice and there's an easy way of replicating something similar on the VIC-20.

Rather than loading in a whole new font we can double the width of the existing font stored in ROM to create a new fatter font in RAM, which we can then point to.

Standard Character Definitions

Each character definition is stored in ROM and for this demonstration we'll base our new fatter font on the uppercase characters stored from location 32768 ($8000). Because this example is for an unexpanded VIC-20 we'll only use the first 64 characters.

The Vic uses 8x8 fonts as default and each character definition is stored as a series of 8 bytes with each byte representing a row of the definition.

Fat Double-width Character Definition

To create the double-width font we get the value of each line of the original font, double it and then OR it with the original value. This has the effect of doubling each pixel's width in the character definition.

This looks like the following where a equals the original value of the row in the character definition and b is the new value.

b := a or (a*2)

All we need to do then is go through each row of each character definition and store the results in RAM. Once we have stored them in RAM, we use location 36869 ($9005) to set the character map address to our location in RAM.

A Short Program to Create a Fat Font

The following program will use the first 64 characters of the uppercase character set to create a double-width font. Because we're only altering the first 64 characters it means that the reverse characters aren't defined as usual so the cursor won't blink properly nor will the characters above 63 be defined properly.


First we'll create a little routine in assembly language to create the double-width font in RAM from location 7168 ($1C00). This location is being used because we are creating a custom character set with 64 entries and 7680 (Start of screen memory) - 512 (8*64 characters) = 7168.

            ldy  #$00
loop        lda  $8000,y       ; Get row of character from ROM
            asl                ; Double the value for the row
            ora  $8000,y
            sta  $1C00,y       ; Store row of character in RAM
            lda  $8100,y       ; Get row of character from ROM
            asl                ; Double the value for the row
            ora  $8100,y
            sta  $1D00,y       ; Store row of character in RAM
            bne  loop

Thanks to C.S. Bruce for his suggestions to make this more succinct.

Machine Code

Next we assemble the routine to machine code using location $02A1-$02BA to store it in.

02A1: A0 00 B9 00 80
02A6: 0A 19 00 80 99
02AB: 00 1C B9 00 81
02B0: 0A 19 00 81 99
02B5: 00 1D C8 D0 E9
02BA: 60


Finally we create a short program to load the machine code routine into memory, execute it and point to the new character map.

10 poke 52,28:poke 56,28:clr
20 for i=673to698:read a:poke i,a:next i
30 sys 673
40 poke 36869,255
50 data 160,0,185,0,128,10,25,0,128,153
60 data 0,28,185,0,129,10,25,0,129,153
70 data 0,29,200,208,233,96

It is worth highlighting a few points in the code:

  • Location 51/52 points to the bottom of BASIC active strings and hence the top of available free space. On an unexpanded system it defaults to: 7680 ($1E00), by poking 28 ($1C) into 52 this lowers it to 7168 ($1C00).
  • Location 55/56 points to the end of BASIC memory. On an unexpanded system it defaults to: 7680 ($1E00), by poking 28 ($1C) into 56 this lowers it to 7168 ($1C00).
  • The command clr, clears program variables and registers the bottom of BASIC active strings and end of BASIC memory.
  • Location 673 ($02A1) is the location we assembled the machine code to. There is an area of memory from 673-767 ($02A1-$02FF) which is used for user indirect vectors or other storage and hence is a good location to put our machine code routine.
  • Location 36869 ($9005) controls the location of the character set. We set it to 255 as this sets the location to 7168 ($1C00), just below the screen map at 7680 ($1E00). To see more about setting the character map location look at: Mapping the Vic, p.129-133.

Video Demonstrating Fat Font

The double-width fat font can be seen in the following video.

Creative Commons License
Double-Width C64 Style Font on the Commodore VIC-20 by Lawrence Woodman is licensed under a Creative Commons Attribution 4.0 International License.

Share This Post


Related Articles

Getting the Address of BASIC Variables on the VIC-20

Getting the address of a BASIC variable can be useful if you want to pass data to a machine code routine or want to access the bytes of a variable directly to improve speed and reduce garbage collectio...   Read More

Saving and Loading Memory on the VIC-20

Saving and loading memory is quite easy on the VIC-20 once you know how. However, it isn't obvious how to do this and therefore this article will present a few simple ways of doing it from BASIC and A...   Read More

Programming in Assembly with VICMON on the VIC-20

VICMON is a machine language monitor released by Commodore in 1982 and is great for programming the VIC-20. Its interactive nature means that it can often be quicker to develop via this rather than us...   Read More

Storing Machine Code in REM Statements on the VIC-20

BASIC programs often contain machine code routines but they take up quite a lot of space in BASIC. An interesting way to reduce the amount of space that they take is to store the machine code in REM s...   Read More

Code and Data in Display Memory on the VIC-20

The unexpanded Commodore VIC-20 only had 5K of RAM and therefore creative ways had to be found to maximize the available RAM. The display memory would use some of this memory and therefore one option ...   Read More