Written and Maintained by Gregory Nacu
 

NEWS, EDITORIALS, REFERENCE

Subscribe to C64OS.com with your favorite RSS Reader
January 19, 2018Programming Reference

Vic-20 / Commodore 64 SuperChart

This table appears in The Complete Commodore Inner Space Anthology, pages 37 and 38. The data are rearranged here to best suit the format of a webpage, and a couple of pretty big errors in the Inner Space Anthology listing have been corrected. Corrections are noted below each block.

It is called a SuperChart because it lists the PETSCII, ScreenCode (both character sets), BASIC character/token, and 6502 operation for every value in the 8-bit range. The numeric values are conveniently given in both decimal and hexadecimal. I'm digitizing it so I can have access to it without having the reference text open on the desk in front of me, and tagging it here as Programming Reference for others to find. Hopefully someone out there will find this chart as useful as I do.


PETSCII, ScreenCodes and BASIC codes are roughly divided into 8 blocks of 32 characters each. For this reason, the chart is presented below as eight 32-row tables. See the end discussion for an explanation of PETSCII's block structure.

SuperChart

Bit 7 Low: %0xxx xxxx

Bit 7 High: %1xxx xxxx

Block 1

DEC HEX PETSCII SCREEN
(up/gfx, lo/up)
BASIC 6502
0 00   @ end-line BRK
1 01   A a   ORA(I,X)
2 02   B b    
3 03 stop C c    
4 04   D d    
5 05 white E e   ORA Z
6 06   F f   ASL Z
7 07   G g    
8 08 lock H h   PHP
9 09 unlock I i   ORA #
10 0A   J j   ASL A
11 0B   K k    
12 0C   L l    
13 0D car ret M m   ORA
14 0E text N n   ASL
15 0F   O o    
16 10   P p   BPL
17 11 cur down Q q   ORA(I),Y
18 12 reverse R r    
19 13 cur home S s    
20 14 delete T t    
21 15   U u   ORA Z,X
22 16   V v   ASL Z,X
23 17   W w    
24 18   X x   CLC
25 19   Y y   ORA Y
26 1A   Z z    
27 1B   [    
28 1C red £    
29 1D cur right ]   ORA X
30 1E green Up Arrow   ASL X
31 1F blue Left Arrow    

Corrections: Inner Space Anthology lists the block–1 alphabetic SCREEN CODES as only uppercase. They should be uppercase in the upper/graphics character set, and lowercase in the lower/upper character set, as shown above.

Corrections: Inner Space Anthology lists the SCREEN CODE $1C as a backslash. It should be the british pound symbol (£) as shown above.

Block 2

DEC HEX PETSCII SCREEN
(up/gfx, lo/up)
BASIC 6502
32 20 space space space JSR
33 21 ! ! ! AND(I,X)
34 22 " double quote "  
35 23 # # #  
36 24 $ $ $ BIT Z
37 25 % % % AND Z
38 26 & & & ROL Z
39 27 ' apostrophe '  
40 28 ( ( ( PLP
41 29 ) ) ) AND #
42 2A * * * ROL A
43 2B + + +  
44 2C , , , BIT
45 2D - - - AND
46 2E . . . ROL
47 2F / / /  
48 30 0 0 0 BMI
49 31 1 1 1 AND(I),Y
50 32 2 2 2  
51 33 3 3 3  
52 34 4 4 4  
53 35 5 5 5 AND Z,X
54 36 6 6 6 ROL Z,X
55 37 7 7 7  
56 38 8 8 8 SEC
57 39 9 9 9 AND Y
58 3A : : :  
59 3B ; ; ;  
60 3C < less than <  
61 3D = = = AND X
62 3E > greater than > ROL X
63 3F ? ? ?  

Block 3

DEC HEX PETSCII SCREEN
(up/gfx, lo/up)
BASIC 6502
64 40 @ @ RTI
65 41 a A A EOR(I,X)
66 42 b B B  
67 43 c C C  
68 44 d D D  
69 45 e E E EOR Z
70 46 f F F LSR Z
71 47 g G G  
72 48 h H H PHA
73 49 i I I EOR #
74 4A j J J LSR A
75 4B k K K  
76 4C l L L JMP
77 4D m M M EOR
78 4E n N N LSR
79 4F o O O  
80 50 p P P BVC
81 51 q Q Q EOR(I),Y
82 52 r R R  
83 53 s S S  
84 54 t T T  
85 55 u U U EOR Z,X
86 56 v V V LSR Z,X
87 57 w W W  
88 58 x X X CLI
89 59 y Y Y EOR Y
90 5A z Z Z  
91 5B [ [  
92 5C £ £  
93 5D ] ] EOR X
94 5E LSR X
95 5F  

Corrections: Inner Space Anthology lists the block–3 PETSCII alphabetic characters as uppercase. They should be lowercase as shown above.

Corrections: Inner Space Anthology lists the block–3 SCREEN CODES lower/upper alphabetic characters as lowercase. They should be uppercase as shown above.

Block 4

DEC HEX PETSCII SCREEN
(up/gfx, lo/up)
BASIC 6502
96 60     RTS
97 61     ADC(I,X)
98 62      
99 63      
100 64      
101 65     ADC Z
102 66     ROR Z
103 67      
104 68     PLA
105 69     ADC #
106 6A     ROR A
107 6B      
108 6C     JMP(I)
109 6D     ADC
110 6E     ROR
111 6F      
112 70     BVS
113 71     ADC(I),Y
114 72      
115 73      
116 74      
117 75     ADC Z,X
118 76     ROR Z,X
119 77      
120 78     SEI
121 79     ADC Y
122 7A      
123 7B      
124 7C      
125 7D     ADC X
126 7E     ROR X
127 7F      

Block 5

DEC HEX PETSCII SCREEN
(up/gfx, lo/up)
BASIC 6502
128 80   @ reversed END  
129 81 orange A reversed a reversed END STA(I,X)
130 82   B reversed b reversed NEXT  
131 83 load & run C reversed c reversed DATA  
132 84   D reversed d reversed INPUT# STY Z
133 85 F1 E reversed e reversed INPUT STA Z
134 86 F3 F reversed f reversed DIM STX Z
135 87 F5 G reversed g reversed READ  
136 88 F7 H reversed h reversed LET DEY
137 89 F2 I reversed i reversed GOTO  
138 8A F4 J reversed j reversed RUN TXA
139 8B F6 K reversed k reversed IF  
140 8C F8 L reversed l reversed RESTORE STY
141 8D car ret M reversed m reversed GOSUB STA
142 8E graphics N reversed n reversed RETURN STX
143 8F   O reversed o reversed REM  
144 90 black P reversed p reversed STOP BCC
145 91 cur up Q reversed q reversed ON STA(I),Y
146 92 rvs off R reversed r reversed WAIT  
147 93 clear S reversed s reversed LOAD  
148 94 insert T reversed t reversed SAVE STY Z,X
149 95 brown U reversed u reversed VERIFY STA Z,X
150 96 lt. red V reversed v reversed DEF STX Z,X
151 97 dk. grey W reversed w reversed POKE  
152 98 md. grey X reversed x reversed PRINT# TYA
153 99 lt. green Y reversed y reversed PRINT STA Y
154 9A lt. blue Z reversed z reversed CONT TXS
155 9B lt. grey [ reversed LIST  
156 9C magenta £ reversed CLR  
157 9D cur left ] reversed CMD STA X
158 9E yellow Up Arrow reversed SYS  
159 9F cyan Left Arrow reversed OPEN  

Corrections: Inner Space Anthology lists the block–5 reversed alphabetic SCREEN CODES as only uppercase. They should be uppercase in the upper/graphics character set, and lowercase in the lower/upper character set, as shown above.

Block 6

DEC HEX PETSCII SCREEN
(up/gfx, lo/up)
BASIC 6502
160 A0 space CLOSE LDY #
161 A1 ! GET LDA(I,X)
162 A2 double quote NEW LDX #
163 A3 # TAB(  
164 A4 $ TO LDY Z
165 A5 % FN LDA Z
166 A6 & SPC( LDX Z
167 A7 apostrophe THEN  
168 A8 ( NOT TAY
169 A9 ) STEP LDA #
170 AA * + TAX
171 AB + -  
172 AC , * LDY
173 AD - / LDA
174 AE . LDX
175 AF / AND  
176 B0 0 OR BCS
177 B1 1 > LDA(I),Y
178 B2 2 =  
179 B3 3 <  
180 B4 4 SGN LDY Z,X
181 B5 5 INT LDA Z,X
182 B6 6 ABS LDX Z,X
183 B7 7 USR  
184 B8 8 FRE CLV
185 B9 9 POS LDA Y
186 BA : SQR TSX
187 BB ; RND  
188 BC less than LOG LDY X
189 BD = EXP LDA X
190 BE greater than COS LDX Y
191 BF ? SIN  

Note: Inner Space Anthology lists two PETSCII values at $A9 and $BA in the block–6 range. See end discussion on the changes I've made above.

Block 7

DEC HEX PETSCII SCREEN
(up/gfx, lo/up)
BASIC 6502
192 C0 TAN CPY #
193 C1 A A ATN CMP(I),X
194 C2 B B PEEK  
195 C3 C C LEN  
196 C4 D D STR$ CPY Z
197 C5 E E VAL CMP Z
198 C6 F F ASC DEC Z
199 C7 G G CHR$  
200 C8 H H LEFT$ INY
201 C9 I I RIGHT$ CMP #
202 CA J J MID$ DEX
203 CB K K GO  
204 CC L L   CPY
205 CD M M   CMP
206 CE N N   DEC
207 CF O O    
208 D0 P P   BNE
209 D1 Q Q   CMP(I),Y
210 D2 R R    
211 D3 S S    
212 D4 T T    
213 D5 U U   CMP Z,X
214 D6 V V   DEC Z,X
215 D7 W W    
216 D8 X X   CLD
217 D9 Y Y   CMP Y
218 DA Z Z    
219 DB    
220 DC    
221 DD   CMP X
222 DE   DEC X
223 DF    

Corrections: Inner Space Anthology lists the block–7 PETSCII alphabetic characters as lowercase. They should be uppercase as shown above.

Note: Inner Space Anthology lists two PETSCII values from $C1 to $DA, $DE and $DF in the block–7 range. See end discussion on the changes I've made above.

Block 8

DEC HEX PETSCII SCREEN
(up/gfx, lo/up)
BASIC 6502
224 E0     CPX #
225 E1     SBC(I),X
226 E2      
227 E3      
228 E4     CPX Z
229 E5     SBC Z
230 E6     INC Z
231 E7      
232 E8     INX
233 E9     SBC #
234 EA     NOP
235 EB      
236 EC     CPX
237 ED     SBC
238 EE     INC
239 EF      
240 F0     BEQ
241 F1     SBC(I),Y
242 F2      
243 F3      
244 F4      
245 F5     SBC Z,X
246 F6     INC Z,X
247 F7      
248 F8     SED
249 F9     SBC Y
250 FA      
251 FB      
252 FC      
253 FD     SBC X
254 FE     INC X
255 FF   π  

Corrections: Inner Space Anthology lists the block–8 reversed SCREEN CODES at $F7, $F8, and $F9 as their non–reversed counterparts at $77, $78 and $79. They should be reversed as shown above.


Discussion

Understanding the difference between PETSCII values and Screen Codes can be confusing. The lists you will find if you search the internet for PETSCII Table do not all agree with one another, nor do they all agree with older listings found in books such as The Complete Commodore Inner Space Anthology. My SuperChart listing above is derived from Inner Space Anthology. I have made some corrections, as noted above, but I've also made some changes. I want to explain my reasons for the changes, even if others would not agree with me.

Screen Codes

Screen Codes, divorced from their conversion from PETSCII values, are easy to understand. Any value from 0 to 255, which occurs in a screen memory location, will be rendered by the VIC-II chip as a unique character.

The appearence of that character depends on the character set that is used. A character set is a 2 kilobyte bitmap. The screen code value is an index into that character set bitmap. The C64 and Vic-20 come with a character ROM, which is 4 kilobytes and contains 2 complete character sets. Additionally, custom character sets can be defined in RAM. Therefore, what actually gets displayed on screen is determined by the contents of the character set.

In the two built–in character sets, the bottom half, 0 to 127, are all unique. But the upper half, 128 to 255, are the reverse of the bottom half. Conveniently, this means that toggling bit 7 of any screencode will exchange that character with the inverse version of itself. Custom character sets do not need to adhere to this standard, but the fact that the two built–in sets work this way was a very clever design decision. In order to make the cursor blink, the KERNAL need only toggle bit 7 on and off. A nice side effect is that it allows text to be written in reverse characters for titles that stand out, and doubles the available graphics characters for drawing PETSCII–based images.

PETSCII Values

What the PETSCII values actually are is more contentious. Obviously, it cannot be the case that there is a one–to–one relationship between PETSCII values and Screen Codes. If there were, then every PETSCII value would have a visual representation, and there would be no room in the 8-bit space for control codes.

The tables above are divided into 8 blocks of 32 characters, because PETSCII itself is divided into 8 blocks. And it is easiest to understand PETSCII by comparing those blocks with each other. The 8 blocks are further subdivided into the low 4 blocks, and the high 4 blocks, as indicated in the table of contents links at the top of the SuperChart. The high 4 blocks are like a shifted version of the low 4 blocks.

  • Block 1 → shifted → Block 5
  • Block 2 → shifted → Block 6
  • Block 3 → shifted → Block 7
  • Block 4 → shifted → Block 8

PETSCII uses blocks 1, 2 and 3, and their shifted equivalents, 5, 6 and 7. PETSCII leaves block 4, and its shifted equivalent block 8, undefined. This provides a space of 192 values, however, in practice 22 of even those values are not defined.

The general scheme, with minor exceptions, is as follows:

  • Block 1: Control Codes
  • Block 2: Symbolic / Numeric
  • Block 3: Alphabetic (Lowercase)
  • Block 4: Unused

The shifted corresponding blocks are as follows:

  • Block 5: Control Codes (Often with inverse role)
  • Block 6: Symbolic (Graphical)
  • Block 7: Alphabetic (Uppercase)
  • Block 8: Unused

If you look at the two blocks of control codes, the unshifted block 1 is almost always inverted by the same code in the unshifted block 5, (if that the control is dual in nature at all.) For example:

  • Stop → Run
  • Text → Graphics
  • Cursor Down → Cursor Up
  • Reverse On → Reverse Off
  • Home → Clear
  • Cursor Right → Cursor Left

The alphabetic block 3 values are therefore the lowercase letters, which become their uppercase equivalents in the shifted block 7.

And the symbolic / numeric block 2 becomes a set of 32 graphical symbols in the shifted block 6. The set of symbols don't have conceptual shifted alternatives, that's why the shifted block 6 is the block used for graphic symbols.

Strings of PETSCII that are output through the KERNAL's Screen Editor are interpreted and mapped into Screen Codes, which are put in memory to be displayed. For example, there is no PETSCII code that represents "reversed lowercase a". However, the following PETSCII sequence: $0E, $12, $41, (that is "the control code for Lowercase/Uppercase character set", "the control code for Reverse On", and the PETSCII value for "a") will cause a reversed lowercase "a" to appear on the screen where ever the cursor currently is, and then the cursor will be advanced. That sequence of PETSCII is interpreted by the KERNAL, but it results in the Screen Code $81 being put into screen memory, and the Lowercase/Uppercase character set being selected.

Confusion from Character Sets and Conversions

The above description of PETSCII is hard to uncover, because of how the alternative character set is laid out, and how PETSCII values are interpreted by the KERNAL's Screen Editor and converted to Screen Codes for display.

When a Commodore 64 is first turned on it is using the Uppercase/Graphics character set, rather than the Lowercase/Uppercase character set. It may simply be that Commodore thought having more graphics characters to work with in exchange for having only one case of alphabetic characters seemed like a good trade off. But, if you're going to have only one case of letters, you're going to choose the set of uppercase not lowercase. Why is that? Well, many things in this world are written in all–caps, but very few things are ever written in all lower case. Hence the existence of the term all–caps, and the existence of all–caps fonts. Also, many words, such as the pronoun I and most proper names cannot be correctly rendered in small letters alone. So it makes sense that the character set is Uppercase/Graphics and not Lowercase/Graphics.

However, it also makes sense that in the Uppercase/Graphics character set, it is the lowercase PETSCII values that are replaced by uppercase characters. Otherwise, when in Uppercase/Graphics mode you would have to constantly hold down shift in order to get letters instead of graphic symbols. But on the other hand, when you do switch character sets, it has to be that whatever you've typed without the shift key will appear as lowercase characters, otherwise, in the Lowercase/Uppercase character set, you'd be getting uppercase characters without the shift key, and you'd have to press the shift key to get a lowercase character. That just doesn't make any sense.

And so, all this taken into consideration, one should not be confused about the PETSCII values on account of the existence, availability and even default–status of the Uppercase/Graphics character set. It is merely a character set, an alternative on–screen representation. In a conversion from ASCII to PETSCII, ASCII's uppercase characters map to PETSCII's block 7, and ASCII's lowercase characters map to PETSCII's block 3.

In my opinion, a "standard code of information interchange," which is what the SCII in PETSCII stands for, cannot have multiple meanings for the same value. Given that I feel there are good arguments that the lowercase alphabetic characters are in block 3, and the uppercase equivalents are in block 7, then the Lowercase/Uppercase character set should be used for deriving the canonical PETSCII values in block 6 and 7 where there are alternatives.

Specifically, $A9 and $BA. These are mapped by the KERNAL to the Screen Codes $69 and $7A respectively. In this entire block (block 6), both character sets are identical, except for these two values. My listing indicates that the canonical PETSCII value is the character that appears in the Lowercase/Uppercase set, and that the other character is merely a result of swapping in an alternative character set.

The only other contentious spot is $DE and $DF in block 7. These get mapped by the KERNAL to the Screen Codes $5E and $5F, where there is an alternative appearence in the Uppercase/Graphics set. This ambiguity is not there when you type the unshifted ↑ (PETSCII $5E, mapped to Screen Code $1E), because $1E is identical in both character sets.

The Character Sets

Here are the character set images, to help clarify how the Screen Codes work and differ between them.

Lowercase Uppercase Character Set @2X Uppercase Graphics Character Set @2X

Lowercase/Uppercase Character Set, and Uppercase/Graphics Character Set.

December 20, 2017Editorial

World of Commodore '17

Another year, another adventure. Long live the C64. Your second World of Commodore since returning to the scene cannot ever be as epic as the first one. But as the fates would have it, I would not be able to repeat last year even if I'd wanted to. This is my review of World of Commodore 2017. In a nutshell, friends and networking. Networking as in what people do when they get together? No, no, no. Digital, 8-bit, computer networking. Read on.

The Journey

Last year the journey felt truely epic. I got up early on a Saturday, I brought my son along with me. I had no expectations, except bad ones, and when I arrived it was just one pleasant surprise after another. What could compare with that?

I've got several of hobbies that tug at my heartstrings and draw me out into the world, from Esperanto conferences to Star Trek CCG tournaments. Commodore Expos are a new(old) one to add to my list of places to go throughout the year. But my wife is always making me agree to compromises so that I actually, you know, spend time with her and our family. The dice were rolled, and this year the Archibald Family Christmas party at a cottage north of Peterborough came up on the same Saturday as World of Commodore's main day. Drat! I tried to argue that I've been working my butt off all year and that the one and only time and place that anyone on earth is going to care about what I've been doing is that very Saturday, in a hotel basement in Mississauga. I didn't get far along those lines. Besides, the show is a two day event, right?

So I went to the cottage on Saturday, December 9th. A 3 hour drive out, in the winter, and a 3 hour drive back at the end of the day. I was already exhausted and I hadn't even made it to the show. I packed up my C64 Luggable project, got some Commodore Logo Patches ready to bring with me and went to bed early. The next morning, I got up before the sun. Bid farewell to my wife and sleepy kids and hit the road.

I wasn't quite as excited as last year. I knew where I was going, I knew how long it would take me to get there. And I knew that the location was the same as last year so there weren't likely to be any big surprises. And there weren't. The weather was great, the drive was fast and easy, I stopped once for food and a bathroom, and I didn't even bother to get gas. By the time I pulled into the parking lot at the Admiral Inn my tank was on empty, and it was 10am sharp.

I left my equipment in the car, and didn't even bother to put on my coat. I just dashed across the frigid parking lot in my sweater, and into the hotel lobby. I ran past a guy loading a luggage cart stacked with 8-Bit Commodore goodness into the back of a van. Besides that and a tiny square sign with the chicken lips logo and an arrow pointing right, there wasn't much sign that anything was going on. I was a bit worried. Maybe last year was an exception? Maybe Bil Herd had drawn an uncharacteristically large crowd last year that would never be matched again?

I headed downstairs and found the sign in table completely unmanned, and no one was waiting to sign in. Damn it, did I miss the whole show?

Before I took five more paces, Jim Mazurek, a true 8-Bit soldier, who along with Eric Kudzin seem to go to supernatural lengths to make an appearance at every show, popped out from around the corner. He greeted me kindly, and broke some unexpected but hugely uplifting news. Dave Ross is here! Holy crap! Dave (Watson) Ross, one of my oldest online Commodore friends, together with Vanessa Dannenberg and David Wood (too bad they couldn't make it), was here in the flesh.

I took a couple of steps into the showroom and peeked around the corner to see what I could see. "No shit! Is that really Dave Ross I see sitting there?!" I called out. The same guy, only a decade older than the last time I saw him spun around and stood up with a smile on his face. After extending my arm to shake his hand, I thought, ah screw that, and gave him a hug. He could have been sitting completely alone in that giant room, and I still would have been happy to have made the journey.

The Showroom

A couple of quick thoughts. First, there is no doubt that this is a Sunday morning, the second day of a successful show. The tempo was way down from last year, but that was not a sign of less interest or smaller participation. Instead, the room was only partially populated by sleepy lookin' dudes who had just gotten out of bed from the partying the night before and strolled down to see what was going on. Many of the regulars, Joey and Leif, for example, weren't even there. John (CRT) Hammarberg, who I wrote about last year, was there, but he was packing up his gear and getting ready for the 6 or so hour drive back home. I barely had a chance to say hi, shake his hand and wish him a safe trip home. I also discovered that Jim Brain had not made it up this year. That's a bummer.

The second thing to point out though, is that the room was bigger. Like, two times as big. At first I was worried, were there no presentations this year? Last year the room had been divided in half by means of a folding partition that slides along a track in the ceiling. This year, that partition was neatly tucked away inside the hidden wall compartments, and the showroom floor was consequently much more spacious than last year. But, fear not, I soon found out, when the first presentation of the day was due to begin (which I'll go into later), that there is a whole separate room reserved and set up just for presentations.

The Showroom shot 2 The Showroom shot 1

This year the showroom was configured more like a giant 8 on an old digital alarm clock. The room was rectangular, like two squares side by side. Tables flanked the entire outer rim of the whole room. And in the middle, two inset squares, one for vendors the other for more demo machines.

Let's walk our virtual way around the room to the best of my memory. Last year I left a couple of people out who weren't super happy that I'd completely forgotten about them, but you know how it is with memory.

The first machine I cam across was a breadbox C64 with a conspicuously bright red 1541 Ultimate II+ hanging out the back. I got a look at it, and learned a few tidbits, such as that the onboard speaker plays sounds that make it sound like a spinning 1541 disk drive. To be honest, I'm not sure how much I like that, and I hope that feature can be turned off. The other thing I learned is that it's access to mass storage devices works a lot like the uIEC/SD. I'm still going to have to get my hands on one for testing and compatibility with C64 OS. I'd love it if this became the storage device in my C64 Luggable. Because it packs a lot into a small space, ethernet, USB Storage, REU emulation, Freezer Cartridge, and more.

A breadbox 64 with a red 1541 Ultimate II+

Beside it on the same table was an Amiga 600 with the Vampire 2 accelerator board. I've never been into the Amiga, but there were quite a few of them scattered around the room. I looked up the Vampire 2 board online, and found this link for you, if you're interested in getting one for yourself. http://www.apollo-accelerators.com.

In the video review of the Vampire 2, in the first 5 seconds of the video Dan Wood declares that the A600 is "by far the worst Amiga every created" and the black sheep of the Amiga family. I would not have guessed that that's how people feel about it. It has always been my favorite model, coming from a guy who has never used an Amiga in his life. I think it's the diminutive form factor, but to me it is the sleekest looking and most visually appealing of all the Amigas. It's so cute, I love it. Anyway, now it's got some amazing speed boost via the equivalent of the SuperCPU of the Amiga World, Vampire 2. Or something. Check it out.

An Amiga 600 with a Vampire 2 accelerator

Right next to that was a table with two of Steve Gray's PETs with their tops cracked open. I got a couple of quick looks on the inside. The first is an 8032, and it was showing off the Multi-Edit ROM adapter. And the second was a SuperPET with some other board mounted inside. Unfortunately I didn't get a clean look at that one.

Two of Steve Gray's PETs with the tops cracked open

Check out the Multi-EditROM up close and personal. Looks wonderful! I love seeing all those jumper wires, reminds me of when I installed the daughter board in the c128D to make it compatible with the SuperCPU128.

If you're interested in this project, you can read all about it on Steve Gray's website, (a sub-page of 6502.org, how cool is that?) http://www.6502.org/users/sjgray/projects/editrom/.


The Editor ROM is responsible for all video initialization, screen output, keyboard input, full-screen editor, and IRQ handling. By putting all this in one ROM Commodore was able to customize the machines for various markets, with different options. Commodore provided for most combinations, but not all models got all options. Later CBM machines had additional editing features and even a simple power-on menu for "Executives". Steve Gray—The PET/CBM Editor ROM Project

He also had on display the absolute behemoth, CBM Model 4040 Dual Disk Drive. The thing is like the size of a small washing machine. No, I jest, but it is big. Especially compared to the very modestly sized Super Disk Drive SD-2 by MSD which he also had on display, with the top case removed, right next to it.

PET with Multi-EditROM, shot 1 PET with Multi-EditROM, shot 2

Zooming around the corner, Leif Bloomquist, TPUG member and co-organizer of World of Commodore had several of his own machines set up.

The first was in his transparent C64c case which I marveled at last year. This year he was using it to show off a Raspberry Pi C64 interface board of his design. Unfortunately this is not a commercially available project, and he doesn't currently plan on manufacturing the board. Although the schematics and plans are (or will be) available on his website.

Essentially, the Raspberry Pi is mounted on a board that plugs into the User Port on a C64. It is fully powered by the C64 and can be communicated with over the serial connection provided by the User Port, as though communicating with a remote machine over a modem. Except, the other machine isn't remote, it's neatly hanging straight off the back of the C64.

Besides just being a kind of neat project, to see that you can do that, I'm not sure if Leif has in mind any bigger plans for what one might be able to do by having access to a Raspberry–Pi–in–a–UserPort–Cartridge. Frankly, I'm a fan of when these sorts of projects are turned into commercial products, even if there is only a run of 50 boards or whatever, even if it's a kit, but I respect the fact that there is some financial risk in making a bunch of product that no one ends up buying.

By the way, I consider this project to fit within the theme of "networking" for this year's show. Even though it's a point–to–point network over a very short distance, it is an interesting entry in a wide range of demonstrations of people using their C64s to talk to other computers.

Raspberry Pi C64 Interface, shot 1 Raspberry Pi C64 Interface, shot 2

Leif also had on display a C64c with a beautiful shade of blue for the top case. This one he painted himself. It looks very professional. The machine also had a number of buttons, switches and knobs popping out the left hand side. I should have asked him what all those do.

Beautifully modded C64c with blue top shell

Another contributor to the networking theme of this year, but not quite a Commodore 8-Bit, Leif also had set up a WYSE WY-60 Terminal between his C64c's. WYSE was a company founded in 1981 that specialized in terminals and then thin clients, before being acquired by DELL in 2012. You can read all about there sordid history here, https://en.wikipedia.org/wiki/Dell_Wyse.

Essentially this machine was just a terminal. Sort of like how we use terminal emulator software, that software just emulates what these machines and their ilk had built in. Leif hooked up a DB-25 Parallel WiFi Modem that emulates a Hayes Modem. It's pretty cool, because with this simple "dumb" machine, plus the intelligent guts crammed onto that tiny WiFi modem, this is all you need to be able to log into a free Unix Shell account somewhere, such as Toronto Free-Net. From there, you can read email with PINE or browse the web with Lynx, and so on. Pretty cool.

Also sitting on the desk there beside the WYSE Terminal is the very last one of Leif's own deluxe WiFi modem. He made a run of about 100, and has since sold them all, down to this very last one. There will not be another run, so be the lucky one to get your hands on this one. I asked him if he felt it was a success, and the answer was a resounding yes! He expected to make 20 or so, and questioned whether he'd be able to offload them all. So it was a nice surprise for him to be able to move so many units, especially considering the plethora of cheaper but less capable models of WiFi that have become popular in the community in the last few years.

My only regret is that I'll have to remove his modem from the Commodore 8-Bit Buyer's Guide after I get word that he's sold the last one.

WYSE WY-60 Terminal Back of a WYSE WY-60 Terminal with a WiFi Modem

The VR64, a set of Virtual Reality Googles for the C64, was demoed earlier in the year, but this is the first time I had a chance to try it out for myself. Jim had sent Leif a sample of the goggles to put on display at World of Commodore.

The project was put together by Jim_64 (I can't seem to be able to figure out his full name, apologies.) He wrote a nice blog post about how he put the project together with links and resources to be able to build your own. http://64jim64.blogspot.ca/2017/09/vr64-virtual-reality-goggles-for.html.

His project is so clever and of interest to people on cutting edge of technology with VR being all the rage these days, that VR64 was featured in an article on Gizmodo, and on PopularMechanics.com. I'd say that's a pretty big win for the popularity of the C64. The articles feature some nice videos you can watch.

I got to try on the goggles, and play the one game that Jim_64 also produced to go along with the project, Street Defender. It's cute, a bit simplistic, but there is some real promise there. I'd love to see what sort of mind bending 3D visuals could be produced if a couple of experienced demo coders felt like putting their talents behind some VR64 demos.

VR64 3D Virtual Reality Goggles for C64 VR64 in action with Street Defender on a C64c

UPDATE: January 2, 2017

Didn't I say I'd forget someone? It totally skipped my mind when I first wrote this review, but Francis Bernier from Montreal was setup after VR64. I didn't take any pictures of his table for some reason. He had on display a C64c, top case removed, with several modifications made by soldering things directly onto the main board. To be honest, it gave me the willies to think of dropping a soldering iron directly onto a c64's main board, for fear of damaging something.

He had removed the RF Modulator, and used the space for a modification that broke out the video signals in some way as to get a cleaner signal with less noise and interference. He paired this with a LumaFix64. The LumaFix64 is listed in the Buyer's Guide, and I would have bought one from him right there on the spot, but he only had one left and it was already spoken for. The LumaFix64 sits directly into the VIC-II socket, and the VIC-II chip is then plopped into the matching socket on the other side of it. There are then three tiny adjustable knobs. These can be tweaked in realtime while the machine is on. You watch the display, and if there are vertical bars or other interference, you can gently adjust those knobs until the picture becomes clearer.

The combination of his video line mod, plus the LumaFix64, produced an output of truly stunning clarity. I thought the 1084S produced sharp output. But I've never seen results this good. He had an upscaler connected to a VGA monitor. You don't see raster lines when using a VGA monitor, and I commented that the expensive upscaler probably had something to do with the quality. But he assured me it did not. He had another 64 setup without those mods. With the flip of a switch he could toggle the display between the two machines. The machine without any modification had the typical blurriness we're all familiar with, horizontal bleed and weird vertical lines. But when it flipped back to the machine he was showing off, it was like looking at an emulator it was so clear.

Getting a LumaFix64 is on my shortlist of purchases for 2018.

Apologies to Francis for forgetting to mention you in my first draft.

Sliding down the tables some, Jim Mazurek had a flat c128 with a 512K REU clone, a small drive stack, 1351 mouse and an Amiga monitor. Across the table was scattered a variety of new and old hardware expansions, a machine language reference guide, some cables and a networking router. Precariously balanced atop the Amiga monitor was a XYPLEX MAXserver 1600, Terminal Server. A say what now? A 16-port terminal server, which you can pick up on ebay for anywhere between 100 and 200 dollars.

Jim was using the XYPLEX as a TCP/IP to Serial bridge, which he connected to the c128 via a UserPort serial adapter that looks suspiciously like an EZ-232. Ironically, this entire goldbergian contraption plus the benefit of wireless networking is more or less encapsulated in any of the many popular WiFi modems. However, Mazurek assures me that there are numerous useful benefits to using the XYPLEX, like 16 serial ports(!) that make it worth the trouble and physical space it occupies.

In another wonderful contribution to the networking theme, Jim was using his setup to display the modern revival of Quantum Link, or Q-Link, the originally Commodore 64/128 only online service that changed its name to, you guessed it, America Online, in 1991. After the name change, the service expanded to service the PC and Macintosh and still exists today.

Q-Link offered a number of departments for such things as news, online shopping, chatrooms with other users, downloading software, and more. As a kid I remember having the Q-Link disk that came packaged in the box with GEOS on my first C64c. By the time I acquired my first modem in the mid 90s the service had long already moved on and was no longer accessible by the C64 version of the software.

The efforts of Glenn Holmer and Jim Brain, lead to QuantumLink RELOADED. The backend code has been updated to support a bridge to the IRC network, which is what Jim Mazurek was demonstrating. The process of getting online was, in my opinion, complicated. But I do applaud the effort. And it's nice, as Jim said, for people to experience the nostalgia of using Q-Link again if they had been devoted users of it back in the late 80s.

Jim Mazurek's c128 setup Jim Mazurek's c128 demoing QuantumLink RELOADED

Later in the day, I decided to shoot a video of Mazurek walking me through the process involved in getting his c128 connected and logged into the #c64friends IRC channel via Q-Link. Give it a whirl, it's got a few funny bits, and some chit chat that gives the feel of what it's like to hang out with your C64 friends at an expo.

Rounding the bend again, and a long line of tables ran the back of the room. First up was a table setup with diagnostic cartridges and lots of bare motherboards. Along with some monitors, this table was meant for people to test hardware. Joe Palumbo spent a healthy portion of the day hunched over some C64 mainboards cursing and scratching his head. It also served as a nice spot to congregate and discuss some of the technical intricacies of C64s, such as how to identify when a particular RAM chip has gone bad. Also discussed was how to deal with the fact that many C64s have their ROM chips soldered directly to the mainboard, and how best to upgrade these machines with JiffyDOS.

I learned a few fun facts. One, is that it is very likely the reason some mainboards have sockets and others have the chips soldered on, is because Commodore was cranking out mainboards as quickly as they could. But as they went, the production of the mainboards was not always in sync with the ready supply of ICs. If some ICs were not available at the time the board was being produced, they simply substituted in a socket instead. When the chips became available they could easily be plopped into place.

Next up was the table with drinks and a super old school TPUG sign ringed with a string of neon blue lights. Bottles of pop, stacks of red plastic glasses and a couple of bottles Rye Whiskey tantalizingly called out my name. 11am seemed a bit early to crack into the booze though, and by the end of the day I was going to have to drive home. This is one of those unfortunate downsides to showing up on a Sunday. If I were there on the Saturday, and had a room somewhere a few short storeys above the show, I'm sure that smooth liquor would have added to the authentic World of Commodore experience. This is bringing back fond memories of hot pressing T-Shirts with the words "I Partied with Jim Butterfield" strewn across them.

Speaking of Jim Butterfield, his name still comes up at these shows. And there is still an air of sadness in the room from the loss of such a vibrant, eccentric and memorable Commodore icon. It was at an expo much like this, many years ago, that I first learned about how to make the BANANA prompt.1 Let's all raise a red plastic glass of Coke and Whiskey, to Jim Butterfield.

Jim Butterfield, in memory of.

On the next table, past the emergency exit, sat a few Amigas doing their Amiga-y thing. One was a brand new Amiga motherboard inside some partially transparent PC tower case. I think a lot of Amiga fans would have been excited to see it, but I admit, that, by the time it gets up to that level, it just starts looking like standard boring PC to my eyes.

Somewhat more exciting for me, were the Amiga 1000 and 3000 (I think) that followed it. I'm not 100% sure what these were showing off, but it's always fun to poke around on an Amiga when you get an opportunity.

Amiga 1000 Amiga 3000

And it's pretty traditional to have a few games stations set up. In the corner following the Amigas were an SX-64 and a Breadbox with what look like the KONIX SpeedKing, or perhaps the EPYX 500XJ Joysticks, hard to tell from the photos. These machines dutifully pumped some SID tunes into the air.

SX-64 Gamestation Breadbox C64 Gamestation

Next up around the corner from the game stations was Jay Hamill's rig. He had a old laptop sandwiched between a C128D and an SX-64 of his own. The C128D had four custom toggle switches jutting out the bottom of the front panel. To one side of his rig was a bin full of cartridges, eprom burners, joysticks, a cartridge port expander and a 1764 REU. To the other side was a stack of drives, including a 1541-II, a 1581 and "Micro IEC" which predates the uIEC/SD. It's the same concept, was made by Jim Brain, but has a Compact Flash card and a socket for an IDE ribbon cable. This Micro IEC storage device is no longer available, having been superseded by the uIEC/SD. But what amazes me is that this device was created, came into existence, was commercially sold, and then stopped being sold in favor of a newer, smaller model supporting newer smaller storage cards, all within the length of time that I'd been out of the scene.

The table surrounding his 128D was covered with networking hardware. WiFi modems and serial adapters, some of which after all my searches online for populating the 8-Bit Buyer's Guide, I'd still never even heard of.

Better yet, he demoed for a small crew of spectators how to configure and sign into a WiFi SSID, and how to connect to IRC and FTP using these devices. But, I'll return to cover those in the next section about the demos. Chalk another one up for the networking theme.

Dave, Eric and Jim watching Jay Hamill give a demo of a WiFi modem WiFi modem configuration utility to connect to a hotspot by SSID

After that came the freebies table. In the middle of the room, there was another PET, with the built–in cassette deck and the chicklet keyboard. And beside it a Vic-20 with the old style power cable.

It was on these inner tables that I brought in and set up my C64 Luggable, such as it is. I didn't have time to finish it before World of Commodore, but I also didn't want to rush it. I put the bits together, but the back door is super rough, the front panel is not yet permenantly screwed down, so it's held on with masking tape. But the speakers and their grills were in place, the front panel is all dremeled out, but held on with masking tape. None of it is painted, though I put the paint chit on the top, to show what color I intend it to be.

The handle and display were put in, the mainboard was mounted in, and the keyboard and mouse I brought along to set out in front of it. I stuck the sweet little chrome Commodore logo to the keyboard, and masking tapped the "Commodore 64 Personal Computer" logo mark to the front of the machine. However, I did not have the rubber feet on the bottom yet, because I want to wait until after the bottom has been painted to apply those.

I did bring with it one little surprise that I haven't posted anything about online yet. My mother, who is a very skilled quilter, has made me a custom fit, quilted cover with a cutout for the handle. And to the cover she stitched on two of the embroidered Commodore Logo Patches that are available in the Buyer's Guide.

I got a few oohs and aahs, and a handful of questions about the machine. But, unfortunately it was not in functional condition, so I was not able to turn it on. Next year, I fully expect that not only will I be turning it on, but I'd love to use it as a demo station and a 4 player game station. The speakers in my C64 Luggable were easily the nicest to be found anywhere in the room, so using it as a High Voltage SID Collection boom box would also probably go over pretty well.

With any luck, C64 OS will be at least bootable, with something interesting to show for next year too. In which case, this will be my demo machine for whatever talk I can schedule myself in to give. Ironically, I didn't take any photos of my machine on display at the show, but I took some photos of it in the workshop the night before I left, and here they are.

C64 Luggable, under the wraps of a custom quilted cover C64 Luggable, with keyboard, as displayed at the show

The Presentation Room

I'll be pretty straight with you, I totally missed the best and most interesting presentations, the ones that took place on Saturday. But there were some presentations on Sunday too. My greatest lament is that I missed the in depth talk on the PLA, delivered by the guy who created U17 PLAnkton. Forgive me for having forgotten his name. Eslapion, as he's known on the Melon64 forums. Such a talk would have been really great. I'm actually rather disappointed I didn't get a chance to hear it.

The presentation room this year was actually in another physical room, accessible from the main lounge area outside the showroom. It had seating for about 40 people, which to me seems a bit cramped. But on the Sunday it was okay because there was a smaller crowd that day. There was a projector and a screen at the front of the room, as well as a microphone and a presenter's table for his or her machine and notes. Tom Luff was operating the video recorder on a tripod from the front row. One blessing is that I'll at least be able to watch the discussion of the PLA on YouTube.

UPDATE: January 2, 2017

According to MindFlare Retro (@MindFlareRetro) on Twitter, the title of the presentation by Eslapion about the U17 PLAnkton was "The PLAin Truth About the Commodore 64 PLA". I haven't found the video yet on YouTube, but when I do, I'll embed it here.

The first talk I went to was by David Zvekic about DizzyTorrent for Amiga 68K. DizzyTorrent was actually released the weekend of World of Commodore, so this is pretty fresh news. He gave a delightful talk about the challenges involved with getting an Amiga to play nicely with the world of BitTorrent. One main issue that sticks out in my mind is that when you are torrenting a large file, you receive little bits of it at a time. And whichever little bits you have become available for other people to get from you. The problem is that this requires more or less random access into what could potentially be a multi–gigabyte file. The Amiga file system known as Fast File System (FFS), as it turns out, is not that fast. His description of it reminded me of the problem with seeking through a large file on a typical C64 file system, like that of a CMD native partition. The directory entry points to the first block, and the first block points to the second block, and so on until the end of the file. There is no way to find the last block of the file without loading in every block along the way and traversing all the pointers. His solution was clever, and the rest of the talk was similarly interesting.

If you're an Amiga 68K user, soak up the love here: http://amitopia.com/bittorrent-client-for-amiga-68k-released-on-aminet/

DizzyTorrent for Amiga 68K demo A quick shot of half the audience of a typical talk

Although this next one was not an official talk, (if it didn't happen on Saturday,) it should have been. It was super easy to talk Jay Hamill into giving us a pretty extensive demonstration of the typical WiFi UserPort Modem.

With the development of a very inexpensive component, a WiFi modem, full TCP/IP stack, with programmable firmware, to RS232 serial. It was originally developed to make it easy for companies with older but very expensive hardware with serial ports to get that hardware onto a WiFi network for cheap–and–ready remote management. This little device has become an absolute boon to the retro computer market. There are many more solutions that make use of it than I was initially aware of, and I'm still playing catch up trying to get them all listed in the Buyer's Guide.

Hamill was eager to show off a myriad of interesting hardware devices, he had scattered around his c128D. Here are just a few examples: The MicroIEC (CF/IDE), C64Net Wifi, Link232, EZ-232, Commodore 64 Telnet Wifi Adapter and Strikelink WiFi. Check them out.

MicroIEC, CF and IDE C64Net WiFi Link232, Swiftlink Clone
Commodore 64 Telnet WiFi Adapter Strikelink WiFi Assortment of networking hardware expansions

Hamill walked us through what you can do with a WiFi Modem. He picked one that had been created by Bo Zimmerman, along with a number of programs that Zimmerman has put together to make use of them. We only got to see a few of them in action, but others lurked in the directory listing that I'm pretty excited to download, reverse engineer and learn from and about in the coming weeks.

A config utility that lists the WiFi Hotspot SSIDs, and lets you join by supplying a username and password. Looks pretty straightforward, although I'd love to rewrite this application for C64 OS. The great thing about the hardware is that its network connection is independent of the C64. Once you use the configuration utility to get it online, it will remain online and even survive the C64 itself being reset without dropping the connection. This is very convenient because it allows you to hop from one program to the next without needing to reconnect to the hotspot.

The other two programs he walked us through are IRC Chat 1.5, and an FTP client. We used the IRC Chat program to get on #C64friends right then and there. While I appreciate the nostalgic angle of using QuantumLink RELOADED, using a dedicated IRC Chat program is much more straightforward. He then walked us through connecting to Bo Zimmerman's own FTP server using the FTP program. We downloaded a program and it worked great. The most astonishing fact about these programs, get ready for it, is that they were written in BASIC! The BASIC and KERNAL roms of the C64 include built–in support for RS-232 communications (up to 2400 baud) over the UserPort.

A few caveats, yes, these programs, and these modems, are kind of slow. The common UserPort WiFi modems can actually support 9600 kbps, which is a lot better than 2400 baud. But, if you've been used to using a 33.6K or 56K modem over a Turbo232, these things are gonna feel pokey. There is no real reason why this WiFi modem dingus could not be connected via RS-232 that connects to the C64 via the expansion port however. Also, these particular programs, which feel a bit more like proofs–of–concept than truly end–user friendly, were written in BASIC. BASIC and the built–in KERNAL routines only support up to 2400 baud. It will be most interesting to see what sort of speed can be accomplished with a native TCP/IP stack and 64NIC+ or RR–Net ethernet adapter.

IRC Chat, shot 1 IRC Chat, shot 2

Near the end of all that Jay Hamill was helpfully showing us, we got called away for another talk in the presentation room that was about to begin.

This talk was called Annals of Digital Archaeology. It was given by a gentleman, Zbigniew Stachniak, who described the process they underwent to uncover the lost secrets of an early home computer company that was founded in Toronto in 1970s.

This company was so on the bleeding edge of what it even meant for computing to be done by home users, that they wanted to explore the early software to see what ideas they had. Unfortunately so much of the format and the documentation has been lost to time, that uncovering what they could was really quite akin to archaeology. They started by digitally recording the analog contents of the audio cassettes and analyzing the waveforms to try to figure out what encoding scheme was being used so that the original information could be extracted.

It was a bit of an unsual talk, but as I sat and listened, I marveled at how, here in a hotel basement people have peacefully gathered together, and are sitting quietly watching a man give a presentation which he had clearly devoted a great deal of time to. It is so genuinely refreshing to take part in something that is so real. Our world has become annoyingly fixated on what is next, what is better, what is faster, and often regards just last year's technology with dismissal and disdain. Yet here we are, gathered together, watching with interest and curiosity to learn about the clever ingenuity, pragmatic decisions, and inevitable pitfalls and missteps of engineers who were pioneering personal computing a decade before I was even born. It's fascinating.

I grabbed a panorama of the presentation room for this talk.

Presentation Room Panorama

Wrapping Up

Unfortunately, the Sunday show ends at 3pm. It gives everyone who has traveled some distance a chance to get back on the road. I sat around for a half hour or so catching up with Dave Ross before he had to get a lift up to the airport to fly back to Boston.

Our stories are not too different. We both sort of fell out of the community for a few years. We both got married, and we've both got toddlers running around at home. And here we are, years later, a few more grey hairs in our sideburns, hanging out at a Commodore Expo again. We both agreed that something over this past decade changed.

In the 80s our Commodore computers were on the top of the hill, the peak of their commercial popularity. In the 90s, we struggled with trying to keep our machines competitive with a rapidly developing PC landscape, losing community members like big blue was swatting flies in the window of a summer farm house. By the start of the 2000s we had to come to grips with the fact that we had lost the war. And there was a widening gap between the old guard and the youngsters. The old guard was a progressively aging group of people who didn't want to move on, and still wanted their beloved Commodores to be productive. And a younger crowd, who more and more just wanted to make retro games, go to demo parties, and stay up all night drinking, hacking and coding. We both left the scene somewhere around this time. But in the intervening years, something happened with PCs, and the rise of the iPhone, and other mobile computing appliances, that are beautiful to behold, work flawlessly as if by some magic dust that's been sprinkled inside, and yet have hermetically sealed away even the faintest trace of anything electronic.

PCs won the war. But they won the war so hard and so completely, that they came out the other side as something entirely different. They are not really even computers any more. They're just iPads and smartphones. Apple has this ad they recently released showing a young kid living and breathing his iPad Pro and Apple Pencil. But when his mom tells him to put away his computer he innocently asks, What's a computer? Exactly. What is a computer? Commodore 8-Bits, and other platforms of its era, have found a new life, as computers. Things you can dig into, and get your hands dirty with, and learn about in all their intricate technical details. It's about falling in love all over again. That sense is palpable at World of Commodore. We are living in the retro computing renaissance. And it is wonderful.

Heading Home

Before we left the hotel, I kept pickin' the brains of Leif, and Ian, and Jim and Eric, about how NTSC and PAL video signals are encoded, and how computers are timed around their delicate requirements. Then we shot the video of the QuantumLink RELOADED review, as embedded above.

I tested as working, and bought a 1541-II from JPPBM. Then I splurged and bought my first PAL C64c, an Australian model. Can't wait to finally be able to view those PAL demos I've been deprived of all my life. I bought a couple more Abacus books, one on Graphics Programming for the C64. I'm sure that'll come in handy throughout this year as I continue to work on C64 OS.

We packed our cars, and made one last rendezvous at Master Steaks. A grungy diner just off the 401 highway, ate some deliciously tasty chicken souvlaki served on a cafeteria tray, and carried on the technical discussions with Joe Palumbo, Leif Bloomquist, Eric Kudzin and Jim Mazurek. When we were finally plump and satiated, and well caffeinated for the long drive home, we shook hands, wished each other well, and headed off in separate directions into the dark and frigid Canadian winter night.

See you all next year my friends. It's been a pleasure.

  1. The BANANA prompt was a fun trick he showed us, first copying the contents of the BASIC ROM into the underlying RAM. Then swapped out the ROM for the RAM, and poked the letters "BANANA" overtop of the constant for "READY." at $A378 []
December 5, 2017Software

The Big 5 Oh

Welcome back C64 and 6502 fans! This is my 50th blog post at c64os.com. Today will not be a highly technical post, but I have a lot of small things to talk about.

World of Commodore 2017

It's that time of year again. World of Commodore 2017 is just days away. Read about it here. I had a wonderful time refamiliarizing myself with everything that's new in the Commodore world at this show last year. It is sure to be a blast.

Unfortunately I can't make it on the Saturday, because of family obligations. But I will be there on Sunday, as this is a two day event. I'm not sure what I'll be bringing. I've been hard at work through the whole year, but nothing I've been working on is really ready. C64 OS has seen lots of work and now consists of lots of code, but there isn't much yet to demo. And C64 Luggable, which I'll likely bring with me, is not fully finished. But, I might bring it anyway, just to show people my progress. I know at least a couple of people are interested in where it's at.

C64OS.com

Hopefully someone will notice, but I've put a lot of work over the course of the year, into building this website. In the last couple of weeks I have done a fair bit of work touching up the styles. I've normalized the fonts and the hierarchical usage of H1/H2/H3/H4.

I have also put quite a bit of work into getting the whole site to be responsive. This involved redoing the top navigation completely. This is now done entirely in CSS, no more graphics for those little bubble buttons. This allows the top navigation to scale smoothly down to mobile phone sizes. And in all the pages with a left side bar, WELCOME, C64 OS, C64 LUG, and WEBLOG, the side bar now gets out of the way on mobile devices, letting the content take the full width. You can call out and dismiss the side bar with new buttons that appear above the section title.

Responsive design of C64OS.com

I want to give a shout out1 to my longtime friend and colleague, Brad Marshall, for his help, suggestions and patience with me, on some design and CSS questions to get this site more responsive and looking a bit snazzier. He's a talent web designer, and you can read more about him and his work on his own blog, bmarshall.ca.

The popularity of the site is growing, as I promote it on Twitter and IRC and move up the ranks in the search engines. I'm now getting hundreds of unique visitors a month. So I feel pretty good about that. I've only got ~230 followers on Twitter, so it helps a lot when people with a couple of thousand followers retweet my tweets that promote a new blog post, or a new feature in the Buyer's Guide. Thank you to everyone for that!

C64 OS

As for the C64 OS Technical Docs on this site, I have not put nearly enough time or effort into them. That is something I intend to put a lot more energy into in the coming months. One reason for my reluctance has been the nature of ongoing and active development of the project itself. Unlike a weblog, the C64 OS Technical Docs are a living document that are meant to reflect the current state of the project, not a chronological log of how things are changing.

As it stands, whatever is in the Tech Docs is pretty out of date already. The good news is that development of C64 OS continues apace. I feel like I've got the modularity of it well worked out, assembling multiple modules that plug together and are able to call each other's routines has never been easier. This lets me focus on how to organize the code. And there is still a fair bit of flux involved here. Big projects are in large part, in my opinion, a matter of good organization.

It's quite a trip to be coding native. Many tools that would ordinarily be available to a programmer on a modern platform are simply unavailable. I continue to learn about what the tools I'm working with can actually do. I'm almost ashamed to admit this, but I didn't realize that Turbo Macro Pro has block operations! How could I have lived without them? Well, I'll tell you, whenever I needed to rearrange large blocks of code, I would export my code as text and open it in novatext. That's the surprisingly–pleasant–to–use 40 column text editor that comes bundled with Novaterm 9.6. It has block move/copy operations that make it convenient to search/find and rearrange large sections of text. Then I'd save it back to the text file, and reimport into Turbo Macro Pro.

As it turns out, TMP+ has block operations built in. This has greatly sped up development. Let's just call that special Commodore back arrow "escape", since it's in that spot on the keyboard. ESC-m activates the mark submenu. Press 's' to set the start mark, and 'e' to set the end mark, thus defining a block. Move the cursor to somewhere new and press ESC-b to activate the block submenu. Press 'c' to copy the block to the cursor's location, or 'm' to move it. You can also delete a whole block at a time.

There are some other things you can do that take time to become second nature. For example, a block can also be written to disk as a text file. My new practice is to write a block to disk with a standard filename of "@:clip.t". The @: tells the DOS to overwrite any existing file with the name clip.t. Thus, clip.t becomes a sort of generic clipboard. The latest clip always overwriting the previous clip, just like a modern computer's clipboard. Getting used to this convention makes it much faster and easier to copy and paste between source code files, as there is less mental overhead in coming up with a name, and then remembering what I named it. Good times.

One tool that's missing is the ability to do a backtrace. It's very handy in PHP or Javascript to be able to see where the flow of the program has gone. I can't do this in my native coding in 6502. So, I've been doing it to paper. At first, it sounds crazy, but I think this is how people used to do this sort of thing in the past. I've now got somewhere between 25 and 50 sheets of paper with notes of every description on every module in the project. Recently I've added some sheets that cover the flow trace of the booter booting up the system, launching the first app, how the main event loop distributes events to the application, and lastly how an app quits and the system deallocates memory and launches the home application again in its place. Some of this was covered in this post.

Lots more to say about development on new areas of C64 OS, but I'll leave those for another post.

C64 Luggable

While I'm dicussing my projects, I may as well give an update on where I'm at with C64 Luggable. The progress documentation on C64 Luggable is not fully up to date with where I'm at. But there is one advantage to documenting a hardware project. What has been completed tends to stay the way it is, in a way that is not true of a software project.

The front panel is almost fully complete. I had to order a second set of DB9 breakout boards, because the first set were problematic in a number of ways. The audio amplifier, as it turns out, I got two of those as well. The power jack is fully mounted, the speaker holes are finished, and the front metal plate has been dremelled out.

Front Panel, metal plate not on Front Panel at angle, with metal plate on

The back hinged door has been cut, and the hinges have been put on. So the door now officially opens and closes. I've purchased the paint and have started to apply the white base/sealer to some pieces of the chassis. One concern I have is that, due to the size and shape of the audio amplifier, once I fully screw on the front part of the chassis (with the speaker holes, and the cut out for the front I/O board), I will no longer be able to pull the front I/O board out. Even though it has been mounted to the bottom chassis in the same way as the rear I/O board.

This is truly unfortunate, because it would have been very convenient to be able to pull out and swap out the entire front I/O board after the main chassis is fully assembled. This has made me reluctant to put the front part of the chassis on, because I have to be certain that everything is as I want it to be first. And I can't start painting the outside chassis in earnest until the whole chassis is actually together.

I would love it if it had been all ready to go for World of Commodore 2017. However, after working on the project for a year, I don't want to rush the end of it and do a poor job, just to meet this deadline. I'll probably show it as it is, and next year, for sure, it will be fully together and working.

I do have a couple of small surprises about the project, which I'll show off at World of Commodore. Nothing big, but a couple of nice touches.

Commodore 8 Bit Buyer's Guide

Lastly, I just want to give an update on the Buyer's Guide.

I am super pleased with the Buyer's Guide. I just wrote about the recent updates to the guide in the introduction to the post 3D Printed Components. So I don't want to repeat myself so soon after. I'll merely point out a few new things.

The recent responsive site design changes extend to the Buyer's Guide as well. I continue to put effort in to improve the responsiveness and more changes are coming soon. The main table of contents is now responsive, changing to 2 columns of 6 categories at one breakpoint, and changing to one column at the smallest size.

The fonts in the guide also resize to make them fit better, and the overall grid layout of the items in the guide has been able to smoothly scale since the beginning.

Making the features pages responsive is under development. But speaking of features. As I tweet out random products in the Buyer's Guide, I'm adding features. In order to add a little bit of dynamic feedback to the guide, the From the Editor letter near the top of the Buyer's Guide now tells you how many feature pages are available. This is realtime read from the file system that hosts the features files, so it's always up to date. At the time of this writing it's at 11 features. Additionally, I've added a view count (yes, the good old page counter) to each feature page so I and everyone else can get a simple metric on how popular some of the products are. I am hoping that seeing these numbers and watching them grow will be an inspiration to me to keep writing.

In writing features, I realize that there are some products which I know absolutely nothing about and have not been able to find out anything about by searching the web. This has encouraged me to reach out to the vendor or the product's creator to get more information. For example, Tim Harris from Shareware Plus has actually mailed me a paper copy of the user manual for one of his products, which I will be digitizing in order to have some documentation to link to.

I would call this a net win for the Commodore community.


That's all for today. Hopefully I'll see you at World of Commodore in Toronto, this Saturday December 9 and Sunday December 10, 2017.

  1. I hate this expression, but it is a thing in our language. https://www.urbandictionary.com/define.php?term=shout-out []
November 24, 2017Software

BASIC Wedge Programs

Following up on my technical deep dive, Anatomy of a Koala Viewer, I've been thinking about how one could go about making improvements to the viewer and its UI, or lack thereof.

My thinking about this is completely aside from work on C64 OS, except that everything one learns helps in developing other projects. And I am a C64 user after all, I'm not going to just live the rest of my life inside the bubble (an OS environment) I'm creating for myself.

I was thinking about how command line interfaces normally work. Usually the shell program offers a single line editor. And whatever you type into that line, when you press return, is parsed and interpreted by the shell. Typically the first word is the name of a program and subsequent words and symbols are meant to be arguments given to the program. The shell has a path environment variable that defines a set of file system paths to be searched through when looking for the program.

Presuming that the program is found, it is loaded and run automatically and it has programmatic access to all the arguments. Shell's do get more sophisticated but we don't need to worry about that for now.1

Think about how the "command line interface" works on a C64. It is actually the BASIC interpreter. You are free to start entering a program, simply by putting a line number at the start of a line you type out. But all immediate mode lines, those lines you enter without a number at the beginning, are constrained to a limited set of BASIC commands. BASIC 2.0 has 71 commands, but only a subset of those can be used in immediate mode.

How do you load a program? You use the LOAD command. And it takes a fixed set of arguments, "filename" (this argument can include a partition number and directory path), then a device number and a relocate-supression flag. Although all of these arguments can be omitted depending on whether the defaults match what you're after. (LOAD by itself will start the computer loading the first file it finds on the tape in the datasette.)

There are extensions to this command set that can be made, both with software and hardware. For example, when you're using an IDE64, you can directly issue "CD" and "MKDIR" and so on. That's because the hardware adds those commands to the BASIC language. JiffyDOS does the same thing to add its commands, * for transfering files, @ to read the current device's command channel, / to load relocated, etc. But none of these extensions allow you to pass arguments directly to a program that was just loaded.

BASIC Wedge Programs

One reason you'd want to add an argument to a program call is the simple use–case of being able to pass a filename to that Koala Viewer. All the viewer does is show a Koala image, but you have to manually load the data yourself. And you have to load it using the correct flags, or the correct JiffyDOS load commmand, so it goes to the right place in memory. Then you run the program and it assumes you've put the correct format of data into the correct place in memory. Otherwise, you just see crap on the screen.

It works, but its as brutal as you can get.

There are lots of multi-color image file formats, as outlined in that post. They are all very similar, but different enough that the viewer program needs to know which ranges of memory hold the color data, and the bitmap data, etc. The Koala Viewer program, however, has no way of knowing what data was loaded in, (or even if any data was loaded in) because it has no concept of files. The file loading is handled entirely by BASIC and JiffyDOS, which just puts raw data into memory. That Koala Viewer in fact takes no input whatsoever.

I have a different idea.

How about this. You load and run a program, let's call it "image viewer". You do this the standard way. If you have JiffyDOS £ IMAGE VIEWER. Or if it's off in some other place then maybe like this:

	£"3//TOOLS/:IMAGE VIEWER",10

That sort of thing is pretty common to load a one file program, from a CMD device for example.

This command loads the program into memory according to its header address, so maybe it's assembled to $C000 or somewhere else convenient. And it's jumped to immediately to run it. The program has put assembly language routines into memory, and it has been run, which wedges the BASIC vectors to add new BASIC commands, but it doesn't need to implement a UI. It doesn't need to show you a full screen menu, or anything like that. It just needs to add one or more commands to BASIC.

BASIC already provides a means of inputting lines, and your code has the ability to intercept the interpretation of a BASIC line. I am going to reserve the technical details of impelemntation for a later post, when I've actually got the tool written. But the short explanation is that the ability to wedge BASIC is done by redirecting a series of vectors to your program's own routines.

By simply returning to BASIC, you continue to use BASIC (and JiffyDOS if you have it) to navigate around the file systems of the devices you have hooked up, and to view directories the way you always would from BASIC, such as by pressing F1 or with @$. When you want to view an image file, you would use a new BASIC command that the loaded image viewer has wedged in. In this case, maybe just the keyword "view" would be suitable:

	VIEW "DS9.KOA"

Besides being able to just use BASIC to navigate your drives and their file systems, the advantage here is that the program gets to see the filename. It can load the data in, according to how the data should be loaded, even if that still means loading it directly to where the file itself wants to go (as odd and archaic as that seems in the context of a modern OS.) But, because it sees that file extension, one image viewer program can handle multiple formats, like Advanced Art Studio, Amica Paint, CDU-Paint, Draz Paint, Koala etc. Or it can even handle other formats besides multi-color.

Presuming these files have standardized, identifiable elements to the file name. As long as the type is identifiable from a feature of the name, the program can look for that feature to determine the file type.

Further Considerations

Help

One complaint about command line programs is their inherent lack of visible UI. Sometimes it can be hard to remember how to use them. You load and run an image viewer as I'm describing, and instead of getting a menu or an input prompt, you get nothing. You're just back to the regular READY prompt.

Two solutions to this come to mind. The first is to immediately print out a simplified help text. Even if it's just a one–liner like:

	USAGE: VIEW "IMAGEFILE"

That would get you pretty far down the line to knowing how to use it. The second thing to do would be to have the program wedge at a minimum two commands. The main command to do whatever the program is designed to do (i.e. "view") and also a standard "help". If every program that used this model implemented the "help" command, you could just type help whenever to reveal A) if such a program is loaded in. A syntax error will tell you that the program hasn't been loaded yet. And B) It can be used at any time to remind yourself of the commands and any extra arguments or syntax. And the explicit invocation of help could also be used to show extended help, beyond what the initial one–liner showed.

Exit

How about exiting? What do you do to "exit" this type of BASIC wedge program? Well, to me there are two kinds of exit to consider. The first kind is how to get back to the READY prompt after something the image viewer takes you to a full screen bitmap. Even though the Koala Viewer accepted slapping the space bar, somehow I managed to not notice that for quite a while. I tried pressing many different keys and then just gave up assuming there was no way to exit.

In after thought, the space bar is the obvious key, but in my opinion pressing the run/stop key should always work. It is after all the "STOP" key, Commodore's equivalent of Escape. "RUN" is supposed to be the function of the key when pressed in conjunction with SHIFT. But on further thought, why shouldn't any key be pressable to return to the READY prompt?

The second kind of exit is, how to unwedge the program from BASIC? Or, how to move on from the image viewer to a different kind of program? For the most part, you don't really have to explicitly unload such a program. Pressing run/stop-restore will probably reset the BASIC vectors and thus unwedge the commands. And otherwise, the program can remain resident.

If you load a different kind of BASIC wedge program, it may well overwrite the all or part of the first one, but then it'll update the BASIC vectors to point to its own routines. And there was no explicit need to get rid of the first one.

UPDATE: November 27, 2017

Pressing run/stop–restore does not reset the BASIC vectors. I tested that last night. However, doing a jump to $E453 (SYS 58451) will run the KERNAL's routine that restores the BASIC vectors to their hardware defaults. This is better than manually setting them, because different KERNAL rom's have different vector defaults. For example, JiffyDOS points two of those vectors to its own routines. One could do this SYS manually to unwedge an installed program.

But here's what I'd suggest. When a BASIC wedge program is first run, the first thing it should do is JSR $E453 to reset the vectors, this will unwedge any previously installed program. Then, it should copy the vectors to its own variables and change the vectors to point to its own routines. But, in its routines, when forwarding control to BASIC, forward through the backed up vectors and not to hardcoded addresses. This will maintain support for the special features of alternative ROMs like JiffyDOS and its command wedge.


Other Uses

In my mind, this program model of wedging in some BASIC commands works really well for the Koala Viewer. It's a simple one–purpose program, that takes at least one argument. I can immediately think of several other programs that fit the same description that could benefit from the same paradigm.

How about an unzip utility? As PC/Mac command line unzip/gunzip shows you, you really don't need a full UI. Just load in the tool, it wedges in a command "unzip". (And help.) You navigate to a directory where a .zip/.gz file is. And enter:

	UNZIP "MYFILE.GZ"

It uses the .gz or .zip extension to know what kind of archive it is. It just immediately starts unzipping the contents and writing them to the same output device and path as the original file. As it finds files in the archive, it can print out the name of the file. When it's done, you're just back at the READY prompt, and can immediately call the command again with a different filename.

How about for a background SID file player? Load in the program, and it wedges three commands, PLAY,PAUSE and HELP. The play command can take two arguments, filename and maximum number of seconds to play back. 0, could mean to play to the natural end of the song (presuming the natural end can be found.)

	PLAY "SOMEFILE.SID",120

That loads in the file, maybe it tells you where in memory it was loaded to, wedges in to the IRQ the necessary routine to play the file, but ultimately returns you to the READY prompt with the file still playing. Want to stop the music?

	PAUSE

This command could simply unwedge the IRQ routine. Would it be compatible with every SID file? No, it would probably conflict with some of them. But on the whole it would be pretty cool.

There are side benefits to wedging these sorts of programs into BASIC. Want to make a playlist? Want to make a slide show? Just write a BASIC program that loops reading file names out of data statements, and calls the custom command. You have to load the wedged program in before loading in the BASIC program, but it'd be like this:

	10 FOR I=0TO2
	20 READ A$:PLAY A$,180,1:NEXT
	30 END
	40 DATA "SONG1.SID","SONG2.SID","SONG3.SID"

Oops, there's a gotcha here. When inside a program like this that loops, you'd want the PLAY command not to return until the song is finished. That's easy to handle. The PLAY command can take an optional third parameter. That's what the extra ,1 does after the number of seconds of playback.

You could do the same thing with an image viewer. Let BASIC work as a script language to move from image to image.

	10 FOR I=0TO2
	20 READ A$:VIEW A$:NEXT
	30 END
	40 DATA "IMAGE.KOA","IMAGE.HED","IMAGE.OCP"

Run it, you're looking at the first image, slap the long one, and boom, the next one loads in, etc.


I think it's a great idea. And to that end, I've been reading Advanced Machine Language for the Commodore 64 by Abacus Software. It devotes the whole 3rd section of the book to hacking/wedging BASIC with your custom machine language programs.

I've learned a lot so far, and a few neat things about BASIC that I never knew before. So when I get a bit further into it, I'll write another post describing my findings, and how it works out.

  1. Shells get more complicated because the output from that program can be streamed into either a file or as the input of another program. That's what makes a unix command line so powerful. []
November 22, 2017Hardware

3D Printed Components

It's been over a year since I started this blog, and this is my 48th post. And I'm still feeling like I've got a lot to talk about. So let's hope I can keep it up.

The Commodore 8 Bit Buyer's Guide has recently undergone a pretty major update. And I'm trying to produce a new Feature Page, one for each product, every business day. Sometimes that's not possible, because on some day's I've already got a super busy schedule and simply can't fit it in. But that is the aggressive schedule I'm trying to hold myself to. In order to keep myself motivated I'm tweeting (#8bitbuyer) every day about one product, pulled at random, from the guide. Whatever product I tweet about, that's the product for which I write the feature page. You can follow this stream on Twitter here: https://twitter.com/hashtag/8bitbuyer

The guide is now up to 175 products, projects and kits, up from just over a hundred. And these come from 25 vendors, up from just 15. It isn't that this many products just spontaneously sprang into existence, but that I keep digging deeper and expanding the set of available products to buy.

For example, I dug into the catalog at Retro Innovations, and pulled out cables, adapters and rom chips, and have listed them. I discovered Poly.Play and all the stuff they carry and listed that. And I discovered some older projects like the 64JPX adapters that are still commercially available and I listed them. Among numerous others. If it's available, if you can pull out your credit card, place an order and have a thing for your Commodore 8-Bit, then I want you to be able to find it in the Buyer's Guide.

There are some caveats to that. I don't want to list used products of limited stock that people are selling off from their personal collections. You can generically use ebay for that sort of thing. If someone came into the possession of 25 or more Commodore REUs, but they were technically all used, I'd probably still list them. What I'm saying is that, as the editor of the guide, it's a judgement call what I think will benefit people to be able to find.


3D Printing

All the above said, I had a tough time with deciding what I should do about the relatively new phenomenon of 3D printing.

By their very nature there is no stock of 3D printed products waiting to be sold off. Someone somewhere has designed a 3D model and upload the file to a publicly accessible catalog. And there are ways you can turn that into a product you can hold in your hand. I'll get into the details of how to do that below.

One problem that I wrestled with is that there is a virtually unlimited number of such 3D printable products, and their lack of stock means once they're in the catalog, there seems little reason to pull them from the catalog. And, doesn't that present a philosophical problem? Won't simple 3D printable components eventually overtake everything else? This was my concern.

Someone on IRC suggested some 3D printable products and gave me links to items on thingiverse.com. I decided to peruse Thingiverse for everything I could find that is C64 related and just see what I could come up with. What I found was about 12 or 13 things. But immediately didn't see the utility of listing a couple of them. They simply fell below my radar of what I thought would provide enough value that it merited a place in the guide. And I reduced the list to just 10 things.

The philosophical problem is not yet a real problem. There are in reality only 10 things, not the theoretical infinite number of things. And the truth is, I am a person with judgement, and if I feel that the 3D printed options are getting a bit out of hand and not providing the right value, I'll cull them as the need arises.

I had one other problem though. Thingiverse is not a supplier. They're just an online catalog with pictures and related–links, and they host the 3D model files. My concern for the Buyer's Guide is that I want you the Commodore enthusiast to be able to get your hands on the products in as practical, straightforward and realistic a way as possible. So, let's get real, how do you actually get the real, physical, 3D printable component?

How to Acquire a 3D Printable Component

As it turns out, it's a hell of a lot easier than I thought it was going to be. I was pleasantly surprised. And I'm glad to be able to pass on this news and the details of my experience to you.

The Commodore 8 Bit Buyer's Guide provides a link to the component's page at thingiverse (or other online 3D printing catalog). Like this: https://www.thingiverse.com/thing:1561772 Which takes you to a page that looks something like this:

Screenshot of Thingiverse.com's C64c Keyboard Brackets page

You don't have to have a sign in, you don't even need an account, you don't need to give them your email address. You can just click that blue "Download All Files" button at the top of the rightside column. It downloads a zip file that contains a standard structure. A couple of text files for readme, licence and attribution information. A folder called images with a couple of rendered pictures of what the item is. And a "files" folder which contains the actual model file or files. In the case of the C64c Keyboard Brackets, there are actually two components, the left and right brackets. So this zip package has 3 files, one for the left bracket one for the right, plus a third that contains the models for left and right brackets together in a single file.

Next, you need to find a source for printing the model files. Thingiverse recommends two websites that help you find a source and connect you with that source. I picked one of them at random, treatstock.com. I can't speak to how the others work, but my experience with treatstock.com was seamless and easy.

You do have to make an account with them, because you're going to be placing an order, so your account will maintain your order history and also facilitates communications between you and the source that actually prints your item. Fortunately, creating an account was dead simple. Basically just a name and email address. You don't have to provide any credit card information and creating an account is free. They get their money by taking a cut of whatever you pay when you decide to actually place an order and get an item printed up.

TreatStock makes the process of ordering incredibly easy. There are buttons on the main page that say, "Order 3D Print." That's all you have to do to get started. Their main page also has a simple overview of the entire process end–to–end. Here's what they say:

  1. Upload Model
    • Upload your files
    • Find 3D models in our catalog
    • Hire a designer to help bring your ideas to life


  2. Select a Service
    • Choose from over 100 materials and colors
    • Compare prices, reviews and locations
    • Place an order with the best service for your project


  3. Order Delivery
    • Track the production process of your order
    • Our friendly customer support team is always here to help
    • Fast delivery within 5 days with free shipping insurance

It is in fact even easier than they describe. Step 1, upload your model. You don't need to find a model in their catalog, and you definitely don't need a designer to help you bring your own ideas to life. The 8 Bit Buyer's Guide already gave you the page whence you downloaded the model files for the item you want. Uploading is a snap, in my case, I only uploaded the one file which contains both left and right brackets.

Selecting a service is also very easy. Once you've uploaded the file, they know what its dimensions are. Some services are only going to be capable of producing item within a limited size range. The services that can't produce your item are automatically removed from the list.

There are a choice of materials, which is initially a bit daunting, but they do have simple description text beside each choice to tell you how they're different. And they are generally ordered by price, least expensive first. I went with ABS Plastic, second cheapest, very durable and doesn't biodegrade. And you can also choose your color. The C64 parts are mostly monochromatic so whichever you choose, the whole thing will be produced in that one color.

There is a small gotcha to consider. Varying the material and color changes the set of available sources. The reason for this should be obvious. Some sources simply lack the ability to print in certain materials or to produce certain colors. After toggling through a variety of color options, I discovered that in ABS Plastic, with some colors, there was a source available right here in my home town of Kingston Ontario. The next nearest sources were Ottawa, Montreal, Toronto, Vancouver etc. Other Canadian cities. Especially because these are an internal component, I would gladly forgo some color options, for the ability to have it locally printed where I could drive to pick it up.

I placed an order with the local Kingston source. It cost $10.85. A tad pricey, but not outrageous. We are after all now talking about production runs of one at a time. Before 3D printing, making one thing would be prohibitively expensive for all but companies that were prototyping in advance of mass production.

Payment was handled via my pre-existing PayPal account. So that was a snap. The site then tells you when the source has completed producing the part. I expected it would take a few days, but the source got back to me before the end of the business day. I used the site to send him a message, saying I'd come pick it up after work. TreatStock gives you the address and renders it to a map to show you where the source is located. Zooming in on the map, I discovered that the Kingston source I used is just a house, in the middle of a new residential division! It was just a 10 minute drive up the road.

When I arrived, I went up and knocked on his front door. The parts were ready and waiting. And I didn't have to come with cash because they'd already been paid for. The guy just handed me a set of keyboard brackets for my C64c. Amazing. It's the next best thing to having a replicator. Given the absolute ease with which I went through the process on my very first try, I am more confident than ever recommending printable 3D components in the Buyer's Guide.

Right bracket, bottom view Left bracket, bottom view

Quality

3D prints are pretty cool. You get a thing, and it's like it just comes out of nowhere. For designers, it must be totally amazing.

However, 3D printing is not perfect. If you're expecting to get what you would get from an injection mold, you'll be disappointed. The printer prints in layers. And the layers are stuck together using some chemical magic that I'm not even trying to understand. The parts are sturdy, there is no doubt about that. For internal components, such as these brackets, there is virtually no downside. They're the right size, they add a splash of color when you open the chassis, and they are more than robust enough to hold up your keyboard.

I'm a bit more reluctant to recommend 3D prints for enclosures. Simply because the quality is below what we would normally expect. However, if that doesn't bother you, and if you'd rather have a 3D Printed case than no case at all, then by all means, that's why people have taken the time to design those models.

Feel free to ask me any questions, either in the comments on this article, or in the comments on any of the feature pages that are being built out for these components.

November 15, 2017Technical Deep Dive

Anatomy of a Koala Viewer

After 10 years away from the Commodore scene, the most shocking part of coming back was not the speed of a 1 Mhz clock, or even a 320x200 16-color display. The most shocking part is the essential interaction model. And this was my inspiration to start working on C64 OS. GEOS, as I've mentioned in many of my posts, comes much closer to what you would expect from a modern computer. But even it has all kinds of oddities that show its age. And unfortunately, its fully bitmapped UI is just too damn slow to get used to. Using a C64's READY prompt to do ordinary computing tasks is truly a blast from my adolescent past. But returning after a few years away and learning all over again how it works, it feels like a totally foreign world.

Nothing better exemplifies this than my most recent experience working with koala image files and a koala viewer program. So I'm going to deep dive on exactly what I mean by totally foreign and discuss how C64 OS will work to make the whole model more modern.

What is Koala?

Koala was a suite of drawing technologies for a number of computer platforms in the 80s. You can read all about it on this Wikipedia article. It was developed by a company called Audio Light. The suite consists of the KoalaPad which is a drawing tablet and KoalaPainter which is an accompanying art/graphics program. The program, on a C64, works with Koala format image files. The KoalaPainter program can load existing files, which you can then edit using a wide range of tools like fills, boxes, lines, ellipses, and brushes with different patterns. Then you can save your work to disk, again in the Koala image file format.

That's great! It actually sounds pretty modern when described like that. It sounds like how photoshop works. The quality of the images and assortment of tools, of course scaled for the age of the machine, but it sounds like a pretty standard modern way that a computer works. You have an application, which you load and run, it has a graphical user interface, you pick a file from a directory listing from a harddrive, it loads the file in, you work on it, save it back to disk. You can later copy the image files around individually, back them up, share with your friends, upload to the internet (or to a BBS back in the day), and so on. Very modern.

The KoalaPad drawing tablet

As with any image file format, especially if the files are going to be distributed, the people who receive the image are most likely not artists with intentions of manipulating the image. They are normals like us who just want to look at the beautiful artwork that has been produced by others far more talented than we are. And so to view the image files it doesn't make sense to have to load the entire Koala graphics editing program. Not to mention the fact that the original full graphics editing software likely cost money, as well it should.

What you want then is to have a free viewer that is small and quick to load, which can display the image files created by the full editor to people who just want to look at them. Again, though, this is a very modern concept. You don't have to own photoshop, nor launch photoshop, to look at an image file that was produced by it.1

I like to use a Mac to convert images (JPEGs, PNGs, etc.) to Koala format, (exactly what that format is I'll mention below.) And I also plan to have a network service which will fetch images from URLs and convert them to Koala format on–the–fly for C64s to be able to browse the web, in a more meaningful way, via proxy. A viewer is therefore far more important to me than the original KoalaPainter program. And so I found a simple koala viewer online. It's just 4 blocks (~1 KB) on disk. But… how do you use it? How does it work? Where does the modern end and the antiquity begin?

How is a Koala image formatted?

First, let's talk about the graphic formats we're all accustomed to. When you examine a JPEG, or a PNG, or a GIF, you actually find that the internal structure and layout of the data on the disk—even when they represent the same picture on screen—is radically irreconcilably different from format to format. Why is that? Well, there are proximate reasons and ultimate reasons. I like thinking in terms of the latter. The ultimate reason is because the graphics capabilities of modern video hardware long ago outpaced the increases in storage and load speed from harddisk or network. I'll explain.

A MacBook Pro, today, has a pixel resolution of 2560x1600, and each pixel can show at least 24 bits of color. 8 bits of red, 8 bits of green and 8 bits of blue for every pixel. That's 3 bytes of data per pixel. 2560 times 1600 is 4,096,000 (4 MILLION) pixels, times 3 bytes each is 12,288,000 bytes. That's 12 megabytes of raw data for just a single image that fills the screen. And we all know that many images are in fact larger than the screen and software allows the user to zoom in or out or pan around to see the whole thing. It is not at all practical or economical to actually store all those megabytes for one image.

Therefore, each of the common graphics formats, JPEG, PNG, GIF, etc. use different compression techniques (sometimes lossy) optimized for different general use cases. Each format sacrifices something, sometimes something that is difficult to perceive, in order to dramatically decrease the necessary storage requirements on disk. The task then of the viewer or the decoder, as they're more properly called now, is to uncompress the data on disk (or from a network) and reconstitute the full bitmap data in memory, whence the video hardware actually outputs the data to the screen.

And herein lies the first big difference. On a C64, 16 colors is 4 bits and 320x200 is 64,000 pixels. That would mean at least 32 kilobytes of storage for a full screen of image, but storing and manipulating color data alternately in upper and lower nybbles is a pain. So practically speaking even though 16 colors can be represented with 4 bits, if each color value is given its own byte that doubles the memory storage requirement. So, with that in mind, 64,000 pixels times one byte per pixel takes up almost 64 kilobytes in memory. But the C64 only has 64K of memory.

For this reason, the design of the VIC-II chip relieves it from needing to assign one color to each and every individual pixel. The VIC-II can show a fullscreen image, and if the artist is clever, it can be hard to notice any limitations on colors per pixel, even though there very much are. But if you think about what that means, the VIC-II chip embeds a special type of compression directly into its native display modes. The VIC-II in fact, as we all know, has several display modes, which in a sense can be thought of as multiple native compression formats. Koala makes use of the format called "Multi-Color". This is not a post about the details of how multi-color is structured, but you can read all about it and see some examples here.

The point is, only around 10 kilobytes of memory is required for the VIC-II to show a fullscreen colored image. And what is saved to disk is also very close to 10K, just a few extra bytes are in a Koala image file on disk than the raw data in memory. It's not a bad compression format at all. If you convert a 10K Koala image to PNG, it becomes 20K. If you convert to GIF, it becomes 14K. 10K is pretty good. It becomes apparent, especially if you send the file over to a PC or Mac that doesn't have the same colors–per–pixel limitations, that the on–disk format is essentially just a full bitmap with a rather unique compression scheme and cooresponding set of limitations. In fact, the Mac has no difficulty at all viewing Koala (and other) C64 image formats. You just need the right decoder. You can download and install a quicklook plugin with support for various C64 formats here. This plugin decompresses a Koala image essentially the same way another plugin decompresses a GIF, and converts it to raw bitmap data native to the Mac's video hardware.

And so the format of a Koala image is effectively just a dump of memory. The bitmap and color memory regions are not contiguous in a C64, however, so in a Koala image those three dumped regions are packed together, plus a few extra bytes. And that's it. On disk, a Koala image file is already in what amounts to a compressed format. So it's quick to load and small to store. But, it's better than that, because the viewer program doesn't actually need to do any work decompressing and converting the data to the native format of the video hardware. Because the video hardware interprets the on–disk compression scheme natively.2

This is very different than modern graphics formats. There are other multi-color mode image formats for the C64, you can read all about them and their technicals here. What is so telling is that it only takes a couple of lines to describe the difference between each of these formats. Because they are merely different arbitrary orders of appending together the various dumped regions of memory. Some put color memory first before bitmap memory, some after, some put the background color at the beginning, some at the end, some between the bitmap and color memory, etc. But large swaths of the files from two of these different formats will be exactly the same: the format of the VIC-II's multi-color mode, either bitmap or color data.

Where else do things differ?

Let's start with how you actually use the Koala Viewer that I found. The viewer is so simple it almost boggles the modern mind. It has a one-button user interface, and I mean, a one keyboard button UI. You load it, then you run it, and by default a bunch of crap displays on the screen. Nothing is responsive, there is no anything, except crap on the screen. At first, I would just reset the computer to get out of the viewer. There goes my uptime! (A concept that does not exist in the Commodore 8-bit world.) Power cycling or resetting the machine is a common user interaction model for exiting a program.

It wasn't until I disassembled the program to figure out how it worked that I discovered you can press the space bar to exit the program (or press fire on a joystick in port 1.) It's really that simple. Run the program, press space to exit the program. The end. On afterthought I should have thought to slap the long one, since that is indeed such a common interaction in games and demos that it is more or less a C64 standard. Disassembling the program also got me to figure out how it is you actually use this viewer program to, you know, view something. But I'll return to analyze the code a bit later. First, let's just pretend we knew all along how to use it.

Here's what you do. You first have to use commands from the READY prompt to load the image data into memory. Then you load and run the viewer, and it knows where to look in memory to find that image data. This explains why you just see a bunch of crap if you load the viewer but you haven't first loaded in any image data. The viewer just happily shows you whatever left over crap was in that place in memory from the last thing you ran. The VIC-II happily interprets something, anything, even executable code, as though it were image data. This is all so very very different than anything you'd find on a modern computer. It's just so low level. But it is fun, it's fun to feel yourself so close to the bare metal.3

Loading Koala image data Loading Koala viewer

You'll notice that even though a Koala image file is technically data, as opposed to executable code, it is stored on disk as a PRG type file. This means it can be loaded. But the C64 has two kinds of loads. You can load a PRG relocated to $0801, which is the default, or you can use a relocate flag that prevents the relocation and will instead load the data to the address specified in the first two bytes of the file on disk. That two byte header is not loaded into memory, it is loaded from disk, used to figure out where the following data should go and is discarded. Fortunately, JiffyDOS includes 4 different load commands: (/,^,% and £). Frontslash, up-arrow, percent sign, and British pound sign. These commands alone feel incredibly ancient. They are completely arbitrary one-character BASIC commands. They're so unusual, modern North American keyboards don't even have 2 of these 4 symbols on their keys. They do the following, respectively:

  • Load relocated to $0801 but do not run,
  • Load relocated to $0801 and run automatically,4
  • Load to header–specified address but do not run, and
  • Load to header–specified address and run (by jumping to wherever it was loaded.)

For Koala images, we have to use the percent sign (%) load. This puts the data in memory, but does not attempt to jump to it (thank god, because it's not executable.) Next, we load the viewer. In the screenshot above I loaded it with frontslash (/), so that I could list it to see what we can see before it runs. What we see is the standard basic preamble, SYS 2061, a BASIC command to jump to the immediately following assembly code. Pretty standard way to get assembly code to be loadable/runnable with the "/" and "^" JiffyDOS commands, or ye ol' standard LOAD"PROGRAM",8. Finally, let's run the viewer!

Viewing a Koala Image of DS9 After exiting the Koala Viewer to the READY prompt

Oh. My. Gawd. It's Deep Space Nine!! I'm so excited. But, seriously, it's pretty cool right? It worked. After we enjoy the image for a while we slap the long one to exit the viewer. Strangely though, we're taken back to the READY prompt but something is not quite fully restored. The majority of text on the screen is white rather than light blue. Only the final READY prompt is light blue, and that's because it was drawn to screen by the KERNAL after the program exited.

It should start to feel more and more as though this entire experience is straight out of a computer era that ended decades ago. How is it even possible that we could use the JiffyDOS un-relocated load command to get the image data into memory? For that, we need to examine the file itself. It also takes time to find equivalent tools on the C64 that you might be used to having on a Mac or PC. This is not a pick–on, of course, once you start living in the Commodore world for some time, you do find these tools, and you start to know intuitively which tools you need to use to accomplish common simple tasks.

DraCopy is a great little utility released by Draco in 2009. You can download it here. Besides being a useful 2-panel file system navigator and file copier, it has a built–in HEX viewer. When you don't have a HEX viewer, and you need a HEX viewer, finally finding one is like a breath of fresh air.

Using DraCopy to find a Koala image file Using DraCopy to HEX dump a Koala image file

And there it is. We examine ds9.koa with the HEX viewer and find that the first two bytes are $00 and $60. That's little endian, least significant byte first, for the memory address $6000. When we do the JiffyDOS un-relocated load, it is reading that address from the image file, and using it to know that it should put the image data into the fixed memory address $6000... and up.

Please, take a moment to stop and think about what this means. The image file itself has hardcoded into it a fixed memory address whither the data should be loaded. Unthinkably ancient. Can you even imagine, a JPEG file embeds a memory address that tells the computer where in memory this JPEG data ought to be loaded?! How presumptuous. How parochial and shortsighted. Why should it ever be the decision of the data file where it itself should go in memory? That data file has no idea about what else the computer might be running or using that memory for.

Why it worked this way actually makes sense, from the original KoalaPainter program's point of view. The KoalaPainter program put itself into memory, and intentionally left a space in memory where the graphics data should be loaded to. $6000 to $8710. (More on this particular location when we examine the code of the viewer.) Next, everyone who has coded anything with files on a C64 knows that the KERNAL can do a LOAD much faster than if you loop over repeated calls to CHRIN, reading one byte at a time. It was an eminently reasonable decision for KoalaPainter to save the image data as a PRG with a header address of exactly where the program wants to load the data, for itself.

But from a viewer perspective it makes no sense. The viewer program is not the original KoalaPainter program. And who knows how big it may be or what areas of memory it may occupy, and inside the context of an OS with a memory manager it is even more obscene. But it is what it is, and this viewer program is clearly hardcoded to look for the image data starting at $6000. Here's the thing though, if JiffyDOS handles loading the data into memory, and the data is already formatted as expected by a native VIC-II display mode, then what is it that a Koala Viewer actually has to do?

Digging into the code of a Koala Viewer

I manually transcribed this code to a Gist, from the photographs of an ML monitor's disassembly. All the numbers are in HEX. This isn't typically the style I would use when writing code, and labels are not used, rather hard memory addresses are used because that's how the disassembler produced the output.

Obviously I added the comments. I'm not super familiar with VIC-II programming, so why it has to mess with the VIC's raster interrupt, I truly do not know. Here's the code, let's dig in.

The code consists of three main parts. The first part, lines 1 to 44 are the "main" program. The second part is a subroutine called by the main program to configure the VIC and CIA2, and move data from where it was loaded in memory to places the VIC can use it. And the last part is a short routine wedged into the KERNAL's IRQ service routine, which scans the keyboard and allows the main program to progress past viewing the image, to the clean up part and exit.

So, main program, in detail. Mask CPU interrupts so we don't get disturbed while setting up memory and the VIC. Then it also masks CIA1's interrupt generator, not 100% sure why this is necessary since the CPU is already ignoring interrupts.

Lines 8 to 11 are what wedge this program's IRQ routine into the KERNAL's IRQ service routine. The KERNAL hops through a vector at $0314/$0315.

Next, it turns off the VIC's display. This causes the screen to blank out. I believe that's because it will look cleaner to move all the graphics and color data into place while the screen is off. And the screen can be turned back on when everything is ready to go. At this time the border color is also set to black. The border color must not be specified by the Koala image format. That's a shame, if you ask me, it's an important part of the image. Especially if you wanted the edges of the image to blend seamlessly into the border.

At line 17, the main program calls the only proper subroutine. So let's go check out what that subroutine does.

The first thing it does, from lines 50 to 72, is copy 2 kilobytes of data. The way it does this is very cool. It actually loops just 256 times, one complete cycle of the X register from $00 to $00 counting backwards. On each loop, it copies 8 regions in parallel. The first 4 regions are the 1K (256 * 4 = 1024 bytes) of Koala data into Color Memory. The second 4 regions are 1K that are copied from Koala data into "Screen" Matrix Memory. In the Multi-Color mode, Screen Matrix Memory is used to hold the extended color data. Both upper and lower nybbles are used to hold 2 additional colors per 8x8 pixel cell.

Interestingly, that's all the memory that is ever copied by this program. What about the 8 kilobytes of bitmap data? Now it becomes clear to us why the Koala image format, as insane as it seems, specifies where it should be loaded in memory. It's loaded to $6000 you'll recall. Let's dig into the VIC for a second.

The VIC-II chip has 14 address lines, not 16. 14 bits can address from $0000 to $3FFF, or 0 to 16383. So, the VIC can see 16K of memory at a time. The most significant 2 bits of addressing are supplied by the CIA2's Port A bit0 and bit1. This means that configuring CIA2 allows you to choose which of 4 blocks of 16K the VIC II "sees." In the C64's main addressing space those ranges are:

  • $0000 - $3FFF (CIA2's $11)
  • $4000 - $7FFF (CIA2's $10)
  • $8000 - $BFFF (CIA2's $01)
  • $C000 - $FFFF (CIA2's $00)5

Within a 16K block, there are two 8K chunks. From the VICs perspective an upper 8K and a lower 8K. Bitmap data is just shy of 8K (8 * 1000 = 8000, but 8K is 8 * 1024 = 8192). The VIC can be configured to read bitmap data out of either of these 8K regions, upper or lower, but always aligned to these two regions. It cannot for example arbitrarily read an 8K bitmap that's shifted by just 2K or something like that. If you divide the 2nd bank in half, the lower 8K goes from $4000 to $5FFF, and the upper 8K goes from $6000 to $7FFF. Bingo. $6000 is the start of an 8K bank whence the VIC-II can directly read a bitmap.

So, what's neat about loading a Koala, is that the JiffyDOS (%)-command, (load to the header address,) without any other viewer or decoder program's involvement, literally loads data directly off the disk and straight into video memory exactly where it needs to be for the VIC to display it. Crazy low level. But, hey, it's hard to imagine how it could be more efficient.

Moving on now.

Lines 74 to 94. The VIC's background color is set, which is just one byte from the Koala data into the VIC's background color register. Next, in short order, the VIC's raster interrupt is masked (or unmasked?) I'm not sure what this is for. The byte inside the infinite loop code in the main program is set so the loop will loop, not sure why this had to be done here and not in the main part of the program itself. CIA1's interrupts are, started? Or touched, not sure why this has to be done. Then the VIC's raster counter is set. Next the VIC's memory pointers are configured. This is what tells the VIC to get the bitmap from the upper 8K, and where in the lower 8K screen memory (1K, used in this mode for extended color info) should be found. And the write to DD00 is CIA2, which configures which of the four 16K banks the VIC should see. Lastly, multi-color mode is turned on and the subroutine ends, returning to the main program.

Back in the main program, now that all the color data has been moved into place and the VIC's registers have been configured, the VIC's display is reenabled. And lo and behold the image appears.

CPU interrupts are unmasked, and at lines 24 and 25 the main program just goes into a hard infinite loop. This to me is one of the things that makes this program—and the C64 and its lack of modern OS—feel the absolute oldest. Literally, the program goes into an infinite loop that is just 4 bytes long, 2 opcodes and 2 data bytes. It loads an immediate value of #01 into the accumulator. Then if the value loaded is not zero, which, duh it's not, it branches right back into loading the accumulator again. Argh! While you look at the image, that beautiful 1 Mhz CPU spins like an insane idiot doing absolutely nothing. Almost nothing. Modern computers, and C64 OS will never do absolutely nothing like this.

Almost but not quite nothing. Because there is still the interrupt service routine. So let's look at that. While the main program is in a tight infinite loop, the routine at line 100 to 110 is being called 60 times a second. The first thing it does is clear the VIC's raster interrupt. I'm not super familiar with raster interrupts, but actually, it looks like the CIA1 might not be the one generating the interrupts this time. It's the VIC's raster that generates the interrupts. Not sure why they did it this way. But in any case, reading from DC01 checks the column that the space key, and the control port 1's fire button is in. You can read my recent post about How the C64 Keyboard Works for more detail on why this works.

If the space key is not held down, it skips over the next two lines and returns into the KERNAL's usual IRQ routine. However, if space key is held down, it writes a #00 directly into $0832, that's the address that holds the immediate argument inside the main program's infinite loop! Thus, it breaks the loop and allows the main program to proceed into its second half.

The main program, following the infinite loop, is from lines 27 to 44. Everything is done between a pair of SEI/CLI, so that interrupts are masked at the start and unmasked at the end. Then it does three quick subroutine calls into the KERNAL. These initialize the SID, CIA, VIC and IRQ, and reinitialize the I/O vectors (which unwedges the IRQ subroutine previously discussed.)

The only problem with returning to BASIC immediately, is that the Multi-Color Mode uses color memory for part of the image's color map. But when the VIC is put back into character mode that color memory is used for the colors of the characters. So color memory needs to be reset. But, what to reset it to? In this case, a short loop (again in parallel, 4 regions are being set per loop,) color memory is filled with #01... white. And that explains why all the characters on the screen go white after the program returns to BASIC. The reason is because color memory is shared between character mode and bitmap mode, and when the Koala data is copied into color memory, it clobbers whatever was in there. Then it picks white arbitrarily to put back in. It could put light blue back in and most people wouldn't notice, but what would be even better is if it made a backup of color memory, and then restored from that backup.

So that's the whole process, in as deep detail as I care to go.

Screenshots of the disassembly in an ML Monitor

Some Thoughts About Modernization

Memory

Inside the context of an OS, memory cannot just be clobbered arbitrarily. It just can't be. What if the ethernet driver has allocated some space to buffer some incoming data, and you don't even know where those allocations have come from? Memory in the middle of the computer, especially around $6000 cannot just be blindly filled up with data that is loaded straight from disk. Even if that space were available, you'd need to at least mark it now as occupied, so that future allocations don't come out of it.

The VIC-II shares memory with main system memory. And a bitmap can only be in one of 8 possible locations. In my mind, the ideal place for bitmap data is under the KERNAL ROM. The 4th 16K block that the VIC can see goes from $C000 to $FFFF. That range, in C64 OS, is never allocated by the memory manager. It's too messy to use for arbitrary purposes. $C000 to $CFFF is used for C64 OS code, so that's never available. $D000 to $DFFF is under I/O, and is used in precision ways by C64 OS for storing system related data, which it accesses by patching out I/O when it needs to. And in C64 OS the KERNAL ROM is used and usually patched in, which covers the remaining 8K, $E000 to $FFFF.

However, the VIC doesn't always see what the CPU sees. Even when the KERNAL ROM is patched in for the CPU, when the CPU writes to $E000-$FFFF those writes go into the underlying RAM. And when the VIC reads from that range, it always reads from RAM. That's so great. That means with the KERNAL ROM patched in, code executing within that range can write data into that same range and can be affecting what the VIC is showing in realtime. As long as your code needs the KERNAL, and also wants to show bitmap data, storing the bitmap data under the KERNAL sounds like a no brainer.

If bitmap data is the 8K block under the KERNAL, then screen matrix memory can be configured to be found somewhere inside the 8K block from $C000 to $DFFF. In C64 OS, the layout of $D000 to $DFFF is managed manually, much the way the system workspace is managed manually from $0000 to $07FF. A thousand locations can simply be reserved under I/O space, just for extended color info for multi color bitmaps. No need to dynamically allocate it, that's just where it goes.

What does this mean for Koala image files? Or all the other multi-color format variants? Like, Advanced Art Studio, Amica Paint, CDU-Paint, Draz Paint, etc. When reading these from disk, they should be read more along the lines of how C Structures are read into memory. The headers on a file are prescribed sizes. You declare a structure composed of a certain number of bytes, and you allocate space for that structure. Then you populate the structure by loading a fixed number of bytes from the file into the memory allocated for the structure. And then, you can access all the properties of the struct to know more about the file contents.

A loader for Koala would start by loading 8000 bytes into $E000. Then another load operation would load 1000 bytes into where screen matrix memory will be assigned in $Dxxx. Then, if you want to have color memory preserved, perhaps another 1000 byte block under $Dxxx should be managed by C64 OS as the color memory buffer. But the point is that you have to load the data in chunks.

File Access

It is much faster to use the KERNAL's LOAD routine rather than looping over CHRIN and putting the bytes where you want them to go. It's also easier to use, it requires less code. However, LOAD is brutally uncontrolled. It must load the entire file, and it must all go to one contiguous range of memory. But that's just not going to cut it.

C64 OS has a File module, which not only works with C64 OS File References, but also offers the standard FOPEN, FREAD, FWRITE and FCLOSE. This makes it very easy to load in chunks. You call FOPEN with a pointer to a File Reference (which will get created for you using the C64 OS file manager,) then you call FREAD with a pointer to the open File Reference, a pointer to where the data should go, and a 16-bit length to read. And that's all you have to do. FREAD will put the data in the buffer. It handles errors, it handles looping, it handles hiding the mouse cursor and other implementational details.

Will FREAD be slower than the KERNAL's LOAD? Yes, by definition it has to be, as it uses the KERNAL under the hood. But, on the other hand, we're not loading from a 1541 anymore, (hopefully.) We're loading from a CMD-HD or an SD2IEC, or better yet from an IDE64 or a 1541 Ultimate.

More Sophisticated Data Structures

All of these image formats, (20 of them hires and multi-color, not counting the interlaced and more advanced formats) they all just assume the image is exactly 320x200. And that nothing whatsoever will appear on the screen at the same time as the image.

Storing data in a format native to the VIC-II is fine. But, what about images that are bigger than the 320x200? What about images that are smaller? Smaller images make a lot of sense to me, especially if you've got a background scene, say, already loaded, and you want to load in a small rectangular area to overlay on top of the scene. I can imagine tonnes of uses for this in games, or animation sequences or presentation software. Or even just to overlay some UI controls on top of a bitmapped image. Instead of having no UI, but to slap the space bar, in C64 OS the mouse will be active, what about being able to load in a "close" X, or back and forward arrows and display them on the bitmap screen? They could be loaded directly from disk when you want to show them. The possibilities are endless.

A more modern C64 image format doesn't need to abandon the idea that the data is in a VIC-II friendly format. But what it should have is a standard header that can be loaded first into a struct somewhere. That could specify the dimensions of the image data in 8x8 cells, the data format (hires or multi-color), and the offset into the file for the color and bitmaps sections. It could even specify where on the screen it ought to display, if that made sense. But it wouldn't do that with hardcoded memory addresses, but instead with offsets from a virtual top left corner.

Concluding thoughts

Going into a tight infinite loop as your program's main behavior can't be how it works. I understand why this viewer does that. I mean, what else is it supposed to do? Where else ought the CPU to execute? It's got to execute something, and with the image statically displaying on screen, and the IRQ routine checking for the space bar, there isn't really anything else to do.

But, even though C64 OS isn't "multi-tasking", it's still got system level code that is capable of processing events. After setting up the image to display, in C64 OS, your code would just return. And it's the OS's own main event loop that will do the infinite looping. Meanwhile maintaining the ability to respond to timers, pass mouse and key command events to your code, process incoming network data, and whatever else gets added in future versions of the OS.

Okay, that was long. But it's good to go into this stuff. Leave your thoughts in the comments section.

  1. It's not a perfect analogy because modern image editors store extra data besides the final image. Such as layers, groups, tags and selections. So they export a "flattened" version of the image. Whereas Koala edits the same flat file that is ultimately viewed. But, it doesn't affect my argument much. []
  2. That said, there are some formats that are compressed on disk further. Such as RLE compressed Koala format. These require the viewer to use a decompression routine. However, such schemes are very primitive. []
  3. I wouldn't even know where to begin to actually put data, directly, on to the screen on a Mac. Most nerds would ask, "Why would you ever want to do that?" Just out of curiousity. My guess is that you have to be some sort of super privileged low level OS process to be able to put things directly into video memory. It's so arcane it seems that no one really knows how to do it. []
  4. Note, of course, that these "relocations" don't modify the code. If the code is not intentionally designed to be runnable from $0801 it will crash. The relocate is a simple shift of position in memory. Real code relocation is much more complicated. Although it is possible it requires a special binary file format and a special assembler. See here, for example. []
  5. It tripped me up for a minute, because the program writes #$02 to DD00, to me, that looks like it should be the 3rd 16K bank, 00, 01, 10, 11. 0,1,2,3, right? But I checked the C64 schematics, and indeed, the V14, V15 address lines coming off the CIA2 have a bar above them. That means they're inverted, for whatever reason. So the correct order is 11,10,01,00 for banks 0,1,2,3. Thus #$02 really does give you the second bank not the 3rd. []
October 31, 2017Programming Theory

Passing Inline Arguments

Happy Hallowe'en C64 enthusiasts. This post is about passing inline arguments to subroutines in 6502. Hopefully it won't be too spoooky.

When I first began learning 6502 ASM, many years ago, one of the issues that vexed and confused me the most was how to pass arguments to subroutines. Arguments are basically, the data that the subroutine is supposed to work on. I'd been programming in Javascript, and was learning C, and I knew that functions could take an arbitrary set of arguments, but at the time I wasn't clear on how those arguments were actually passed and accessed by the function. And when I started looking into the KERNAL, as you'll see if you read up on C64 KERNAL documentation,1 all of the calls make use of processor registers to pass data to the routines.

This has some advantages and some disadvantages which I'll talk about below. But the most obvious problem is that the 6502 only has 3 general purpose registers, .A, .X and .Y, plus some flags in the Status Register can be used as single bit arguments. The carry is most frequently used in passing data to and from a subroutine. Each of the registers is only 1 byte (8 bits), so using registers you're limited to a total of 3 bytes of arguments plus maybe a couple of additional bits. It is common when writing C (or Javascript) functions to have 5 or 6 arguments some of which are pointers that need at least 2 bytes each. How can we handle this in 6502 ASM? How does C handle this? That's what this post is about.

The KERNAL and Register Arguments

As limiting as just 3 bytes of arguments might seem, there are a lot of routines one could write in this world that can get by with so few. As mentioned above, the KERNAL uses processor registers exclusively for its arguments. There are times when three bytes aren't enough. But to handle this the KERNAL simply requires you to call one or more preparatory routines first.

The chief example being opening a file for read. In a C program this can be done with a single function, but in the KERNAL you have to supply several arguments. A Logical File Number (1 byte), a device number (1 byte), a secondary address or channel number (1 byte), a pointer to a filename string (2 bytes), plus the length of the string (1 byte). Count 'em up, that's 6 bytes. The KERNAL therefore requires you to call SETLFS with the first three arguments, and then SETNAM with the string pointer and length, before you can call OPEN. (And incidentally, OPEN doesn't take any input arguments.) A bit more complex than C, but not outrageous.

Here's a neat thing about using registers for subroutine I/O. Some KERNAL calls uses more than one register for return data. In the case of OPEN, the carry bit is used to indicate success or failure, and if it failed then .A holds the error code. But some other routines, such as RDTIM (Read Time) returns the 3-byte Jiffy Clock time in .A, .Y and .X. C and Javascript are limited to a single return value (although it can be more than 1 byte).

The advantage to processor arguments is that they're fast. Load the .A register with a value, that could be as short as 2 cycles. JSR to a routine that takes .A as its only argument, that takes a standard 6 cycles, but .A is already loaded and ready to use by the code in that routine. I mean, there is virtually no overhead. Especially if .A was already set by a previous routine's return, there is sometimes literally zero overhead in passing that data through to the next subroutine. So, if you can get away with using just 3 bytes of arguments, it's fast, it's really damn fast.

But there are downsides. Registers are effectively little more than in–processor global variables. Global variables which certain instructions operate directly on, and which those instructions require to work at all. An indirect indexed load, for example, can only use the .Y register, and must use the .Y register. So if you use the .Y register to pass in an argument, but the code in the routine needs to do an indirect indexed instruction, the .Y register has to be overwritten and whatever argument was passed in on it has to be saved somewhere so as not to be lost. This makes matters somewhat more complicated.

The other downside is that if your routine is using .A, .X and .Y for holding on to temporary state, such as the index of a loop, but inside the loop you JSR to another routine, it is necessary to know which registers that other routine (and more complicated, any subroutines that it may call, and so on ad infinitum) will use. The KERNAL routines are all very self–contained. They generally don't go off and call other routines, so their register usage is predictable. In fact, the documentation tells you exactly which registers are used (and thus disrupted) by each KERNAL routine. For example, VECTOR, takes .X and .Y as inputs, and returns a value in .X, but it uses .A in the process. So before calling VECTOR if you care about what's currently in .A you'd have to back it up first.

For the accumulator, backing up and restoring is easily done by pushing and pulling from the stack, but to push and pull .X or .Y from the stack it is much more complicated. The NMOS 6502, and the 6510 (NMOS 6502 variant used in the C64 and C128)2 there is no way to push .X and .Y directly to the stack, nor to pull them directly off the stack. You have to first transfer them to .A, then push that. And you similarly have to pull to .A and transfer to .X or .Y. This adds complication, because it involves disrupting .A. In the end, the most efficient way to write 6502 code that uses and worries about the registers for argument passing and local variables, is to write code carefully by hand. Like an art form.

C Language and the Stack

C is a super popular language, and without even considering how much code is still written in C today, most other modern languages are modeled syntactically on C. Don't believe me, the Wikipedia article, List of C-family programming languages, lists over 60 languages in the C family. One of the most recent being Apple's Swift which was first released just in 2014. The original C language began development in the late 1960s and first appeared in 1972.

Assembly is a step above Machine Code, by abstracting absolute and relative addresses with labels, and instructions with mnemonic codes, but the assembly code maintains a strict one–to–one relationship between code typed and instructions output by the assembler. The assembly programmer still has complete control over the precise gyrations the CPU will go through. C is a compiled language, which means by definition the programmer is not the one writing the Machine Code, the compiler is. The compiler is not an artist. It needs simple, reliable rules, that operate on a very local scale to produce predictable output that will work. There is no way that a compiler would go off looking through the code of routines that the current routine will call, to see which registers it will use and thus which are safe for its own use. It's just, too artsy. It's not rigorous enough.

Instead, the way C works is that all arguments are passed on the stack. Not only are all arguments on the stack, but all local variables of a function are also on the stack, and the return value is sent back via the stack too. The big advantage is that every instance of a function being called has its own unique place in memory, and that place is dynamically found and allocated. This makes recursive code really easy. A single function can call itself over and over, deeper and deeper, and each call instance has its own set of variables that don't interfere with the previous instances, because they are each stored further and further into the stack.

Passing arguments is easy too. The declaration of the function (or the function's signature) defines how many bytes of arguments it takes. When the function is called the caller puts that much data onto the stack and the function uses the stack pointer to reference those variables.

So, why doesn't the C64 programmer use C or a C–like solution?

I said it so poetically in my review of World of Commodore 2016, after listening to Bil Herd talk about the development of the C128 and where it was in the market at the time. People back in the 60s and 70s already knew what could be accomplished by a computer with an incredible amount of computing power. Computers in those decades already had 16 and 32 bit buses, megabytes of RAM and complex, timeslicing, multi–user Unix operating systems. The only problem is that these computers cost millions of dollars and were as big as modern cars. But, none–the–less, they had many of the modern computer amenities. What made the 6502 so special was that it was a complete central processing unit, all crammed together into a single IC package that could be sold for just a few dollars a piece, but not that it was a powerful and fully featured CPU. As I said in my article, early home computers really were just toys, in the eyes of the big business computers of the preceding 15 to 20 years.

Take the PDP-11, for example. The PDP-11 was commercially available starting in 1970, contemporaneous with the development of C. It was a very popular 16-bit minicomputer by DEC, that looks like an enormous stand–up freezer with tape reels on the front. Its main processor was in fact the design inspiration for both the Intel x86 family and the Motorola 68K family. And the C language explicitly took advantage of many of its low–level hardware features.

Just as a point of comparison, while the 6502 has 3 general purpose 8-bit registers, the PDP-11 has 8 general purpose 16-bit registers. But the the most important advantage is its addressing modes. The PDP-11 has, among others, 6(!) stack relative addressing modes and efficient support for multiple software stacks in addition to the hardware stack. The 6502 has a single, fixed address hardware stack, a single 8-bit stack pointer, no dedicated features for software stacks, and NO stack addressing modes at all. In fact the 6502 has just two instructions that affect the stack pointer. TSX and TXS. Transfer the Stack Pointer to .X register, and vice versa respectively. What this means is that performing operations directly on data in the stack is very crippling.

It is possible to write in C for the 6502, there is a 6502 C Compiler. But my understanding, from conversations I've had on IRC, is that it works by emulating in software the features of the CPU that are not supported natively. This is a recipe for slow execution. The bottom line is, no matter how convenient C's stack–oriented variables and arguments are, C was not designed for the 6502. And the 6502 is simply not well suited to run C code.

Alternative Argument Passing

Okay, now we know how the KERNAL works and what the limitations are with its techniques. And we also know why it is that the C64 doesn't just standardize on C (or a derivative) like every other modern computer. But we are still left with the problem of how to pass more than 3 bytes worth of arguments to a subroutine.

I can think of at least two ways to work around the limitation and then I'll go into some detail on a third and fairly clever solution. I found these tricks and techniques by reading the Official GEOS Programmer's Reference Guide. It discusses various ways that the GEOS Kernal supports passing arguments to its subroutines.

Zero Page on the 6502 has been described as a suite of pseudo registers. Almost every instruction has a Zero Page addressing mode that can work with data there more quickly than in higher memory pages. But, there is a limited amount of ZP space, just 256 bytes. And $0000 and $0001 are actually taken by the 6510's processor port.

But one trick that GEOS does is reserves 32 bytes of Zero Page as a set of 16, 16-bit registers, which it numbers R0 through R15. After that, it treats them very much the same way we would treat the real registers, and all their limitations described above. I said that one problem with the CPU registers is that they are effectively global variables. And so are the GEOS ZP registers. The advantage is that you've got 16 of them, and they're 16-bits wide. So various routines that need several 16-bit arguments are simply documented as, you put a 16-bit width into R5, you put an 8-bit height into R6, you set a fill pattern code into R3, and a buffer mode in R1, and then you jump to this subroutine and it draws a square and blah blah blah. I'm making up the specifics, but you get the point.

When the subroutine is called, it expects that you have populated the correct global ZP "registers" with suitable values, which it reads and acts upon. It also comes with a suite of Macros that help you set those registers. It's a bit slower than using real registers the way the KERNAL does, and has many of the same limitations, but it greatly expands the space, up to 32 bytes from just 3 bytes. You also have to have a lot of Zero Page dedicated to this solution. This is something GEOS could do because it replaces the C64 KERNAL entirely, and can do whatever it wants with the entire computer's memory space. This is more or less a solution I was able to dream up on my own, but in a less structured way. You can always just shove a few bytes directly into known system working space addresses, and then call a routine that will use those work space addresses. But, there is something that feels a bit dirty about this. What happens if you change the KERNAL and reassign some common work space addresses? Likely, old apps will stop working and will probably crash the system.

An alternative solution that I have come up with on my own, I don't recall seeing this in the GEOS Kernal, is to pass a pointer to a structure. This is the essential behavior in C64 OS of the Toolkit and its view objects. And it is also the way File References work. When calling a subroutine that operates on a file, for example, rather than with the KERNAL having to manually pass 5 different properties of varying 8- or 16-bit widths, the properties are set on a local (or allocated) block of memory. Then a pointer to that block of memory is put in .X and .Y (lo byte, hi byte) and the routine is called. The routine generally needs to write this pointer to zero page, and then use the .Y register as an index in indirect indexed mode to work with those properties.

There is no way around needing to use some global zero page space. As indirect pointers can only be referenced through addresses in zero page. However, moving the responsibility of what free space in ZP to use from the calling routine to the called routine feels much less dirty.

Using structures and passing pointers to structures is a pretty good solution, and it solves a lot of problems. But it isn't the best solution for all problems. Which is why we'll now look in more detail at one more solution I found in the GEOS Programmer's Reference Guide.

Inline Argument Passing

Let's just use the concrete example of what I've run into in C64 OS to understand the issue. C64 OS has a file module, which knows how to work with File Reference structs, and is a light wrapper for working with streams of data. The essential functionality I want is: fopen, fread, fwrite and fclose. Fopen will use a file reference struct to identify the device, partition, path and filename, and also dynamically stores the logical file number which doubles as the status for whether the file is currently open. Using a file reference requires a minimum of a 2–byte pointer. The file can be opened either for read or for write. After which, the pointer to the file reference can be used with fread or fwrite.

If the file was opened for read, for example, then we will want to call fread to actually read some data out of it. The arguments for fread will be, a pointer to the file reference, a pointer to a buffer of memory into which the data should be read, and a 16–bit length of data to read. That's 6 bytes of arguments.

If on the other hand the file was opened for write, then we want to call fwrite to write some data in memory to that file. The arguments will be, a pointer to the file reference, a pointer to a buffer in memory where some data is to be written, and a 16–bit length of data to write. That is also 6 bytes of arguments.

Closing a file reference is pretty straight forward. It only needs the 2 byte pointer to an open file reference. It just reads the logical file number out of the reference struct, closes the file and releases the LFN.

But now let's consider opening the file. If opening for read, not too hard. We need the pointer to the file reference, plus probably 1 byte to indicate the read/write direction. That's only 3 bytes, we could get away with putting the read/write flag in .A and the file ref pointer in .X and .Y.

When it comes time to opening a file to write, though, it becomes a bit more needy. You need the file ref pointer (2 bytes), the read/write flag (1 byte), plus a file type byte (1 byte, USR/SEQ/PRG), and you also need to indicate whether an overwrite should be permitted if the file already exists (1 byte), or if an append to an existing file should happen (1 byte). Naively, that's 6 bytes. Although, you could assign the Read/Write, to 1 bit, the file type to 2 bits and the overwrite and append to 1 bit each, and pack all that into a single byte argument. Dealing with bits makes your code fatter though. And nothing gets around the 6–byte requirements of the fread and fwrite routines.

Some GEOS routines can take inline arguments. Here's how it works:

You do a JSR to a subroutine, and in the calling code, you follow the JSR line with a series of argument data bytes. Effectively, as many as the routine needs. They're called inline arguments because they follow inline with your code.

The routine that accepts inline arguments begins by transferring the Stack Pointer to the .X register. It then uses the stack pointer to find the return address that the JSR pushed on. This address is actually the address where the inline arguments begin. It puts this address into a zero page pointer, where it can use indirect indexed calls to read the arguments (and optionally later overwrite them too if that made sense). But, it must also change the values on the stack to move the return address beyond the end of the inline arguments. When the routine does an RTS, the updated return address will be pulled off the stack and execution will continue just following the inline arguments. It's very clever. It's not always what you need, but it's a really great tool to have in the box if you need it.

Reading about it in the GEOS programmer's reference guide, it sounds a bit complicated to use. So I've written some sample code to show and explain how easy it really is. And how you can abstract the code to reuse it for different routines that take differing numbers of arguments.

Inline Arguments in Practice

Okay, so let's walk through this code and see how it works. We assemble to $0801 which is where a BASIC program should start. And the first include is the basic preamble. This will do a SYS2061, to jump to the first line of our assembly program which starts at line 6. The kernal.s include just defines the names of the kernal routines.

Getting right to the meat and potatoes of what we're showing, line 6 has a jsr getargs, this is an example routine that takes 3 bytes of inline arguments, and will print the three bytes in hexadecimal notation. On lines 7, 8 and 9 you can see that we've supplied three inline bytes of data. These are the arguments, immediately following the JSR instruction. At line 11 we call jsr getargs again, to show that in this case the 3 bytes of inline arguments are in the form of a .text string. It doesn't matter how we inline the arguments, all that matters is that the routine expects there to be 3 bytes and execution is going to continue just after those 3 bytes.

The rts on line 14 is the end of the program. Following this we include our handy inttohex.s. It has a routine tohex that takes a value in the accumulator and returns it as two hexadecimal (0 to F) PETSCII characters in the .X and .Y registers. prnthex is a simple little routine that will print the output of the tohex routine. First it CHROUTs a "$" followed by the two hexadecimal characters and lastly a new line.

And now for the getargs routine and how it works with inline arguments:

I like to comment my routines showing the ins and outs with arrows. In this case I've labeled my inline argument inputs as a1, a2 and a3. Following the right pointing arrow is usually a comment on what the argument holds. I've put in here .byte to indicate the length of the argument. In your calling routine, you could state the inline argument, for example, as a .word, then in the comments mention it's a .word, and you'll have to read two bytes to grab the whole thing.

Lines 5 to 16 do all the magic. The JSR instruction automatically pushes the return address onto the stack. That address is effectively just a 16-bit number. We have to add a number to that address that is equal to the total size of the inline arguments. In this case our args are 3 bytes. So we have to add 3 to that 16-bit return address. But adding 3 to the low byte could overflow that byte, so we have to do a full 16-bit add, using the carry. To begin the 16-bit add we start by clearing the carry, on line 5.

Next, we need to grab the current stack pointer. To do this we use the only instruction the 6502 has for getting the stack pointer, TSX, which transfers it to the .X register. To understand what happens next you have to know how the stack works. The stack consists of the entire first page of memory $0100 to $01FF. But things are pushed onto the stack starting from the top working down. The Stack Pointer is an offset into $01xx and always points to where the next pushed byte will go. Thus, when the stack is empty, the stack pointer is $FF. When the stack is full the stack pointer is $00. With each push, the stack pointer gets decremented, with each pull the stack pointer gets incremented.

After a JSR, whatever the stack pointer is, the return address is at the stack pointer +1, and the stack pointer +2. Here's the thing though, you'd think that once .X holds the stack pointer, you would need to manipulate .X to find the bytes you're after. Instead though, to access bytes relative to the stack pointer you can merely change the absolute start address from $0100 to something bigger or smaller.

Let's say the stack pointer is $F5. If you pushed something onto the stack it would go to $01F5, that means the last two things pushed onto the stack are actually stored at $01F6 and $01F7. Do a TSX, now .X is $F5. You don't need to INX and then read from $0100,X you can simply read $0101,X (which is $0101+$F5) add them together and you get $01F6. Similar to get $01F7 you don't need to INX, just start the absolute address as $0102,X (which is $0102+$F5).

Okay, now we know how we're referencing the address on the stack. Here's the next thing to know. When a JSR happens, the return address is pushed onto the stack high byte first, low byte second. So $0101,X references the low byte, $0102,X references the high byte. Now we're ready to see what happens.

Line 8 grabs the low byte from the stack. Line 9 stores it to a Zero Page address of our choice. It has to be stored somewhere in zero page to be able to read through it as a vector. Once we've stored that low byte, we can add 3 to it and write it straight back into the stack whence it came. Lines 10 and 11.

Adding 3 may have overflown the accumulator, but if it did, the carry is now set. Line 13 grabs the high byte and stores it at $fd, the high byte of the zero page vector. Then we add 0, which includes the carry and completes the full 16-bit add. And we write the new high byte straight back to the stack whence it came. And we're done.

The stack now contains a return address that is 3 bigger than it used to be, just past the end of the inline arguments. And the zeropage addresses $FC and $FD contain a vector that points at the block of inline arguments.

The only thing left to do is read those arguments. Here's one more trick of the JSR return address, it is actually the address of the next instruction... minus 1. But the RTS instruction expects that and returns you to the correct place. So, adding three to the return address was certainly the right thing to do. However, the vector at $FC/$FD actually points to one byte before the inline arguments. Again, no problem, we don't have to waste cycles and memory adding 1 to the vector, we just access the three arguments with offsets 1, 2 and 3, instead of 0, 1, and 2.

Lines 18 to 20, 22 to 24 and 26 to 28 show that we set .Y to the argument offset, then do an LDA indirect indexed through the zero page vector to grab that argument. The jsr prnthex simply prints that argument in hexadecimal which is what this example routine is supposed to do. Note that you don't need to get the arguments in any particular order. You can read any of them whenever it makes sense to in the routine. You can ignore some of them, or even modify and write a new value back to that inline argument memory location. The world's your oyster. You can have up to 255 bytes of inline arguments without needing to modify any of the initial stack manipulation logic.

Summing Up

So you might be asking, well that's neat, but why bother with all that? You could just put a few bytes at the end of your routine to hold the arguments, then load up .X and .Y with a pointer to those bytes and call the routine. The routine then only needs to write .X and .Y to the zero page addresses of its choice and boom you're done. Like this:

And yeah, that works too. But there are downsides. When you put the args below the routine:

  1. You have to use a label to find them.
  2. They are separated from the routine call they apply to.
  3. You have to add code LDX/LDY above routine call.
  4. If there are many blocks of args for different subroutines, it gets messy fast.

And all the converse are advantages for inline arguments. They don't need a label, because their address is known implicitly from the address of the JSR's return address. They sit together in your code with the JSR they apply to. If you have multiple JSRs each with their own arg blocks it doesn't get progressively messier. And you don't need any extra code on the calling side to set it up.

There is just one downside, in my opinion. The called routine has to have a fair chunk of code to manipulate the stack and set up the vector. Instead of 4 bytes for the end argument block, you need 22 bytes for the inline arguments. And if you had many routines that all use inline args those 22 bytes start adding up. Read on for one last solution to that problem!

A Slightly More Advanced Trick

For end arguments, you need 4 bytes in the called routine to setup the vector. And you need 4 bytes in the calling code to setup the .X and .Y pointer to the arguments (plus a label). So you actually need 8 bytes to "pass" the arguments. That's 8 bytes on top of the byte count of the arguments themselves.

With inline arguments, you need 0 bytes in the calling code, but 22 bytes in the called routine. But, if you're going to use this trick for numerous routines with inline arguments, you can move those 22 bytes into a shared argument–pointer–configuration routine, like this:

Screenshots of sample code with inline arguments

Now we've got a new routine called setargptr. It will handle the work of manipulating the stack and setting up the zero page vector. There are some gotchas. First, if we're going to use this routine for multiple routines that accept inline arguments, we need a way to customize how many inline arguments to expect. It can't just be hardcoded at 3.

We pass to this routine in the accumulator the number of inline arguments there shall be. The first thing this routine now does is writes the inline argument count (self–modifying code here) to a new label argc(+1). This overwrites the value to add to the low byte of the return address. The rest of the 16-bit add works as it did before.

The second gotcha is that the real routine, getargs in this case, has to call setargptr. But this call pushes its own return address onto the stack. So the place on the stack where our arguments are is 2 bytes further back. That's easy to deal with though, the new stack absolute offsets are simply $0103 and $0104, instead of $0101 and $0102. That's it.

The only other gotcha is that the setargptr routine now bears the responsibility for which Zero Page addresses to use for the vector. And thus, every routine that uses inline arguments and uses setargptr to manipulate the stack, must all use the same zero page vector. But, depending on your situation, that might be just fine.

In the actual getargs routine now, instead of all that messy code to manipulate the stack we just load the accumulator with the expected number of inline args, and JSR to setargptr. Boom, done. Now, it's 5 bytes per routine that uses inline arguments instead of 8 for end arguments. We actually save 3 bytes per routine. But, the setargptr routine is now 26 bytes. End arguments require 4 bytes per routine, plus 4 bytes per call. So if we write just a few routines with inline arguments, and call those routines a few times. We quickly end up using less total memory than if we used end arguments. But, to be honest, saving some memory is icing on the cake. Inline arguments are just cool.

Thanks for the tip GEOS! GEOS has got a few other tricks I'll probably end up exploring in future posts. Stay tuned.

  1. At some point in the future, I'd like to post my own version of the C64 KERNAL documentation as a Programming Reference. But, it's a lot of work. For now, you can read about the KERNAL in the C64 Programmer's Reference Guide. Chapter 5: Basic to Machine Language (PDF) []
  2. The CMOS version of the 6502, the 65c02, includes extra instructions for pushing and pulling the .X and .Y registers directly to and from the stack, and other handy things. Unfortunately, there is no such 65c10 processor. []
October 23, 2017Technical Deep Dive

How the C64 Keyboard Works

I'm almost ashamed to admit it, but just 1 year ago when I started working on C64 OS, I didn't have the first clue how the C64 Keyboard worked. I knew there was a thing called the keyboard buffer, but I had cloudy thoughts of it being inside the keyboard itself. When I had to actually write the keyboard scanning routine for C64 and started looking at the 1982 fullspread double sided C64 schematics, I could see that the keyboard was wired up to the CIAs, but my first assumption was that when a key was pressed an IRQ would be generated. These ideas in retrospect seem embarassingly ignorant of such a basic feature of the Commodore 64.

But how are we supposed to know until we learn, right? I've written on lots of technical topics, but this post will be a deep explanation of how the C64 reads data in from the keyboard.

The CIAs (MOS 6526)

The C64 has two Complex Interface Adapter chips. Better known as the CIAs, officially the MOS 6526. You can read all about this chip on Wikipedia. But I want to explain some of my most interesting discoveries.

MOS Technology, which was acquired by Commodore and rebranded the Commodore Semiconductor Group, made a whole series of chips which I like to think of as the 65xx family. If you look up MOS 65xx on Wikipedia it'll tell you that this is a series of 8-bit microprocessors. And it's true, the 6501, 6502, 6503, 6504, 6505, 6507, 6508 and 6510, (at least) are all microprocessors. But the 65xx numbered series includes a whole family of chips that are designed to work together on a logicboard, together with some ram, rom, and other glue chips, to implement a complete microcomputer.

The C64 has a 6510 (CPU), 6567 (VIC-II, Video Interface Chip), 6581 (SID, Sound Interface Device), plus two 6526 (CIA, Complex Interface Adapter) chips. Then it has three rom chips (the shortboard reduced this to two), KERNAL, BASIC and CHARACTER roms, a bunch of RAM chips totalling to 64K, a handful of simple glue chips, in the 74xx and 74xxx series, a 556, a pair of 4066's, and most importantly the PLA (Programmable Logic Array). Together, they work beautifully to provide all that the computer is able to do.

pinout of the MOS 6526 CIA chip

So, what does a 6526 provide? Lots of handy things. It's got 16 registers for configuration, it's got a Time–of–Day clock with programmable alarms, it's got two 16-bit programmable timers, it's got a serial I/O port, and, most importantly for today's discussion, it has two 8-bit bi-directional ports, which we'll talk about in a minute.

What is a port, anyway?

If you'd asked me what is a port, just a year ago, I'd have said that it's a connector on the back or side of a computer, to which you can connect a peripheral device. That's half right, but it misses a couple of critical points.

A port is a line, a wire, that the CPU can control. If it's an output port that means instructions executed by the CPU can force that wire either to output +5V or GND, a logical 1 or 0 respectively. An input port means instructions executed by the CPU can read the voltage level of that line, and if it's near +5V the CPU sees this as logical 1, or near GND which the CPU reads as logical 0. There is virtually always some controlling device that allows the CPU to manage the state of that port line. And in the case of the C64 that controlling device is the 6526 CIA chip itself.

The next important thing to understand is that in a digital computer, one line (1 wire) is one bit. The wire can either be high or low, at a time. So when we say that the CIA chip has two 8-bit bi-directional ports, this no longer has to sound like gobbledygook. An 8-bit port is a port with 8 parallel, simultaenously settable or readable lines. Bi-directional means that each of those lines can be configured either as inputs or outputs. And in the case of the 6526 each line can be configured for input or output individually. At the same time some of them can be inputs and others outputs. A serial port, by the way, which the 6526 has, may as well be called a 1-bit port.

If the 6526 has two 8-bit ports, and each bit requires a line, there should be 16 lines dedicated to those ports. And indeed, when you look at the pinout diagram of the CIA above, reading down the left hand side you see PA0 through PA7, followed by PB0 through PB7. PA and PB stand for Port A and Port B, and each has 8 lines numbered 0 through 7. Amazing. That means the computer, software instructions running on the CPU, can cause those 16 chip legs to be +5V or GND, or can read the +5V or GND status of those legs.

One more step to go, the legs of the chips are connected to traces on the logicboard which run directly to a physical connector which is exposed through the chassis of the computer, and boom, it's called a port. Those physical things we've colloquially called ports our whole lives are literal technical ports, wires the CPU can independently manipulate.

How does the CPU interact with the port?

The question of course is, how does the CPU actually interact with one of those 8-bit ports? The 6526 has to be addressable, and it needs to get data from the CPU. And as we'll recall it must have one line per bit. The C64 (the 6510 CPU) has a 16-bit address bus and an 8-bit data bus, so the CIA must have 16 address legs and 8 data legs, right? Almost.

If we go back to our pinout diagram and read down the right side we see 4 legs labeled A0 through A3, and 8 labeled D0 through D7, in amongst a bunch of other lines we don't need to worry about at the moment. The Ax legs are address lines and the Dx legs are data lines. There are 8 data lines, that's good, those are obviously connected directly to the 8 data bus lines from the CPU. But why are there only 4 address lines?

As mentioned above, the 6526 has 16 configuration registers. What that actually means is that there are 16 memory addresses where the CPU can read or write to interact with the CIA. 4 bits can address from 0 to 15, and we need one line per bit, so bingo the 16 registers need only 4 address lines. But in a real C64 there are two CIAs addressed from $DC00 to $DC0F for CIA 1, and from $DD00 to $DD0F for CIA 2. Somehow, only when the upper 8 bits of the 16-bit address bus are $DC (1101 1100) or $DD (1101 1101) should CIA 1 or 2 be active respectively.

In steps the glue logic. A combination of the PLA chip (Programmable Logic Array), which is a highly custom chip created just for the C64, and a 74239 (an off the shelf 4-bit decoder), are used to monitor the upper 8-bits of the address bus and selectively turn on and off a variety of chips that are themselves connected only to the lower bits of the address bus. The 6526 is enabled or disabled via pin 23, labeled in the diagram above "/CE", for chip enable.

The fact that the chip only has 4 address lines plus a chip enable pin, rather than a full 16 address lines, means that custom glue logic can be used to map the CIA's small addressable range somewhere into a much larger address space. And, as is the case in the C64, multiple CIA chips can be mapped into different places in the main 16-bit address space.

The net result is that when the CPU sets $DC00 (1101 1100 0000 0000) onto the address bus, the PLA and 74239 together enable CIA 1, and disable every other chip on the bus. The CIA 1 chip, being enabled, sees 0000 on its 4 address lines, and the 8 data lines interact directly with whatever the 6526's register 0 actually does. We'll get to a description of the 6526's registers in a moment.

What are the CIAs hooked up to?

Now that we know what a port is, that the CIA offers two 8-bit ports, that the C64 has two CIAs, and how the C64's glue logic is setup to allow the CPU to address them, the next question is, what are those CIAs hooked up?

Part of the C64 schematic, showing how the CIAs are hooked up

Above is a section of the schematics of the C64 logicboard. I've intentionally removed a number of extraneous bits around the edges to try to bring focus just on how the CIAs are wired up. In this diagram there are two main ICs shown, U1 and U2, both are labelled 6526 CIA. Their addresses are written in parentheses (DC00 - DCFF) and (DD00-DDFF)1. I added the labels in blue, #1 and #2, as they are usually referred to in documentation.

Along the left side we have four blocks. From top to bottom: Control Port 2, Control Port 1, Keyboard, and User Port. The two control ports are of course the joystick ports we all know and love on the right side of our C64. The user port is that wonderful geek port on the back of the computer at the far left. The keyboard port is a block of pins in the middle of the logic board, which the keyboard is connected to. It is as much a port as any other, but one that is only internally accessible as a result of the design of the chassis.

Let's start with the user port. You can see that pins on CIA 2 labeled PB0 through PB7, as well as PA2, run directly to pins on the user port's edge connector. Nothing could be more straight forward than that. When you plug something into the user port, you are literally connecting something directly to the Port B legs of CIA 2. Now we know why static electric discharge is so dangerous for the computer, when you touch that user port connector, you may as well be rubbing your fingers across the CIA's legs. There are no over voltage or surge protections of any kind. But at least the connection is very easy to understand.

Here's a thought. When nothing is plugged into the user port, the Port B legs of CIA 2 are very evidently not connected to anything at all. It is neither connected to a +5V source, but nor is it connected to GND. We would say that the legs are in the third state of three–state logic. That is, they're hooked up to effectively infinite resistance. In a sense they are actually connected to ground, but they are connected via a massive bridge of open air, which is highly non-conductive and thus extremely high in resistance. The question is what would the CPU read if it tried to read the values off those legs when they're hooked to nothing? The answer cannot be known by looking at the schematics alone. However, in the 6526's documentation, they are said to internally pull up. That's electronics terminology to mean, when the legs are connected to nothing, the computer will read them as logically high, or 1s rather than 0s. This is important.

How is the keyboard hooked up?

Now let's look at how the keyboard is hooked up. You can see that all 8 bits of both Port A and Port B run straight across to the keyboard connector. Port A's bits are correspondingly labeled COL0 through COL7, and Port B's bits are ROW0 to ROW7. Some of those lines branch off and up to the control ports, but we can ignore those for the moment.

The keyboard connector has just 3 additional lines, plus a spacer (KEY) to make sure you orient the cable correctly. These are +5V, GND, and a special line labeled RESTORE, which we'll get to. I took apart a C64 keyboard, shown below, so we can see what its insides look like. It was already dead, so don't worry I didn't sacrifice it in the name of science.

Physical keyboard matrix

In the image above, I've faux-silk-screened the keycaps onto the circuit board so it's easy to see how the keys line up. Notice that the keyboard connector in the schematics labels the pins 1 through 20. On the keyboard's circuit board you can see the solder points where the wires connect, they aren't neatly in a row, but they are labeled, 0,1,2,3,4,5,6,7,8 and A,B,C,D,E,F,G,H,I. But 9 numbers and 9 letters make 18 not 20. One of those 20 is the orientation key, so it's not connected to anything, and interestingly, as we'll see, the +5V line is not needed for the keyboard, so it's not connected to anything.

Looking at the keyboard's PCB it is pretty clear that it has no electronics intelligence of any kind. It is merely a collection of switches. Each key connects two pads, and each pad is connected along a long snaking trace the joins several pads together and eventually leads to one of the 18 wires of the keyboard cable.

If we follow the traces, or better yet, use a continuity tester, we discover that the RESTORE key joins two pads which are alone on their own traces leading back to the keyboard connector. The image above doesn't show the full PCB, the function keys are missing, but you can see that from the RESTORE key's two pads the traces lead off the right side on their own. One pad is connected to the GND pin, the other to the /RESTORE pin in the schematics. Note the slash before that label, on the schematics it appears as a long bar above the word RESTORE. This means that the restore behavior is triggered by pulling the line low, or hooking it to ground. And indeed, when you press the restore key the key switch simply joins the GND line to the /RESTORE line.

Nothing else on the keyboard is connected to the /RESTORE or GND pins, the +5V pin connects to nothing, and pin 2 is just an orientation spacer, so that leaves us with 16 lines. The lines labeled COL0 through COL7 and ROW0 through ROW7, which connect to Port A and Port B of CIA 1. COL and ROW are for columns and rows, because the rest of the keys are arranged into an 8 by 8 matrix. Leaving out the RESTORE key, with its dedicated lines, if you count up all the other keys on the keyboard (and don't forget the four function keys) you get 65. Ah, but there is one other little thing to notice. The Shift Lock key is wired to exactly the same two lines as the Left Shift key. Therefore, the Shift Lock key is just a mechanically latching switch that to the computer is indistinguishable from the ordinary Left Shift key. Exclude this key, and we're left with 64 keys. And 8 times 8 is 64.

Each ROW line snakes around the board connecting to one half of the contact pads of 8 different keys. And each COL line snakes around connecting to the other half of the contact pads of 8 keys. Such that each key is a switch that connects one ROW line to one COL line. It can be a bit tricky to trace these all out visually, but a continuity tester comes in really handy. The result is that the keys are arranged in the following grid:

Commodore 64 keyboard matrix layout

  Bit 0
$01,$FE
Bit 1
$02,$FD
Bit 2
$04,$FB
Bit 3
$08,$F7
Bit 4
$10,$EF
Bit 5
$20,$DF
Bit 6
$40,$BF
Bit 7
$80,$7F
Bit 0
$01,$FE
Insert
Delete
Return Cursor
Left/Right
F7 F1 F3 F5 Cursor
Up/Down
Bit 1
$02,$FD
3 W A 4 Z S E Left Shift
Shift Lock
Bit 2
$04,$FB
5 R D 6 C F T X
Bit 3
$08,$F7
7 Y G 8 B H U V
Bit 4
$10,$EF
9 I J 0 M K O N
Bit 5
$20,$DF
+
(plus)
P L
(minus)
.
(period)
:
(colon)
@
(at)
,
(comma)
Bit 6
$40,$BF
£
(pound)
*
(asterisk)
;
(semicolon)
Clear
Home
Right Shift =
(equal)

(up arrow)
/
(slash)
Bit 7
$80,$7F
1
(left arrow)
Control 2 Space Commodore Q Run
Stop

Original Source: http://sta.c64.org/cbm64kbdlay.html. HTML reformatted, re-styled and one minor error corrected.

We can easily spot check some of these. It is easy to see on the keyboard circuit board that W and E share a trace on the bottom half of their pads. It is also easy to spot that R and T share a trace on one half. And sure enough when we look in the table above, we can see that W and E appear in the same row. And R and T appear together in another row. Similarly, it is easy to see that Left Shift, X, V and N all share a trace. When we look in the table, sure enough, they all share a column.

Keyboard PCB Wire Mapping

I used a continuity tester to map the Letter/Number pairs of each key, which you can see in the image above. You can see several distinct patterns in keys that are physically close to each other. This also lets us map the Keyboard PCB letter/number scheme to the C64 logicboard's 20-pin keyboard connector. If we just look at PCB trace "A", we see that Space, C=, Run/Stop, and Control all share it. Looking at the key matrix table we see that this is ROW 7. "B" is shared by Left Shift, Z, A, and S. Which, again from the key matrix table, we can see this is ROW 1. If we look at one of the Keyboard PCB's numbered traces, say "3", it is shared by Left Shift, X, V and N. In the key matrix table these are in COL7. Repeating this process, we can construct the following table:

Keyboard PCB to Keyboard Connector Map

KB PCB Label KB Connector Pin C64 Connection
A 9 ROW7
B 11 ROW1
C 10 ROW2
D 5 ROW3
E 8 ROW4
F 7 ROW5
G 6 ROW6
H 12 ROW0
I 1 (or 3) GND (or NMI)
0 13 COL0
1 19 COL1
2 18 COL2
3 20 COL7
4 16 COL4
5 15 COL5
6 14 COL6
7 17 COL3
8 3 (or 1) NMI (or GND)

I am not really sure why these numbering schemes seem so illogical. But if I had to guess it would be a result of the physical limitations of where the traces on the logicboard have to go such that they don't cross over each other. I'm also not sure why the letters and numbers on the PCB don't align better with the ROW and COL numbers. For example, why is A = ROW7 and H = ROW0? Or why is 3 = COL7 and 7 = COL3? Who knows. But if I had to guess about this, I notice that there are extra traces connecting some of the rows and columns on the Keyboard's PCB, but that have had holes drilled through in strategic places to sever them. This could have been a cost saving technique for Commodore to produce the same PCB for different keyboard layouts where the only thing they needed to do in the manufacturing process was drill some precision holes. But I haven't (and probably won't bother) trying to figure out exactly which rows and columns would get swapped if some different configuration of holes were present.

Also, I can't clearly tell between "I" and "8" which goes to GND and which to NMI. Because on my keyboard the actual connector was long ago cut away. And because there is only one key, RESTORE, that joins these two together, it actually doesn't make any difference how GND and NMI are assigned to these two traces. This may all seem to be trivial information, but if you ever want to re-wire a C64 keyboard PCB (which I may someday do with the one I've got), the above table will come in handy.

How the Keyboard's PCB traces are labeled was a bit of tangent. If we return now to what is really happening, it is that when you press a key, say the H key, a wire coming off pin PA3 on CIA 1, goes into the keyboard, goes through the closed H-key switch and back out of the keyboard cable and into pin PB5 on CIA 1. Every key on the keyboard (except RESTORE) merely electrically joins one of the CIA's Port A bits to one of the CIA's Port B bits. But, how does the computer know which keys are being pressed? For that, we need to turn to the software scanning routine.

How is the keyboard matrix scanned?

When describing how the CIAs are hooked up, I mentioned something that was an important behaviour. When one of the Port A or B pins is connected to nothing, it is internally pulled up such that the computer reads the value as a logical 1. When no keys are held down on the keyboard, you may have noticed that the CIA Port pins lead to the keyboard connector, and then into the keyboard, and then through traces across the keyboard's PCB but then they eventually just come to an end connecting to nothing. Therefore, no matter which CIA Port you choose to read from, as long as no keys are held down,2 those port pins are floating and they will read as 1's.

The only thing we can possibly do to change one of the port pins so that it reads as a 0 instead is to connect that pin to ground. But the only thing the pin can possibly connect to, by pressing keys and closing the circuits, is to pins on the other CIA Port. And so we are led back to the 6526 CIAs, their behaviors and how they can be configured via their registers.

Each CIA has 4 address lines, for a binary combination of 16 addressable registers. In the documentation they are referred to with their in–chip address, 0 through 15. Since we're talking about CIA #1 in the C64, and it's mapped to $DC00 to $DCFF, we use $DC00 through $DC0F to access those registers. See this article for full technical documentation of the CIA 6526.

Register's 0 and 1 are the read/write registers that correspond to Port A and Port B. However, the direction of the bits in those registers can be configured independently as either inputs or outputs. The directions of the Ports' bits are configured with registers 2 and 3 respectively. A 0 sets the corresponding bit as an input, for the CPU to read the status of something outside the computer. And a 1 sets the bit as an output, for the CPU to send data or control something outside the computer. So, to set all the bits of Port A for input, you write $00 (%00000000) to $DC02.

    LDX #%00000000
    STX $DC02

Since the keyboard joins the bits of Port A to the bits of Port B, and we need to pull one of those bits low, the trick is to set one of the ports as all inputs and the other as all outputs. Then we set the value of the output port as all low. This makes all of those pins a source of GND. Press a key, and the input pin is connected electrically, through the key, to a source of GND and it changes from its internally pulled–up 1 to its ground connected state, 0. When I first realized that's how it works, electrically, it was a very satisfying discovery.

    LDX #%00000000 ;Inputs
    LDY #%11111111 ;Outputs
    LDA #%00000000 ;Low/GND Outputs

    STX $DC02 ;Port A direction config
    STY $DC03 ;Port B direction config
    STA $DC01 ;Set Port B's outputs low

The problem is the following. If you press A, the low PB1 will pull PA2 low, so we know something in COL2 was pressed. However, if you instead press D, then the low PB2 will pull PA2 low, and you would not be able to distinguish between whether it was A or D, or any other key in COL2, that is forcing PA2 low.

In order to distinguish the rows, Port A has to be read 8 separate times. On the first loop Port B has its outputs set such that Bit 0 is low, but all the other bits are high. During that read, if a key in rows 1 through 7 (and column 2) is held down then those keys are just connecting PA2 to a high, and so they have no affect on PA2, it's already internally pulled up. However, only if the key in row 0, col 2, (Cursor Left/Right) is held down will PA2 get pulled low. After reading Port A and storing it, we loop, reset Port B so all the bits are high except bit 1, then read Port A again to see which keys in row 1 are down, store that, and continue until we've read 8 bytes, which are a "bitmap" of the up/down state of all 64 keys. Any set bit in the map means the corresponding key is up, any unset bit in the map is a key that is down.

And this is, more or less, what the keyboard scanning routine in the KERNAL rom does, 60 times per second. Although, it does a bunch of other stuff I'm not going to talk about today.

Concluding Thoughts

There are interesting similarities and interesting differences between how a C64 and how a PC/Mac keyboard work. PS/2, ADB and USB keyboards, even today, still use a key matrix of rows and columns that need to be scanned in order to build a table of which keys are up and which are down. However, each of these above three connection types is a serial port. In PC and Mac keyboards, the matrix scanning logic is all implemented in smart electronics inside the keyboard. The keyboard itself then maintains a memory buffer of the sequence of key codes representing up and down keys.

On a keyboard with 101 keys, assigning one code for up, and another code for down for each key, that's only 202 values, well within the 0–255 range of a single byte. The keyboard's electronics then also need to implement the serial protocol, and sends to the computer, a byte at a time, the codes representing keys going down and keys going up. It's a bit more complicated than that, and varies from serial protocol to serial protocol, but that is the essence of it.

The C64's keyboard, by comparison, is absolutely dumb. It has no electronics at all. Just switches in an 8x8 matrix. The actual matrix switches themselves are fed down the keyboard cable and into the computer. And the C64's own CPU needs to spend precious time scanning the matrix. It is truly unfortunate that on a machine with only 1Mhz to spare, it needs to use some non–trivial number of those cycles, because whatever it takes to fully scan the matrix once, has to be repeated 60 times a second, just to input data from the keyboard.

But on PCs with thousands of times the number of cycles available, it doesn't need to spend any of those cycles worrying about scanning the keyboard. If the state of the keyboard isn't changing, the internal logic of the keyboard is figuring that out, and it doesn't have to send any data to the computer via its serial connection. And there you have it.

Feel free to leave, questions, comments and corrections in the comments!

  1. The careful observer may notice that on the schematic the address range is DC00 to DCFF, instead of DC00 to DC0F. The PLA and 74239 map in the CIAs whenever the upper 8 bits are $DC (or $DD), even though the CIAs are only connected to the lowest 4 bits of the address bus.

    When a CIA is mapped in, bits 4 to 7 are completely ignored. The effect of this is that the CIAs appear to be mirrored 16 times across DCx0 to DCxF. It is not recommended to address them above DC0x (or DD0x) because future Commodore models could include additional glue logic for mapping other components into those higher address ranges. And, in fact, I believe the C128 does this very thing for accessing its extended keyboard. []
  2. Also assuming none of the joystick/control port lines are connected to anything, but I'm leaving aside the control ports in this post to focus on the keyboard. []
October 18, 2017Programming Theory

A C64 OS App's Start Of Life

I'm pretty close to closing the circuit on being able to launch and run an app in C64 OS. It might feel like it's taken a long time to get to this point, it's been almost a year. But when I think back on my progress, I'm not disappointed. I've had to learn a lot, and I feel like the whole process has really been pulling myself up by my bootstraps. Not only have I learned, realistically, to write code in 6502 ASM, but I've learned a lot about all the various parts of a C64 along the way.

I have my C64 Programmer's Reference Guide, the large, double–sided, fold–out C64 schematics from 1982, and the Advanced Machine Language for the C64 by Abacus. I've also read extensively from the 1541-II user guide and the CMD HD manual, and the Complete Commodore Inner Space Anthology has been super useful. And of course, poring over the disassembled and fully commented BASIC and KERNAL roms and understanding how those routines work has been very important. Learning about software and hardware, and electronics theory at the same time has been unexpectedly rewarding for me intellectually. I feel as though I've learned more about technology in the past year of induldging in C64 retro computing hobby than I have in my fulltime professional software developer career over the past 5 or 6 years.

And let's not forget to add, whenever my fingers get tired of typing, or my butt starts to ache from sitting in my chair for too many hours in the day, I've thoroughly enjoyed the countless of hours of work and planning I've put into my C64 Luggable project. The hours I've sunk into coding and designing C64 OS is nearing a turning point where I can actually launch and run applications. This seems like a good time to take a look at what it's like for the system to start up and launch the default home application, in this case, App Launcher. I've already written at least one post on my ideas for launching applications, so if you're curious you might want to read that too.


The Booter

As I've gone over in previous posts, the C64 OS KERNAL (for lack of a better word) is divided into approximately 10 modules. These are implemented as one assembly source code file per module, plus a header file (*.h) that declares the exported routines, and an includes file (*.s) that declares constants and macros. The booter is not part of the C64 OS kernal, and it isn't launched the way a proper application for C64 OS launches.

The booter launches more or less the way a standard C64 assembly program might launch. It's assembled to $0801 and starts off with the basic preamble that does a SYS 2061 to kick it into action. The booter must be in the system folder, AND the drive whence it is loaded must have its current partition and current path defaulted the partition and path of the system folder. For example, you cannot load the booter with a command like this:

	LOAD "2//SYSTEM:BOOTER",8

If you have to specify the partition and path such as in the above example, because the current default partition and path are somewhere else, the booter will fail. I tested this out on the Wheels "starter", just to see if he did something more clever than me, and the Wheels starter has a similar limitation. It completely craps out if the default partition isn't where the starter is located. Although, I believe the starter must also be in the root directory of its partition. This is not the case with C64 OS.

Let's get this out of the way, C64 OS requires the system to be on a storage device that supports subdirectories. This could be an IDE64, or a CMD HD/RL/FD, or any of the wonderful SD2IEC variants out there. Please see the The Commodore 8-Bit Buyer's Guide for what purchase options you have. Other files, documents, images, whatever can be stored on other any other media, like 1541/71/81 or their emulation partitions on CMD devices, or in .DXX images on SD2IEC devices, etc.

When the booter runs, it configures the system file reference.1 It uses $BA (186) to grab the current device number, then it uses a C-P command to read the current partition number from the boot device, and writes these into the system file reference. Next, it opens the sequential file, "config.t" and starts reading it in. It finds config.t in the current partition and path, and if it doesn't find it there, it craps out. The very first line of config.t (subject to change, of course), is the path within the current partition of the system folder. This path entry is essentially just a reflection of the very path where this file is stored. So, isn't there a way to just get this path (present working directory) dynamically? Actually, reliably getting present working directory is a severe pain in the butt, given the way the various file systems on Commodore compatible drives work. If you don't believe me, feel free to read this lengthy thread about the topic. I actually contribute to that thread, in which I mention exactly what I'm talking about here. C64 OS only needs to know where its system folder is, which it gets partially dynamically and partially by the config.t's path entry. After that, all file references are handled in a standard way internal to the OS. And the concept of a PWD simply becomes a file reference held and manipulated by an application.

Before doing any config, the booter loads all of the C64 OS KERNAL modules into memory. Then it reads the config file and builds the system file reference, and a few more config variables such as default system colors. It then runs a drive detection routine and sets up a table of addresses to device types. The booter then JMP's to "runhome" via the system jump table. This JMP, of course, never returns, and nothing from the booter is ever executed again. Any memory it occupied is already marked as available by the KERNAL's memory manager.

Screenshot of code from the booter
Sometimes I give you the source code beautifully formatted in a GitHub Gist. But today you're getting an authentic view of what it looks like to see this code the way I see it, sitting in front of my flat c128.

System Services

One of the C64 OS KERNAL modules is service. It is a bit meta, but provides a few critically important, well, services. It handles environment variables. It holds onto the aforementioned system file reference. It tracks which home application it should load when an application quits. And it has the runhome routine that is actually responsible for finding and launching the home application. Service is also the module that hosts the main IRQ service routine.

When first called, runhome checks to see if the home app's file reference has been initialized, and if not it allocates memory for it, and constructs it relatively from data in the system file reference and environment data about the current home app. That is very nearly all it does. It then loads the pointer to the home app's file reference into the .X and .Y registers and JMPs to loadapp via the system jump table. After this point, the home application is treated exactly the same as any other app. The only thing special about it is that there is a system service routine that always knows how to get back to the home app.

Screenshot of code from runhome in services 1 Screenshot of code from runhome in services 2 Screenshot of code from runhome in services 3

File Services

Another C64 OS KERNAL module is file. Ideally, it handles all file system accesses. It is actually very lightweight, however, because the C64 OS KERNAL does not replace the C64's KERNAL rom, it just augments it. If you have JiffyDOS then all the actual serial routines are handled by the JiffyDOS rom, including all the KERNAL rom's standard vectors that allow the IDE64 and RamLink to work.

So you might think, well, what does the file module actually do then? Mainly it knows how to work with C64 OS file reference structs, which are constructed and manipulated by other parts of C64 OS and its applications and utilities. These abstract the much more primitive KERNAL system of logical file numbers, and the KERNAL routines SETLFS, SETNAM etc. A C64 OS file reference contains a device address which is used to look up the device type ID from the table built by the drive detection routine that was run by the booter. From that ID file knows if the device supports partitions and subdirectories. The file reference also contains a 16-character filename, a partition number and path. As well as a dynamically managed Logical File Number.

A quick aside; The path can be upto about 230 characters long, which if you have 16 character directory names, plus a one-character delimiter (/), supports nested folders a minimum of 13 levels deep. These can go deeper if the directory names use fewer than 16 characters. 1 character folder names, plus the one-character delimiter, means a maximum possible folder depth of 113. Or an average of about 25 nested folders.2

The file module manages Logical File Numbers (LFN), the ones used by the KERNAL rom, for you. You create a file reference, and open it for read or write. The file module automatically handles getting you a Logical File Number that's not in use, changing the device's default path and partition, and opening the file. Regardless of how you've navigated around the directory structure of the device. When you close a file, it doesn't destroy the file reference, it merely releases the LFN back to the pool, and sets the LFN on the file reference to indicate it's not open. Saving over a previously opened file in a text editor becomes effortless. The reference doesn't just remember the filename, but also where it came from, and will write it back to the correct place.

Screenshot of code from loadapp in file 1 Screenshot of code from loadapp in file 2 Screenshot of code from loadapp in file 3

The first thing loadapp does is initializes the Toolkit. Toolkit has two tables, one for allocating its built–in classes, one for initializing those classes. If an application wants to subclass and extend those classes, it can do so by copying the allocation and initialization tables, which it then extends, and changing the vectors that Toolkit uses to find those tables. Reinitializing Toolkit resets the vectors back to the built–in alloc and init tables. This is necessary to recover from any subclassing efforts of the previous application.

Next it calls prepdisk, which changes the default partition and path of the device so that subsequent disk accesses are relative to the application's bundle. Then it reads the "menu.m" file which every application must have in its bundle. In order to read menu.m, it uses the blksize routine to get the number of blocks the file takes up on disk. Then it allocates the same number of blocks in memory, and reads the file into that allocated space. In C64 OS, atomic reads, that is, file reads that will be opened, read, and closed in short sequence, can use the reserved "templfn" for a temporary logical file number, rather than allocating and releasing one from the pool.

Screenshot of code from loadapp in file 4 Screenshot of code from loadapp in file 5

With the menu data in memory, loadapp JMPs to mnuinit in the menu module. However, I'm not going to go down that rabbit hole in this article. Suffice to say it recursively builds a tree of menu nodes which the mnudraw routine knows how to draw.

The memory manager allocates from the top of memory down, remember, so allocations for the menu data and file references and so on are allocated out of memory starting up near where the C64 OS KERNAL is. This is important, because next loadapp loads "main.o", the application's main executable file, out of the app's bundle. The application's main.o must be assembled to $0800. The KERNAL rom's load routine returns the address up to which the load ended in .X and .Y, low byte, high byte respectively. The high byte is the page number and C64 OS uses a paged memory manager. So .Y is left holding the end page number, and .X is loaded with $08, the starting page number, before doing a JSR to pgmark in the memory module. This tells the memory manager the range of pages to flag as allocated.

Next loadapp initializes the mouse, by calling initmouse in the input module. The previous app has the ability to disable the mouse, this will re–enable it. This will also load it from disk, if it hasn't already been loaded. The mouse cursor's sprite data is held in its own file in the system folder and initmouse uses the system file reference to know where to find that.

In general C64 OS uses a mouse–based UI. It provides a rich mouse–based event system that is designed hand–in–hand with the Toolkit, and it also provides a hierarchical pull down menu system. Together these make building an application with a big feature set or a complex UI reasonably simple. However, there are some kinds of apps that might wish to opt out of some of these provisions. C64 OS reserves the top row of characters for itself. There is a CPU busy animation that spins in the top left corner if an app goes into a long loop and stops responding to user input. Then there is the menu strip itself, and in the top right corner is the clock.

There is already a call to disable the mouse pointer, which short circuits the mouse driver so it stops reading the mouse and stops producing mouse events. There is also a set of system draw flags, three bits of which represent the three top row services. The CPU busy, menubar and clock can be prevented from drawing by disabling these flags via the setflags routine in the service module. loadapp calls this to re–enable these features, in case they were disabled by the previous app.

Applications also have the ability to tap into the system IRQ for timed events. loadapp resets the two vectors for custom timers back to their defaults.

Lastly, loadapp makes two more calls, the second of which it never returns from. The first is a JSR to initapp, and when this returns it does a JMP to evtloop. Both of these calls demand an entire section to describe.

Inside the Application

The reason it's so essential that an application is assembled and loaded to $0800 is because C64 OS expects the application to have its primary jump table starting at $0800.

Screenshot of code from home app 1

The jump table consists of a series of export vectors, exactly the same way each C64 OS KERNAL module begins with a table of vectors. In an application, they are as follows:

  • .word init ;Called with the application first launches
  • .word mcmd ;Called when a menu command is triggered
  • .word quit ;Called when the application is about to quit
  • .word frze ;Called when the application will be frozen
  • .word thaw ;Called when the application becomes active after being frozen

The init routine is therefore the first entry point of the application's actual ability to run any code. But remember, in C64 OS, like in modern OSes, the application doesn't ever just go into a hard loop polling for something like a key to be pressed. Instead, it sets up some UI and returns and lets the OS handle the main event loop. Which we'll get to shortly.

In order for an application to interact with the system at all, to be informed of mouse and keyboard events and to be told when to draw, it must push a screen layer. Typically what it would do is first create the basic UI by instantiating and assembling Toolkit views, and wiring up the event callbacks from some of those views to routines. Then it puts the pointer to the screen layer struct into .X and .Y and calls layerpush.

Screenshot of code from home app 2

The screenlayer struct consists of 4 pointers to: a draw routine, mouse event, key command event and printable key event handlers. The layer is then pushed on to a stack that supports 4 layers. The topmost layer (4th is topmost) gets event priority. The menu system, cpu busy and clock are hardcoded to draw on layer 4. When the application is initialized and it pushes its first layer, it is pushing layer 1, the bottommost layer. At that time, layer 2 and 3 are unassigned.

When a mouse event is generated, layer 4 has the first shot at processing it. Layer 4 delegates this task to the menu system. If the menu system decides the mouse is not interacting with the menus then the event is allowed to be processed by the next layer. 3 and 2 are skipped because they're not assigned and the mouse routine from layer 1, is called. This is code the application implements. So technically the application can do anything it wants. With the caveat that it shouldn't go into a long loop, it should always try to return back to the main event loop as quickly as possible so as to remain responsive to the user.

While the app can do whatever, typically the app will just foward the call to the toolkit. That's what the screenshot of this app is doing. All three of the screen layer's event pointers are set to "forward" which simply does a JMP to tk_proc. Similarly the draw pointer is set to tkdraw.

You might wonder, what's the point of this layer system if it ultimately just calls routines in the toolkit? Why doesn't the main event loop just call those toolkit routines directly? The short answer is flexibility. It is important that any OS strikes a good balance between easy and flexible. The Toolkit handles a tremendous amount of redundant work. And it's super easy to snap a few of the views together to get a useful, responsive and consisten UI. But if you were forced to use the Toolkit there might be things you'd want to do that would be very difficult.

The way I've set up C64 OS, imagine you are fully onboard with using Toolkit, but you want to have some special keyboard command that isn't represented by a menu item in the menu system. No problem. Set up the first screen layer exactly as illustrated above, except instead of setting the kcmd pointer to "forward", set it instead to some other routine. That routine will be called every time a kcmd is generated (and not already handled by a higher screen layer). Your routine can check to see if it is your special command and do something if it is, and if it isn't it can then pass it on to Toolkit. Boom, the app can inject event handling outside the context of Toolkit that easily.

Here's a more dramatic example. Let's say you want to use Toolkit, but you want to have a dedicated area of the screen, say the bottom 5 rows, completely free form and unmanaged by toolkit. No problem. When the init routine configures Toolkit's root view, you set its draw bounds so that it's height is just 19 rows high (25, minus 1 for the menu bar, and minus 5 for the free form area at the bottom) and aligned to the top. Toolkit will now never draw into that bottom area. Then you set the mouse routine of the screen layer to call your custom routine. That routine checks to see if the click is above the bottom 5 rows, if it is, call the Toolkit's mouse handler. Otherwise, proceed to analyze the mouse event however you please to work with whatever you're doing in the free form area.

A third and final example. A weird example, but just to show the possibilities. You want a button in the Toolkit UI to lock the screen for 5 minutes after you've clicked it. So you create that UI with Toolkit and create the button and wire the button to call a routine on click. That routine configures a system timer for 5 minutes (18000 Jiffys). Then, in the screen layer rather than merely forwarding to toolkit, you check to see if that timer countdown variable is zero. If it's not zero then you return without forwarding any events to Toolkit. If it is zero then you forward to Toolkit as usual.

What you want is the ease of being able to rely on the Operating System for all the things that it's good for, and you want the ability to do something custom whenever you want without having to fight or hack the system.

The Event Loop

After the app has been initialized, it has presumably pushed a screen layer onto the screen layer stack, and configured whatever UI it wants to set up. The initializer returns to loadapp which promptly JMPs to eventloop. The Eventloop will therefore never return to the loadapp routine.

The IRQ service routine is still firing every 60th of a second, the mouse has been enabled, so mouse clicks are producing mouse events, and the key presses are producing keyboard events. The main event loop loops infinitely performing the same steps over and over.

Screenshot of code from evtloop 1 Screenshot of code from evtloop 2 Screenshot of code from evtloop 3

When an app quits, somehow the event loop needs to exit, and not in a way which involved code at the end of a JSR, otherwise we'd have a stack leak. Instead, there is a loop break vector (loopbrkvec). When loadapp enters the event loop after loading an app, it enters at a small prelude that clears the loop break vector, before proceeding to step 1. However the loop loops back to step 1 skipping the prelude that clears that vector. Near the end of the event loop, step 5, checks to see if the loop break vector is set. If it's not set, it proceeds to step 6 and back to step 1 and continues on. If the loop break vector is set however, it does an indirect JMP through that vector. Therefore, it truly leaves the event loop never to return. This is what will be used for doing a proper application quit, however I haven't implemented that yet.

The Event Loop then performs Step 1, Step 2 and Step 3. They have a parallel structure so it's easy to explain. They handle the three primitive event types: Mouse, Key Command and Printable Key. First a JSR to the appropriate routine in the input module checks for the presense of an event on the queue. If the carry is clear, there is an event on the queue. If the carry is set it simply moves on to the next step.

If an event is on the queue it JSR's to proclayers. Proclayers is what controls the fact that the topmost layer gets a shot at the event first. If it's a mouse event, it calls the mouse callback for the topmost layer. This routine does whatever it wants. If it returns with the carry set, then the event has been handled and proclayers is finished and returns. If the carry comes back clear, proclayers loops to the next layer down, checks to see if its mouse routine is not null and calls that. This allows higher layers to always get precedence. And higher layers have the ability to deny the lower layers from ever even having a shot at the event. This can be used for model dialogs, and is also what allows, say, the menu system to prevent a click event from passing clear through the menu and into the Toolkit UI below it.

After proclayers returns, the event is immediately dequeued. This process repeats in step 2 and 3 handling keyboard events the same way. Key commands for example are routed to Screen Layer 4 first, which allows the menu system to pick them up. If the menu doesn't handle them, then they make their way to the lower screen layers, and the app can handle the key command outside the context of the menu system. The menu system, if the key command matches a menu keyboard shortcut, calls the Application's mcmd routine, as we saw configured in the Inside the Application section above.

The handling of events however should never cause the application to try to redraw itself to screen. If an event causes the visual state of the application to become out of date, the application should call the markredraw routine of the screen module. This sets redraw flags for the current layer handling the event. More on redrawing in a moment.

Step 4 of the event loop handles checking for an expired timer. The timer system is two–fold. There is a timer update vector, and a timer check vector. The timer update routine is called by the IRQ, and so it is called very reliably 60 times a second. When the timer ellapses however, that's not when the resultant code for the elapsed timer gets called. The Event Loop's execution time is less reliable, because event handlers could be poorly behaved and fail to return to the event loop in a timely fashion. Eventually though, when the event loop is executed, step 4 calls the check timer routine. If the timer is elapsed the check timer routine is free to call whatever code should execute as a result. Including possibly resetting the timer.

Step 5 of the event loop calls redraw. This redraw routine doesn't just immediately start trying to draw something. It checks to see if the mark for redraw flags are set. If they are, then it calls the draw routines on the layers starting with the lowest layer first. So the application's first screen layer push has its draw routine called. In our example this was set to forward to the Toolkit's redraw.

If the toolkit does something it shouldn't do, such as draw into the top menubar, this will be overdrawn almost immediately. Because the main redraw routine will call the draw routine on the next highest screen layer. This also allows the toolkit to trigger redraws as the result of a timer, even when the user has a menu open. The toolkit may then refresh some part of the screen that is hidden under the open menu. After it does its draw, the next highest layer gets to draw, and eventually layer 4 gets to draw, and the menu will be redrawn above the updated toolkit view.

There are some tricks I'm working on to make this fairly efficient. When the menu system is due to start drawing, for example, it will draw the lower three layers from scratch, and then buffer them to a memory area stashed under I/O. Then, unless the lower layers have their redraw bits set, it will refresh the screen by copying from the buffer, before redrawing the menus above that. This will allow the menus to be drawn above a complex Toolkit UI very quickly. There are some other tricks I'm working on as well. The Toolkit will keep track of which View triggered the screen layer to be dirty, and when it is asked to redraw, it can redraw just that one view. This will allow, for example, a user to type into a text field, and on each key stroke only that single text field will continually redraw itself, rather than the whole screen.

After the redrawing phase is complete, step 5 also handles drawing the CPU busy animation. It starts by resetting the animation character to the default state. Then it sets the jiffy time into a system variable indicating when the event loop last went through step 5. The IRQ service routine, which updates the Jiffy Clock, compares the current Jiffy Time to when the Event Loop last ran, and if more than a couple of seconds has passed, the IRQ service routine starts to replace the CPU busy character with the next frame of an animation set. Because the IRQ runs independently of the Event Loop, if some application code that handles an event decides it is going to do something for an extended time before returning to the event loop, the event loop will be unable to update the event time variable, and the CPU busy animation will automatically start ticking. This is, of course, subject to the system draw flags mentioned at the very beginning. An application is free to disable the CPU busy animation. But, when the next application is loaded, it will be automatically re–enabled.

Before step 5 finishes, it checks the loop break vector, as mentioned earlier.

Step 6 has one job. JMP to step 1! And that is pretty much all that the Event Loop does.

Last Comments

That's the basic start and life cycle of an application in C64 OS. It doesn't go into much detail about how drawing really works, or how to make a UI with Toolkit, or how an application will be quit and its memory deallocated. But, it does show how, starting with the Booter, the first application gets itself running and where it has the opportunity to set up its UI, and how it will respond to events.

Thanks for reading. As always, leave comments, questions and thoughts below.

  1. I talk a bit about file references in C64 OS here. I promise that I'll go into more detail on file references in the next post, unfortunately as of this writing, I haven't done that yet. []
  2. 230 characters maximum length for a path string. With 16 character folder names, plus delimiter, we get 230 / 17 = ~13.5 folders deep. If the folder names were all just 1 character, plus delimiter, we get 230 / 2 = 115 folders deep. Or an average of 8 characters, plus delimiter, for 230 / 9 = ~25.5 folders deep. I think this limitation is very acceptable for a C64. []
September 26, 2017Hardware

New C64c Cases Available

I am very excited! I began this blog, almost a full year ago, and 42 posts ago. That's an average of about 4 a month, or one a week. I'm pretty pleased with my ability to consistently add new content to this site. But that's not why I'm so excited. My inaugural post was a mere 58 words. I posted about my eager anticipation of the new C64c cases in a variety of awesome retro color schemes. Here's my original, very first post in its entirety:

These new cases look amazing. I can't wait to get my hands on them.

This is a perfect example of what's great about the C64 in 2016. New hardware is still being manufactured, that looks stylish and new, but true to form at the same time.

I've heard from Jens Schönfeld, personally, that these cases are almost ready.

It took almost a year, but these cases have just recently become commercially available. I should probably add them to the Commodore 8-Bit Buyer's Guide. Feast your eyes on this gorgeous sight.

A beautiful box for your new C64c case
A beautiful box for your new C64c case

Close up, angled shot of new C64c case, SX-64 Style
Close up, angled shot of new C64c case, SX-64 Style

Full frontal of the new C64c case, SX-64 Style
Full frontal of the new C64c case, SX-64 Style

You can order them today from Pixelwizard Retro Shop: https://shop.pixelwizard.eu/en/commodore-c64/

These beautiful C64c cases come in 4 styles:

  • Breadbin Grey
  • Classic Beige
  • Retro Black
  • SX-64 Style

I've got not shame in confessing that to my eyes SX-64 Style is the sharpest. Either for reasons of manufacturing difficulty or simply to shape demand, the SX-64 Style case is also 10 € more than the other three.

This brings me to the price. They're not cheap throw–aways! But, I'm a nerd, you're a nerd, and we both know these precious jewels are worth the dough.

They're 59 €, plus 5 € for an additional (recommended) shipping box. Plus shipping fees, which vary depending on how many you order and where they're shipping them to. The SX-64 Style's base price is 69 €. So, all told, for the SX-64 Style I'll be looking at maybe 89 €, which is 132$ CAD.

It's the perfect home for a C64 Reloaded MK2, which is currently available only as pre-order, for 184,95 €. I look forward to doing a write–up about this too, when it becomes available.

What a fantastic time to be a C64 user!

Older PostsClick titles to see full content

The home page shows the full content of the 10 most recent posts.
Below are the titles of 5 posts older than the most recent 10.
Click their titles to view the complete post and read and leave comments.

September 18, 2017Programming Theory

Organizing a Big Module

September 11, 2017Programming Theory

Toolkit Introduction

August 15, 2017Programming Theory

Organizing Module Layout

August 4, 2017Programming Reference

6502 / 6510 Instruction Set

August 4, 2017Programming Reference

Commodore 64 PETSCII Codes

Archive

Full Archive of Past Posts…

Featured Posts

C64OS.com has grown to be more than just a blog about one developer's progress, it is becoming a resource to surround and support a type of C64 user that wants to benefit from the Commodore community and get the most out of modern hardware expansions for their beloved platform.

After writing many posts on the C64 OS weblog, the unfortunate reality is that some of my best work gets lost in the stream of news and developments. Be sure not to miss these full–length editorial reviews:

December 20, 2017Editorial

World of Commodore '17

May 16, 2017Editorial

Review: FREEZE64 Fanzine

December 5, 2016Editorial

World of Commodore '16

Programming Reference

January 19, 2018Programming Reference

Vic-20 / Commodore 64 SuperChart

August 4, 2017Programming Reference

6502 / 6510 Instruction Set

August 4, 2017Programming Reference

Commodore 64 PETSCII Codes

August 3, 2017Programming Reference

Commodore 64 Screen Codes

Search

Needs some ideas? Trying searching for:
PETSCII, Animation, Memory or Pointers

Recent Posts

January 19, 2018Programming Reference

Vic-20 / Commodore 64 SuperChart

December 20, 2017Editorial

World of Commodore '17

December 5, 2017Software

The Big 5 Oh

November 24, 2017Software

BASIC Wedge Programs

November 22, 2017Hardware

3D Printed Components

November 15, 2017Technical Deep Dive

Anatomy of a Koala Viewer

October 31, 2017Programming Theory

Passing Inline Arguments

October 23, 2017Technical Deep Dive

How the C64 Keyboard Works

October 18, 2017Programming Theory

A C64 OS App's Start Of Life

September 26, 2017Hardware

New C64c Cases Available

September 18, 2017Programming Theory

Organizing a Big Module

September 11, 2017Programming Theory

Toolkit Introduction

August 15, 2017Programming Theory

Organizing Module Layout

August 4, 2017Programming Reference

6502 / 6510 Instruction Set

August 4, 2017Programming Reference

Commodore 64 PETSCII Codes

August 3, 2017Programming Reference

Commodore 64 Screen Codes

August 1, 2017Programming Theory

Base Conversion in 6502 (2/2)

July 21, 2017Hardware

Commodore Logo Mark Patch

July 5, 2017Programming Theory

Object Orientation in 6502

June 30, 2017Programming Theory

Base Conversion in 6502 (1/2)

June 20, 2017Software

Huge Site Update

June 5, 2017Software

Recursive File Copier in BASIC

May 29, 2017Technical Deep Dive

Implementing Factorial in 6502

May 16, 2017Editorial

Review: FREEZE64 Fanzine

May 9, 2017Programming Theory

Pointers in Practice, Menus

Older Posts

Full Post Archive