LAUGHTON ELECTRONICS

The KimKlone: Bride of Son of Cheap Video

Op-Code Mapping - the old instructions and the new



This section examines binary encodings for KimKlone instructions, and how the codes get executed by KK hardware. Naturally the 65C02 instruction set is preserved. And, among the new instructions, some use encodings that are assigned rather arbitrarily; others, not. It's all about reaping benefits from the chip's cast-in-silicon peculiarities.

The original NMOS 6502 featured 151 op-codes. That left more than a hundred op-codes undefined, and executing an undefined (or "illegal") op-code on the old NMOS silicon would result in undefined behavior. (Eventually the undefined op-codes were documented extensively — but by hobbyists and experimenters, not officially by the chip manufacturers. The illegal NMOS op-codes exhibit bizarre behaviors that range from marginally useful to downright catastrophic.)

Later, when the Rockwell 65C02 was created, its designers gave it several new instructions, as well as a new addressing mode. In doing so, they raised the number of legitimate op-codes to 210 — and reduced the number of illegal codes to 46. In addition, they made a deliberate effort to "fix" the new chip so illegal op-codes would execute harmlessly. They wouldn't crash the chip, nor even alter any registers. But being harmless doesn't necessarily mean the undefined operations are NOPs. My experiments with the 65C02 revealed two categories.

32 of the undefined op-codes merely increment the Program Counter. I call these the "boring" NOPs. They don't seem to be good for much — they are one-byte do-nothings. (On the plus side, these guys execute in just a single cycle... which makes them capable of doing nothing at a very high rate of speed!)

The other 14 undefined op-codes are rather more interesting. These ones execute by fetching one or two operand bytes following the op-code... and, in some cases, by generating memory access cycles using those operands!

This is not the behavior we expect from a bunch of purported NOPs. However, I noticed the operations were always benign: the memory access was always a read (never a write), and no processor registers were changed other than the PC advancing. This is faintly paradoxical. No change in the registers means the data from the memory access didn't go anywhere; it was fetched and then thrown away. I call this the "Load And Discard" operation; I guess the mnemonic would be "LDD" !

It turns out the "interesting" NOP category includes thirteen codes for LDD. More intriguing yet is the fact that four different addressing modes come into play. There are

  • 7 op-codes for LDD Immediate (02 22 42 62 82 C2 E2)
  • 2 op-codes for LDD Absolute (DC FC)
  • 3 op-codes for LDD Zero-Pg,X (54 D4 F4)
  • 1 op-code for LDD Zero-Pg   (44)
I knew that the Load-and-Discard operations could be put to use. As for the "boring" NOP's, although they themselves are almost useless, at least they acted as placeholders. There's huge potential in the fact that their op-codes do not appear in pre-existing software.

KimKlone Op-Code Mapping

Obviously the design challenge with the KimKlone was to give meanings to the undefined codes: to associate them with useful operations. Here's an example:

In KK terms, the two-byte instruction 42 11 means Load register DP0 with immediate data — in this case 11h.  (I chose the op-code 42 partly in tribute to Douglas Adams's Deep Thought computer but mostly because 42h is a Load-And-Discard op, immediate mode.)  Upon execution the CPU fetches the 42h op-code and then, on the second cycle, obediently fetches and discards the Immediate byte which follows. But also on the 2nd cycle, microcode blips a control line that copies whatever's on the data bus (11h, the Immediate operand) into the DP0 register. This satisfies the job description for the LDDP0 instruction, Immediate Mode. The example is an instance of giving new meaning to a previously undefined op-code. I used the other Load-And-Discards as well, including those with other addressing modes.

Despite piggybacking on the CPU's phantom load operation, some of my new ops are actually store operations (for copying my new registers back to memory). The trick with that is that, on the final cycle — the data access for the LDD — microcode forces a low onto the R/W line going to memory. Neither memory nor the CPU asserts the data bus, and microcode cues one of my new registers to drive the bus instead. The data gets discarded by the CPU but is successfully written to memory.

This explains the general procedure for KK loading and storing its new registers to and from memory. Microcode determines what'll happen on the data bus. The CPU's job is simply to generate the address.

KimKlone Op-Code Re-Mapping





op-code substitution logic
Schematic excerpt (simplified) showing how certain op-codes are intercepted and replaced as they are passed along to the 65C02. Microcode is fed from upstream of the substitution, and thus has full knowledge of the true op-code.

Making use of the boring NOP's requires a slightly fancier technique. Luckily the boring NOP's are plentiful (there are 32 of them), but the problem is that they are NOP's (the CPU doesn't even do a Load-And-Discard). If they're given straight to the CPU "as is," they do nothing. But... (and here the plot thickens...)

The KimKlone uses a brilliantly sneaky trick I learned from two classic Don Lancaster publications, The Cheap Video Cookbook and Son Of Cheap Video. (See Cheap Video à la Lancaster ) What I learned from Cheap Video is that it's really easy to lie to the CPU when it's trying to fetch an op-code from memory! By "lying" I mean hiding what's actually there and making an alternative representation. The trick is simply to create a momentary disconnect between memory and the CPU, such that the byte on the memory data bus isn't reproduced at the CPU. (See the diagrams, left.)

The 32 boring NOP's are very easily recognized. That's because they all conform to the binary pattern xxxxx011. (Each "x" character shows the position of a "don't care" bit.) Notice that the upper five bits can vary, but the "boring NOP" op-codes always have binary 011 in the three least-significant bits. KimKlone hardware recognizes the xxxxx011 patterns and uses them to trigger a substitution.

During op-code fetch cycles the bus will be disconnected if the byte being fetched matches the xxxxx011 pattern. A 32 by 8 TTL PROM (82S123) takes the high 5 bits (the xxxxx) and reads out a corresponding substitute op-code chosen to suit my purpose. In this way, each of the 32 boring NOP's has an associated op-code that gets fed to the CPU. (KK rams are fast enough to leave a margin for the substitution delay; alternatively a wait state could have been provided.) I'll give an example of a KK instruction with a substituted op-code.

D3h is the KimKlone op-code for LDA_ABS_DP1. It's the familiar LDA abs (load accumulator using absolute address mode), except that Data Pointer 1 says what bank to fetch from. If DP1 holds 12h, say, then the three-byte instruction D3 56 34 means LDA from memory at 123456h. (Note the 24-bit address!)

This is a four-cycle instruction. On the first cycle memory fetches the D3. Because it's an op-code-fetch cycle, and D3 in binary matches the xxxxx011 pattern, the D3 never reaches the CPU bus. The disconnect prevails and the PROM sends ADh to the CPU instead, which is simply the 65C02 op-code for LDA abs.

The second and third cycles fetch the operand bytes, 56h and 34h, to the CPU without incident; then on the fourth cycle the memory read occurs. The CPU places 3456h on the low 16 address lines, and microcode selects DP1 which places 12h on the high 8 address lines. Memory responds with the contents of 123456h, and the CPU inputs that byte from the data bus to the Accumulator. That concludes the instruction; the next cycle will be an op-code fetch.

Op-Code Mapping Summary

To recap, of 256 possible codes...

 • 210 are standard 65C02 codes. These execute normally (except memory reference instructions which have been preceded by a prefix).

 • 14 are "interesting" NOP's — the Load-And-Discards. Seven of these are LDD using Immediate mode (one operand byte is fetched following the op-code). KK uses three of the LDD Immediates to load Pointer Registers and two simply to trigger control pulses (for SINC and DINC instructions, discussed later). Two LDD Immediates (62 and 82) are unused. Of the other seven LDDs, six use conventional address modes other than Immediate. Microcode can finagle any of these into a read or write of one of the new registers. Finally, op-code 5C consumes 3 bytes and 8 cycles but fails to generate a useful address; it remains interesting but useless.

 • 32 are "boring" xxxxx011 codes — the 1-byte, 1-cycle NOPs which are subject to substitution.

The xxxxx011 codes granted a lot of flexibility, because I had the design freedom to alias them arbitrarily. I had the PROM translate some of them to standard 65C02 codes and others to Load-and-Discards. In fact, some of the LDD's are each multiply aliased from more than one xxxxx011 op, with microcode treating each alias differently. It's the unique, un-substituted version of an op-code that determines what the microcode will do.

The KK goings-on that accompany the xxxxx011 ops get pretty gnarly, as you will see in the following section.

< Previous Page   KK Index     Next Page >
visit
LAUGHTON
ELECTRONICS
Home
Commercial& Manufacturing
Stage&Studio
Laboratory&OEM
copyright notice (Jeff Laughton)