Commodore 64 BASIC Programming Series
Part 1: Introduction to Commodore BASIC
Part 1.5: Installing CBM Prg Studio on Mac/Linux
Welcome back, this is Part 4 of the Commodore 64 BASIC programming tutorial series.
In this lesson we are going to look at the magic of pokes – more specifically, how using poke and peek you can get the most out of the unique abilities of the commodore 64 rather than stick to generic BASIC commands that are common across most machines of the era.
This is not to knock the excellent type in programs from Usborne and other publishers like them – they did a remarkable job creating basic games that were fun but also worked on a multitude of systems!
The problem they had is they often had to limit their games to what was possible on the lowest common denominator, something that is not a constraint for us.
Sorry the video is so long, if I had more time I would have made it shorter!
Add Your Email, Get the Files

Never miss an update plus get these bonus downloads:
- Commodore 64 .D64 Disk
- Presentation slides
- Handy POKEs/PEEKs Cheat Sheet
- Code Listings
What are POKE and PEEK?
POKE allows you to put things INTO memory. PEEK allows you to examine the contents of memory locations.
You can POKE 1024, 1 to put the letter A at the top, left corner of the screen, because the screen memory starts at memory location 1024. Unfortunately you can only put a byte at a time, so for example, you could not update the entire screen all at once.
On the other hand you can, for example PRINT PEEK(214) to output the current cursor row number.
Again, you can only get one byte of information back.
To access results larger than 255 you will have to PEEK two different locations called the “high and low bytes” which together can generate a number up to 65,536.
Changing Screen, Border and Text Colours
To demonstrate POKE and PEEK we can set the screen colours. While you can set the text colour using the C64 keyboard, the Commodore 64 doesn’t have any built-in colour commands in BASIC.
We can set the border and background to any colour between 0 and 15:
POKE 53280,15 - Border color.
POKE 53281,15 - Background color.
This BASIC program will show every colour combination:
10 rem colour poke all the colours demo
15 b=53280 : p=53281 : v=53265 : t=646 : r=128
20 print "{clear}"
30 c = c+1: if c>15 then c=0
35 forw=1to15:waitv,r:nextw
40 poke b,c : poke p, peek (b)
50 poke t, c
60 print " poke all the colours ";
70 goto 30
Note the trick in line 35 – it tells the C64 to wait for the video update to reach scan line 128 to prevent screen flicker.
Better Cursor Positioning
If you check out the list of handy POKEs and PEEKs in the downloads you will see you can also POKE to put characters on the display, and also see which character is at a certain location:
10 rem cursor position text using poke
20 sys 65517
30 print "this display has "peek(781)"columns"chr$(13)"press a key"
40 get a$ : if a$= "" then 40
50 gosub 100
60 end
100 rem plot the cursor with poke
110 row = 10 : column = 10
120 print "{clear}"
130 gosub 1000
140 print "this should be at 10,10"
150 row = 8 : column = 8
160 gosub 1000
170 print "this is at 8,8{down}{down}{down}{down}{down}{down}{down}"chr$(13)
180 return
1000 rem set the cursor
1010 poke 214, row
1020 poke 211, column
1030 sys 58732
1040 return
1050 rem end of plot routine
1060 rem ..................................
Again we are introduced to another BASIC command, SYS. This allows you to jump to a built-in machine code routine then go back to your program as if you had GOSUB to a BASIC subroutine.
Character Set Viewer

This program allows us to examine the built-in C64 Character Set. We read the data then check each bit, 8 bits on 8 lines.
To get from the decimal to determine the bits we use and 2^p
which translates to AND 2 to the power of P.

0 rem this program displays the pixels of the current characterset
10 dim bytes(7) : px$ = "{reverse on}{black}{207}" : sp$="{reverse on}{light blue}{207}"
20 print"{clear}{white}enter screen code: ";
22 rem
25 rem the following poke turns off the ? after input:
30 poke 19,32 : input c : print chr$(13)
32 rem
35 rem source address for character data
40 addr=53248+c*8
45 rem
48 rem turn off keyboard and IO temporarily
50 poke 56334,peek(56334)and254
60 poke1,peek(1)and251
65 rem
68 rem grab the character data for the chosen char (c)
70 for row = 0 to 7 : bytes(row) = peek ( addr+row) : next row
75 rem
78 rem turn keyboard and IO back on
80 poke1, peek(1) or 4
90 poke 56334, peek(56334) or 1
92 rem
94 rem now lets print out the data
95 print : print tab(15);
98 rem
99 rem 8 rows of 8 pixel bits ...
100 for row = 0 to 7 : for p = 7 to 0 step-1
105 rem
108 rem if row and 2 to the power of pixel is a 1
110 if (bytes(row) and 2^p) then print px$; : goto 130
120 print sp$;
130 next p : print : print tab(15); : next row
140 print chr$(13)tab(7)"{reverse on}{light blue} press a key to continue {reverse off}{light blue}"
150 poke 198,0
160 wait 198,1
170 get a$
180 goto20
Building a Custom Character Set
Even though we are presenting our custom characters in binary, we need to provide the data in decimal. To do this we convert each row of our custom character like so:
1 2 8 | 6 4 | 3 2 | 1 6 | 8 | 4 | 2 | 1 |
---|---|---|---|---|---|---|---|
0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 |
0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
0 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
0 | 1 | 1 | 0 | 0 | 1 | 1 | 0 |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
The first row has pixels in 16 and 8, so that would give a decimal number of 24.
Our last row is blank with 8×0, so that would be 0 in decimal.
0 gosub 10010
5 rem this program loads an entire font and customises it
10 print "{clear}{white}custom font demo{light blue}"
20 rem
30 rem :-------------------------------------------------------
40 rem : first section copies default c64 characterset from rom
50 rem :-------------------------------------------------------
100 rem turn off keyboard
110 poke 56334,peek(56334)and254
115 rem turn off i/o and screen
120 poke 1,peek(1)and251
122 rem goto 250
125 rem counter to increment character
130 for ch=0to255
135 rem if printable character:
140 if (ch > 32 and ch < 128) or (ch > 160 and ch < 191) then poke1064,ch
145 rem copy 8 bytes each
150 for byte=0to7
160 rem copy current byte
170 poke 12288+ch*8+byte,peek(53248+ch*8+byte)
180 rem get next byte from rom
190 next byte
200 rem get next character
210 next ch
220 goto 260
240 rem alternate way to copy the data all in one line
250 for i = 0 to 4095: poke 12288 + i, peek (53248+i) :nexti
260 rem turn i/o and keyboard back on
270 poke 1,peek(1)or4:poke 56334,peek(56334)or1
280 rem
1000 rem
1010 rem :-------------------------------------------------------
1020 rem : this is where we redefine our custom characters
1030 rem : each character has a data row consisting of 8 bytes
1040 rem :-------------------------------------------------------
1050 rem
1070 rem set characterset pointer to address 12288
1080 poke 53272,(peek(53272)and240)+12
1090 rem
1100 rem first we do our player character, char 0
1110 ch = 0
1120 for byte = 0 to 7
1130 read cd
1140 poke 12288+byte,cd
1150 next byte
1500 rem
1510 rem iterate over characters 60-64
1520 for ch=60to64:
1525 if ch=63 then next
1530 for byte=0to7: rem 8 bytes per chacter
1540 rem
1550 rem read next byte of character data (cd)
1560 rem then save the data to the character memory
1570 read cd
1580 poke 12288+(8*ch)+byte,cd
1590 rem
1600 rem iterate to next byte
1610 next byte
1620 rem do next character
1630 next ch
1640 rem
2000 rem
2010 rem :-------------------------------------------------------
2020 rem : now we have our custom characters re-defined
2030 rem : we get to use them as if they were the standard ones
2040 rem :-------------------------------------------------------
2060 rem
2070 rem
2080 dim row$( 12)
2085 row$(0) = " "
2090 row$(1) = " {dark gray}======="
2100 row$(2) = " =========="
2110 row$(3) = " = <="
2120 row$(4) = " {brown}{192} {dark gray} {brown}{192}"
2130 row$(5) = " {dark gray}= ="
2140 row$(6) = " = {white}@ {dark gray}="
2150 row$(7) = " = ="
2160 row$(8) = " = = ="
2170 row$(9) = " = = ="
2180 row$(10) = " = > = ="
2190 row$(11) = " ========{brown}{192}{dark gray}="
2195 rem
2200 rem print the test map
2210 for r=0 to 11:?row$(r):next
3000 rem
3010 rem :-------------------------------------------------------
3020 rem : finally we need to put things back to normal
3030 rem : either that or the user will have to reboot
3040 rem :-------------------------------------------------------
3050 print "{cyan}{down}{down}{down}{down}{down}press a key to quit"
3060 rem wait for a keypress
3070 get a$: if a$=""then 3070
3080 print "{clear}{light blue}"
3090 poke 53272,21: rem restore default characterset
3100 rem
4000 rem
4010 rem :-------------------------------------------------------
4020 rem : this is where we store our character data
4030 rem : each character has a data row consisting of 8 bytes
4040 rem :-------------------------------------------------------
5000 data 092,087,233,089,057,030,020,054 : rem character 0
5010 data 000,126,000,060,000,024,000,000 : rem character 60
5020 data 239,138,012,000,254,170,128,000 : rem character 61
5030 data 000,000,024,000,060,000,126,000 : rem character 62
5040 data 000,060,126,254,122,126,254,126 : rem character 64
6000 rem
6010 rem :-------------------------------------------------------
6020 end
6030
10000 rem :
10010 rem :-------------------------------------------------------
10020 rem : set background colours
10030 poke 53280,0 : poke 53281, 0
10040 return
10050 rem
Updated Dungeon Crawl Game Code

As mentioned previously, over this series we will work on is a Dungeon Crawler game, where you explore a dungeon battling bad-guys and collecting keys and treasure.
In this update we can read the keyboard, move our guy around, prevent him from walking through walls, and we have custom graphics. It has started looking more like a game someone might want to play.
Now we can start actually making our game for real!
10 rem dungeon v2 - poke screen coords
20 rem ================================
30 rem
40 rem first initialise variables
50 col=20 : row=10 : a=0 : br=53281 : na=12288 : da=53248
60 rem set initial start position
70 gosub 4000
80 rem
100 rem ........................
110 rem now we need our c64 font
120 print "{clear}please wait"
140 gosub 7000
150 rem now show map
160 gosub 6000
1000 rem start of game loop
1010 ox=col : oy=row
1020 gosub 3000 : rem get keyboard input
1030 if row<>oy or col<>ox then print "{left} "
1040 gosub 4000 : rem set the char pos
1050 print "{light blue}@";
1060 poke 2021,3 : rem c
1070 poke 2022,54 : rem 6
1080 poke 2023,52 : rem 4
1090 goto 1000 : rem go back to loop
3000 rem ...........................
3010 rem get keyboard input
3020 get p$ : rem peek(197) also works
3030 rem print asc(p$) to work out keys
3040 if p$="o" then col=col-1 : rem <
3050 if p$="p" then col=col+1 : rem >
3060 if p$="q" then row=row-1 : rem ^
3070 if p$="a" then row=row+1 : rem v
3080 if col < 0 then col=0
3090 if col > 39 then col=39
3100 if row < 0 then row=0
3110 if row > 23 then row=23
3120 if peek(1024+(row*40)+col)=32 then return
3130 rem don't move if not to a space
3140 row=oy : col=ox
3150 return
4000 rem
4010 rem ...........................
4020 rem set cursor row and column
4030 x=row : y=col
4040 poke 780,a : rem set a register
4050 poke 781,x : rem set x reg (row)
4060 poke 782,y : rem set y reg (col)
4070 poke 783,0 : rem set status reg
4080 sys 65520 : rem set the cursor
4090 return
6000 rem
6010 rem ............................
6020 print "{clear}{home}{right}{right}{down}{down}{down}{cyan}press keys: q,a,o,p{white}"
6030 rem init vars, set initial position
6040 rem
6050 dim row$( 12)
6060 row$(0) = " "
6070 row$(1) = " {dark gray}======="
6080 row$(2) = " =========="
6090 row$(3) = " = <="
6100 row$(4) = " {brown}{192} {dark gray} {brown}{192}"
6110 row$(5) = " {dark gray}= ="
6120 row$(6) = " = {white}@ {dark gray}="
6130 row$(7) = " = ="
6140 row$(8) = " = = ="
6150 row$(9) = " = = ="
6160 row$(10) = " = > = ="
6170 row$(11) = " ========{brown}{192}{dark gray}="
6180 rem
6190 rem print the test map
6200 for r=0 to 11:?row$(r):next
6210 poke br,0 : poke br-1,0
6280 return
7000 rem
7020 rem ................................
7030 rem load customised chars
7040 rem ................................
7050 rem
7120 rem set characterset pointer to address 12288
7130 poke 53272,(peek(53272)and240)+12
7140 ch = 0 : gosub 8020
7150 ch = 32 : gosub 8020
7160 ch = 60 : gosub 8020
7180 ch = 61 : gosub 8020
7200 ch = 62 : gosub 8020
7220 ch = 64 : gosub 8020
7230 for ch = 1 to 26 : gosub 8020 : next
7240 for ch = 48 to 57 : gosub 8020 : next
7250 ch = 58 : gosub 8020
7260 ch = 44 : gosub 8020
7900 return
8000 rem ...............................
8010 rem load specific custom character
8020 rem ...............................
8030 for byte = 0 to 7
8040 read cd
8050 poke 12288+(8*ch)+byte,cd
8060 next byte
8070 return
9000 data 092,087,233,089,057,030,020,054 : rem character 0
9005 data 0,0,0,0,0,0,0,0 : rem space
9010 data 000,126,000,060,000,024,000,000 : rem character 60
9020 data 239,138,012,000,254,170,128,000 : rem character 61
9030 data 000,000,024,000,060,000,126,000 : rem character 62
9040 data 000,060,126,254,122,126,254,126 : rem character 64
9060 data 016,040,040,068,124,068,238,000 : rem character 1
9070 data 252,066,066,124,066,066,252,000 : rem character 2
9080 data 056,068,130,128,130,068,056,000 : rem character 3
9090 data 252,066,066,066,066,066,252,000 : rem character 4
9100 data 254,066,072,120,072,066,254,000 : rem character 5
9110 data 254,066,072,120,072,064,224,000 : rem character 6
9120 data 060,066,128,142,130,066,060,000 : rem character 7
9130 data 238,068,124,068,068,068,238,000 : rem character 8
9140 data 254,016,016,016,016,016,254,000 : rem character 9
9150 data 124,016,016,016,144,144,096,000 : rem character 10
9160 data 238,068,072,112,072,068,238,000 : rem character 11
9170 data 224,064,064,064,066,066,254,000 : rem character 12
9180 data 238,084,084,084,068,068,238,000 : rem character 13
9190 data 238,100,084,084,076,076,228,000 : rem character 14
9200 data 056,068,130,130,130,068,056,000 : rem character 15
9210 data 252,066,066,066,124,064,224,000 : rem character 16
9220 data 056,068,130,130,146,076,058,000 : rem character 17
9230 data 252,066,066,066,124,072,238,000 : rem character 18
9240 data 124,130,128,124,002,130,124,000 : rem character 19
9250 data 254,146,016,016,016,016,056,000 : rem character 20
9260 data 238,068,068,068,068,068,056,000 : rem character 21
9270 data 238,068,068,040,040,040,016,000 : rem character 22
9280 data 238,068,068,084,084,108,068,000 : rem character 23
9290 data 238,068,040,016,040,068,238,000 : rem character 24
9300 data 238,068,040,040,016,016,056,000 : rem character 25
9310 data 254,132,008,016,032,066,254,000 : rem character 26
9320 DATA 056,068,130,130,130,068,056,000 : REM CHARACTER 0
9330 DATA 048,080,144,016,016,016,254,000 : REM CHARACTER 1
9340 DATA 124,130,002,124,128,130,254,000 : REM CHARACTER 2
9350 DATA 254,132,008,028,130,130,124,000 : REM CHARACTER 3
9360 DATA 012,024,040,074,254,010,028,000 : REM CHARACTER 4
9370 DATA 254,130,252,002,130,130,124,000 : REM CHARACTER 5
9380 DATA 124,130,128,252,130,130,124,000 : REM CHARACTER 6
9390 DATA 254,132,008,008,016,016,056,000 : REM CHARACTER 7
9400 DATA 124,130,130,124,130,130,124,000 : REM CHARACTER 8
9410 DATA 124,130,130,130,124,004,120,000 : REM CHARACTER 9
9420 data 000,000,024,000,000,024,000,000 : rem character 58
9430 data 000,000,000,000,000,024,024,048 : rem character 44
We will start building out the game next time
Before you go, remember to get the code and bonus downloads!