NEWS, EDITORIALS, REFERENCE
The 6510 Processor Port
The 6510 is a variant of the 6502 CPU produced by MOS Technologies. People who have heard of the 6502 often associate it with the classic systems, NES, Apple II and C64, for example. The 6502 was used in the VIC-20, and also used in disk drives like the 1541, but the C64 is actually powered by the 6510.
There isn't a huge difference. The 6510 has the same instruction set as the 6502, so any books and tutorials about programming the 6502 apply (almost without exception) to the 6510 as well. So why does the C64 use the 6510 then?
The reason is because the 6510 includes an additional 6-bit port. In the context of the C64 as a whole, this is referred to as the Processor Port, because it's the port found on the processor. The 6510, like the 6502, is an 8-bit CPU. But what does that 8-bit refer to? Its data bus is 8-bit's wide, and most of its internal registers, Accumulator, X and Y index, Status and Stack Pointer are all 8-bits wide. Also, all of the arithmetic instructions, add and subtract, rolls, shifts, and logical AND, OR and EOR, all operate on 8-bits at a time.
The address bus, though, is 16-bits wide and for this reason the Program Counter is 16-bit as well. This allows the CPU to read and write 8-bits at a time from 65,536 unique addresses, which is the equivalent of 64 kilobytes of addressing space. The C64, famous for its namesake, comes with 64 kilobytes of RAM. But it doesn't come only with 64 kilobytes of RAM, or it wouldn't be able to do very much. It also has a SID chip and a VIC-II chip, a pair of CIA chips for general I/O, and a special static RAM chip for color data. It's also got ROM chips for the Character Set, BASIC and the KERNAL that collectively give it the blinking blue READY cursor.
But something is amiss. If the CPU can address 64 kilobytes of space, and the C64 has 64 kilobytes of RAM, PLUS all that other stuff, how does the CPU manage to talk to all of it? BASIC and the KERNAL span 8K of addressing each, the Character Set is 4K, the VIC and SID and CIAs need addressing space too, so you end up with 64K + 8K + 8K + 4K + 4K that's 88K of addressing. How can a CPU with 64K of addressing space address 88K worth of stuff?
The 6510's built-in port plays a critical role.
What is a Port?
Remember, one bit is one wire. A port is a wire that can be connected to something, for which the computer has direct or indirect control over that wire's digital voltage level.
Since the 6510 and most other things in a C64 run at 5 volts, a port is a wire which the computer can drive up to 5V, or sink to ground, programmatically. A 3-bit port would be a collection of 3 such wires. An 8-bit port is 8 such wires, and so on. The 6510 has a 6-bit port. So the 6510 has 6 wires, then, that it can programmatically drive high or low, independent of whatever else it is functionally performing.
Slightly more sophisticated than this, a port can be an input or an output port, and with a bit more sophistication some of those bits, some of those wires, can be used for input while some of the others are used for output, at the same time. The 6510's port is like this. Each bit can be independently configured for either input or outupt. If a port line is set for input, then the computer can read the voltage level, discretely as either high or low, based on the voltage applied to it.
Here is the pinout diagram of the 6510:
The numbers inside the package, from 1 to 40, are the physical leg numbers. The letter, number and symbol labels outside the package tell us what each chip leg does. We read above that the 6510 has an 8-bit data bus and a 16-bit address bus. Together these make up 60% of all the legs. They are labeled D0 to D7 for the data bus. And A0 through A15 for the address bus. In addition we see the labels P0 through P5. These are the so-called Processor Port: 6 bits, independent of the address and data bus.
As was discussed in some detail in How the Keyboard Works, the C64 makes use of two general-purpose I/O chips, the CIAs, or Complex Interface Adapters, to communicate with most peripherals. Each of these chips has two 8-bit bidirectional (input and output) ports. But these chips are independent of the CPU, out there on the address bus, the mysteriously overloaded address bus. In order for the CPU to set or to read one of those ports, it must do so indirectly by reading and writing to the CIA via the data bus and address bus.
The Processor Port is much more direct. The port is embedded into the CPU itself, so it does not have to communicate via the data and address busses to read or write to to it. Instead, it dedicates two special addresses just to controlling this port. The lower 6 bits of address $0000 are used to set the direction of the corresponding port legs. Meanwhile, the lower 6 bits of address $0001 are used to physically read and write the data. For this reason, $0000 is called the data direction register, and $0001 is the data register.
What does the Processor Port Control?
For this, we turn to the C64 Schematic diagrams. A large, fold-out, double-sided paper version of this schematic was included in one of the copies of the C64 Programmer's Reference Guide that I have. However, digital versions of the schematics and other variations are available on the web, here for side 1, and here for side 2.
To improve clarity and focus on just the parts we care about, I've carefully removed all of the connections that aren't relevant to this discussion. Here's what the CPU's port is hooked up to.
In order to make a schematic diagram more… well, schematic, the leg labels are not laid out in the same order that they are physically found on the chip. Here we see all the address and data bus lines laid out down the right hand side. At the top right and along the top edge are the 6 Port lines. Let's take a closer look at what these are connected to.
The Cassette Port
You can see that the 6 bits are divided into two groups, 0, 1, 2 (along the upper right side) and 3, 4, 5 (along the top edge.) You can see, quite clearly, that the three upper bits, 3, 4 and 5 go up and over to the cassette port. How interesting! The C64 uses the 6510 itself to operate the cassette drive, or datasette. The VIC-20 doesn't use the 6510, so the code in the KERNAL that implements the drivers for the datasette must be different between the C64 and VIC-20.
The most complicated part I left in this schematic is all that hairy analog transistor logic inside the yellow box. But, don't worry about that. I put it inside the yellow box to show that all of that stuff can be considered as if it were one component. With one digital input (on the left), and one digital output (on the right). Plus some ground connections and a +9V connection. Remember that the 6510 operates at 5V. That means, each of its port pins can be set to either 0V or +5V. The problem is that the cassette port needs to drive the datasette's physical motor, and to do that it needs 9 volts. Everything in the yellow box is just a fancy contraption to convert the 5V port to a 9V output.
UPDATE: September 13, 2022
It has been brought to my attention that, technically speaking, this contraption is not converting but switching. When the 5V input goes high, it activates the transistors in order to switch on the 9V supply to the cassette motor. Thanks to Timothy Johnston for that clarification.
Let's look at the cassette port itself. From the outside you can see that it is an edge connector with 12 pins. 6 on the topside of the C64's mainboard and 6 more on the bottom side. But as the schematic plainly shows, there are only 6 distinct lines. The lower half, A, B, C, D, E, and F are electrically connected to the corresponding lines on the upper half, 1, 2, 3, 4, 5, and 6. Lines 1 and 2 are for ground and +5V. These power such things as the read and write head and perhaps the red save LED. These are also the lines that are usually tapped for other devices like the uIEC Deluxe Daughter Card, or for adapters like DataPwr or C2N Power, found in the Hobbyist section of the Commodore 8-Bit Buyer's Guide.
Cassette port lines 4, 5, and 6 are the read line, write line and sense lines respectively. And line 3 is the cassette motor line, with the 5V to 9V step-up regulator crammed in the middle.
Wait a second. The cassette port has 4 digital lines, but uses only 3 bits on the Processor Port. You'll notice that the cassette read line doesn't connect to the Processor Port. It disappears off the left edge of this schematic. In reality, it joins up with the read line from the IEC bus, and both connect to the FLAG pin of CIA 1 (not shown here), which drives that chip's IRQ line. The IRQ line of CIA 1 in turn is hooked back up to the CPU's IRQ line. But, how exactly the datasette is timed for reading and writing is beyond the scope of this discussion.
With Processor Port pin 3 connected to the cassette port's pin 5 write line, and Processor Port pin 5 connected to the cassette motor aparatus, the CPU can directly cause the motor to turn, and can, with careful timing, write data to the tape media as it rolls along. That's pretty cool.
The cassette port's sense line is connected to the Processor Port pin 4. But there is an extra little nubbin on this line too. It's a pull up resistor. When none of the datasette buttons is depressed, the sense line is electrically floating, connected to nothing, which is to say it is in state HighZ or effectively infinite resistance. Whenever the line is in HighZ, the pull up resistors pulls that port line up to +5V. The physical buttons, then, must connect the sense line to that cassette port line 1 ground, pulling the Processor Port pin 4 low whenever a datasette button is depressed.
Here's an interesting little discovery I made while considering this. Unlike on a regular tape recorder (remember those?), the buttons do not directly cause the tape motors to turn. They only connect the sense line to ground, and the computer is ultimately in control. The IRQ service routine, in the C64's KERNAL, monitors the state of the sense line and if it's low, (and some other software conditions are met,) then it turns on the motor line. There is, however, only one sense line, but three buttons affecting the motor. Play, Fast Forward and Rewind. It got me to wondering, how can the computer differentiate the buttons? As it turns out, it can't!
Try it out. (Without JiffyDOS, since JiffyDOS doesn't support the datasette at all.) Type LOAD and hit RETURN. When it asks you to push play on tape, push rewind or fast forward instead. Not only does the cassette motor come on, but the screen blanks and it starts searching for the first program. It must just assume that you actually did as you were told.
So the cassette port occupies the upper 3 bits of the 6510's Port. Now, what about the lower 3 bits?
According to the schematic, (shown again below for reference,) lines 0, 1 and 2 are connected to three bubbles with right-pointed arrows, labeled /LORAM, /HIRAM and /CHAREN, respectively. What's up with the 3 pointy bubbles? These indicate that the connections continue on the reverse side of the page at the matching bubbles with the same labels.
Before we go there, though, we should note that each of these three lines has a pull up resistor (R43, R44, R45) to +5V. Notice that the labels have a bar across the top. In text we put a slash before the name (i.e. /CHAREN) to indicate the bar. The bar means they are active when pulled low. So, CHAREN, (we'll get to what that means shortly,) is activated when this pin is pulled low. But the pull-up resistors default them to be high. It may seem a bit strange to see these here, though, because these lines are hard-wired to the CPU's port. Why would they need pull-ups?
I'm going out on a limb here to guess that they handle the situation (the useless situation?) where someone sets the 6510's data direction register to make these three bits inputs rather than outputs. If these 3 bits were set as inputs, the pull-ups would kick in, deactivating LORAM, HIRAM and CHAREN, and simultaneously causing those input bits on the CPU's data register to read as high.
UPDATE: August 20, 2020
I have since discovered and discussed the reason why these pull-up resistors are here. Read more about about this in the post: Load and Run from 6502 ASM.
Let's flip the page on our schematic now, and see what these three lines connect to on the other side.
Again, I've edited this to remove distracting detail.
From the 6510's perspective (the VIC-II has another story to tell) the C64's addressing space is divided into several regions. These will be familiar from previous posts where I've mentioned them and even included some nice diagrams.
|Region Name||Address Range||Size||1st Function||2nd Function||3rd Function|
*"I/O" includes all of the following chips: VIC-II, SID, CIA 1 and 2, as well as the special static color ram chip, and two blocks dedicated for external expansion via the cartridge port.
To summarize, then, there is RAM that doesn't share any addressing, split between two regions, the bulk in low memory and a small chunk in high memory. Plus there are 3 contentious regions, where memory is shared with something else, named for what shares that space: BASIC, I/O1 and KERNAL.
In the schematic above, the centerpiece is the chip labeled PLA (U17), the Programmable Logic Array. Into its left side come the 3 pointy bubbles direct from the Processor Port's lower 3 bits. And out the right side come connections to: the BASIC ROM, the KERNAL ROM, the CHARACTER ROM, and to the lower right the 74239 (U15) which decodes the addressing for the I/O chips. The PLA is also fed some address lines and has outputs to the RAM and to the expansion port. These connections are not shown, for simplicity's sake, but allow the PLA to disable RAM and enable one of the ROM chips or I/O, depending on the address being accessed.
Thus, by means of the PLA, the low 3 bits of the Processor Port can control what the 6510 will find in each of the contentious regions. The question is, how are those 3 bits used?
I found the role of the Processor Port bits to be quite confusing when I first tried to understand them. And quite frankly, with the exception of /CHAREN, I still find the labels /LORAM and /HIRAM to be confusing and misleading. But, now that I've been programming extensively with code behind the KERNAL and BASIC ROMs, which need to make use of both, and sometimes to make use of I/O, I now have an intimate personal experience with these bits. And not only do they make intuitive sense to me, but they're incredibly easy to use, and I don't think that's an accident. Working with them has led me to have a much deeper appreciation for how they were laid out.
And now I want to impart that wisdom on you.
Using the Processor Port to Configure The PLA
At first I read a verbal description of how they're used, and it makes no sense. What is mapped where as a result of these 3 bits seems almost chaotic. Eventually I found the table, shown below, in the book Advanced Machine Language for the Commodore 64. I love that book. I shoved a bookmark into page 133 and referred to this table often.
Advanced Machine Language for the Commodore 64 — Abacus Software — Archive.org
But after working with the Processor Port and memory mapping for a while, all of a sudden the bits made sense to me. Here's how they work.
Ignore bit 2 for now, and let's just focus on the low two bits, bit 0 and bit 1. Two bits give a possibility of 4 combinations, 00, 01, 10, 11, the binary values for 0, 1, 2, 3. Meanwhile, the three contentious addressing regions are not independent, but rather they depend on each other in a perfect chain. Here's the hierarchy:
0) RAM doesn't depend on anything. RAM is RAM, it can hold whatever you want to put in it.
1) I/O doesn't depend on anything either. So I/O can be mapped in all by itself. You can think of this as Demo mode, or Game mode. Demo and Game coders don't give a crap about BASIC or the KERNAL, they want to have as much memory available for their own routines and data as possible. But, of course, they need I/O, because they need to produce graphics, play music, get input from the keyboard and joysticks, and transfer data to and from disk.
2) The KERNAL, on the other hand, needs I/O. The KERNAL's interrupt service routine scans the keyboard, but it needs CIA 1 to do that. It implements the IEC routines and the RS232 routines, but it needs CIA 2 to do any of that. Very bad things would happen if the KERNAL were patched in while I/O was not.
3) Finally, BASIC is a high-level front end to the routines found in the KERNAL. BASIC, therefore, depends heavily on the KERNAL. Not the other way around though, the KERNAL can happily be used without BASIC (the default mode for C64 OS is KERNAL and I/O on, but BASIC off.)
So you have a perfect dependence hierarchy. BASIC → KERNAL → I/O → RAM. This is not so obvious in the table from the Advanced ML book, because the table lays out the columns according to where these regions fall in memory, rather than how they depend on one another. Then it throws in the Character ROM which adds another layer of confusion. It is WAY simpler than it looks.
|Dependence Level||Binary Value||I/O Region||KERNAL Region||BASIC Region|
Now THIS table makes perfect sense. The binary value of the 2 bits is just the "dependency level" and each time you increase a level, it patches in the next thing in the dependency hierarchy. It's very straightforward. But, it gets even better. Because those 2 bits are the lowest 2 bits of the Processor Port, you can simply INC or DEC the address ($0001, which is in zero page so you can use ZP addressing mode with just $01.)
The A, X and Y registers are not involved in the INC and DEC operations, making them very convenient to use. Suppose some code is being run from behind the KERNAL ROM, this is where C64 OS Utilities run. C64 OS puts the C64 into "ALL RAM" mode before jumping into a Utility's code. But let's say the Utility wants to change the border color. It must patch in I/O, the next dependency level. It can simply do this:
None of the registers get disturbed, none of the higher bits in the Processor Port need to be worried about. It simply patches in I/O, writes a byte to the VIC chip, and patches I/O back out to return to a known consistent state.
Let's say you're in an Application's main code. Application code runs from main memory (low memory.) And while Application code is running the mode is KERNAL and I/O patched in (level 2.) So let's say your code needs to call some routine in BASIC, super easy:
The INC $01 brings in BASIC, you call a BASIC routine, the DEC $01 cleanly brings us back to the known consistent state again.
Or, let's say I'm in a C64 OS KERNAL routine, the default state, like that for Applications, is KERNAL and I/O patched in (level 2.) But let's say the routine needs to put something into the RAM beneath I/O. We've got to go down two levels of dependency. How hard can that be?
It's unbelievably easy. It is so much more straightforward than the books make it sound.
The Character ROM
Usually when a book talks about the Processor Port and how it maps those 3 contentious regions, they can't help but toss into the table the role of bit 2 and its effect on the Character ROM. This, in my opinion, is a mistake.
The Character ROM is special and so is its controlling bit. When the VIC-II can see the Character ROM (in 2 of 4 banks), the VIC-II always sees it, regardless of how you configure the PLA. The PLA's configuration is only for what the 6510 can see. So the VIC-II will happily access the Character ROM as it needs to and you and your code virtually never have to worry about it.
But sometimes, occasionally, it can be useful for your program to be able to read the data out of the Character ROM. For example, if you want to copy the default character set to RAM in a different VIC-II bank, so you can modify one or more of the characters. But there is no program that needs to go back and forth casually accessing the Character ROM here and there. Unlike how your program might need to switch back and forth between, say, I/O and the RAM beneath I/O. That's a good thing.
The Character ROM can be filled into the place of I/O, but as far as the dependency levels are concerned, the KERNAL cannot work when the Character ROM is there, because it needs I/O. But then, BASIC can't be used either, because it needs the KERNAL, which needs I/O. In other words, the Character ROM, despite the fact that it plops in over top of I/O, is only ever usable momentairily to pull some data out of it. Generally speaking, it is so disruptive to have the Character ROM mapped in that you have to mask interrupts.
Thus, bit 2 of the Processor Port should not be considered as part of the low 2 bits as described in the section above. You can't just casually increment and decrement your way into and out of the Character ROM. Instead, bit 2 should be considered a special flag. When that flag is active, the Character ROM is perfectly substituted for I/O. Activating the flag doesn't mean the Character ROM is visible, it means it's visible where I/O normally would be, IF the other bits are set such that I/O would be patched in.
Thus, the /CHAREN label on bit 2 of the Processor Port makes sense to me. It's a flag meaning "Character ROM Enable". And, it's low-active (indicated by the bar), so the Character ROM substitution happens when bit 2 is low. If the CHAREN flag bit is low, here's how the "Dependency Levels" look:
|Dependence Level||Binary Value||I/O Region||KERNAL Region||BASIC Region|
In my estimation, while the CHAREN flag is low, the distinction between dependency levels 1, 2 and 3 effectively doesn't matter. Maybe there is some very self-contained routine in one of those ROMs that could be used to help copy data from one place to another, but besides that, nothing in the KERNAL and BASIC ROMs is useful or usable as long as that CHAREN flag is low.
For a long time I simply used the table in the Advanced ML book to jump into the configuration I wanted to have. For example, let's say you want to have KERNAL and I/O, but you don't want to modify anything else. You could do this:
You can do that. It works. But, it takes 4 steps, involves what looks like voodoo, and worse yet it disrupts the accumulator (unless you push and pull from the stack before and after, which takes even more steps, memory and time.)
The way to go is to always and consistently know what state you're in. Then, moving from that known state to a new state is never more complicated than to INC $01 or DEC $01 the number of levels you're trying to move. To maintain consistency, you just have to reverse whatever you did. This is not that hard. It's a lot like how you deal with the stack. Whatever you push onto the stack, you have to pull it back off before you return to sender.
Have a good weekend, and enjoy your Commodore 64!
- Bearing in mind that the I/O region can also be used for the Character ROM, under rare and short-lived circumstances. [↩]
Do you like what you see?
You've just read one of my high-quality, long-form, weblog posts, for free! First, thank you for your interest, it makes producing this content feel worthwhile. I love to hear your input and feedback in the forums below. And I do my best to answer every question.
I'm creating C64 OS and documenting my progress along the way, to give something to you and contribute to the Commodore community. Please consider purchasing one of the items I am currently offering or making a small donation, to help me continue to bring you updates, in-depth technical discussions and programming reference. Your generous support is greatly appreciated.
Greg Naçu — C64OS.com