Note that there are many ways to do most of these things, and probably just as many that a newcomer might think of that are DOA for various reasons. The goal of this page is to make your home-made computer a useful "Swiss army knife" of the workbench, rather than just a novelty for fun only, or a consumer item. There's no OS or kernel for it on my website yet, but I hope to publish my 6502 and 65816 Forths as time allows.





On this page:

Notes:

Three clock options are shown. You will need to pick one. Do not install the parts for all three on the same board. For using an oscillator can that goes into a 14-pin DIP socket, omit C4, C5, C6, Y1, and R4.

For connecting a crystal directly to the processor, omit U3 (the oscillator can) and C4, and make R4 220K. The data books say to make C5 and C6 51pF and make R4 200K, but more-standard values are 47pF and 220K which I suspect will work fine. The books also say 1MHz; and although I suspect you could go much higher and it would still work, I don't know how high.

For using a resistor and capacitor for a non-speed-critical timebase, omit U3, C5, C6, and Y1, and make C4 68pF and make R4 5.6K to get approximately 1MHz. You could vary this also but again I don't know how high you can dependably go. You won't hurt anything by trying. NOTE about the RC and crystal options above: WDC no longer tests or specifies the gate delays between Φ0 in, Φ1 out, and Φ2 out for their newer 65c02's, and they would prefer that the designer use the external oscillator option. All the same internal inverters seem to still be in place however, so I have little doubt that the circuit above will still work fine; but I had to pass the info on. If you do it per WDC's preference, the output of your external oscillator (probably an oscillator can) goes to everything requiring Φ2, and pins 3 and 39 go unused.



The reason the quad NAND gate is a 74HC132 instead of a 74HC00 is that the reset circuit needs the schmitt-trigger input. The '132 will be a little slower than the '00, but there's a maximum of only two gate delays and that's in the I/O-select circuit. It will be plenty fast for operation at a few MHz. You might be able to find a 74AC132 which would be faster than the 74HC132.



For the power input connector J1, choose a type that will not let you accidentally connect the power backwards. DC-10 jacks are popular.



JW1 selects which interrupt input you use to the processor. If you stay with only one I/O IC, you will normally use only IRQ , not NMI . For this, connect JW1 pin 2 to pin 3, and also pin 4 to pin 5. If you add more I/O ICs later, you may want jumper selections for each one; but be sure to observe the methods in the IRQ/NMI connections section.



JW2 needs to be shorted for non-WDC 65(c)02's. For WDC, leave it open, because it is a vector-pull output instead of a ground connection.



JW3 selects whether you're using a 27c256 EPROM or a 28c256 EEPROM. For the 27c256, connect 2 to 3, and 4 to 5. For the 28c256, connect 1 to 2, and 3 to 4. Slight additional circuit complexity will be needed to allow the computer to write to its own EEPROM (see Daryl Rictor's example code in this forum post), using a WR signal as shown near the bottom of section 6 of this primer, on clock generation.



Addresses will be: RAM: 0000-3FFF (using half of a 62256 32Kx8 SRAM. Running OE to A14 keeps the top half from interfering.)

I/O: 4000-7FFF (but the 6522 VIA shown above will be addressed at 6000-600F. More on additional I/O below.)

ROM: 8000-FFFF (using all 32KB of EPROM or EEPROM)

If you don't ever add another I/O IC, another way to do the address decoding would be to connect A15 to the VIA's CS2 , and A14 to its CS1. This removes one level of gate delay in the address decoding without changing the above addresses, but precludes ever adding more I/O ICs. For most uses of the computer I would highly recommend at least one more VIA though!



For each additional I/O IC you may want to add, connect its CS pin to I/O SEL and its CS pin to the next address line down. This way you can have up to ten I/O ICs with no additional glue logic. You can probably see already that each IC will have more than one possible addresses range; but it is still possible to address each IC individually. In decades of doing it this way, I have never had any problem with it.



The I/O pin headers are standard dual-row 14-pin headers with .025" square posts on .100" centers. You will typically plug projects into these with IDCs on ribbon cables. The pin numbering is given for as you look down on the pins from the top. The organization is the same as Daryl's except that I made the power and ground connections at the ends such that accidentally reversing the IDC on the header will not swap power and ground and damage things. If you want it compatible with his SBCs, make pins 1 & 2 to be ground, and 13 & 14 to be +5V. Be sure to put enough room between the pin headers to plug the IDCs onto. Insufficient room will prevent connecting both at once.



The capacitors shown in the bottom-right corner should go from power to ground as close as possible to the power and ground pins of each IC, with the shortest possible leads and connections. The value is not critical, and 0.1µF is common and is fine; but depending on the construction, .01µF sometimes results in better bypassing at higher frequencies where groundbounce is more of a problem.

In the tips below, you will see that you can interface an unexpectedly high amount of things on a single VIA; but again, I strongly recommend at least one additional VIA for most uses of the computer. Note #9 above tells how to add more. Even if you don't want to add it to start, if there's any possibility at all that you'll want it in the future, at least leave room for it and the connectors.

Do get cozy with the VIA's registers, timers, and capabilities. Its name, V ersatile I nterface A dapter, is quite appropriate. If you have questions, you can email me at wilsonmines@dslextreme.com or post them to the 6502.org forum.









The 65(c)22 VIA's shift register (SR) has many modes of operation and uses. When speed doesn't need to be superfast (as when controlling relays, audio connections, the output voltage of a programmable power supply, etc.), you can use the SR to expand the number of input and/or output bits, virtually without limit. It becomes extra helpful when those bits need to be at a different logic voltage (like 12V), something other than the voltage the computer runs at. (More on that later.) First, consider a way to give lots of output bits at the computer's own logic voltage levels:





Although four 74HC595's (giving 32 output bits) are shown, you can expand the chain almost indefinitely, as long as the 595's are CMOS (ie, have high-impedance inputs, unlike 74LS or other non-CMOS). CA2 is shown as the output to latch the shifted values into the 595's, but you could use any output bit for that. I like to use CA2 to leave PA and PB free for other uses where you might want all 8 bits of a port, like for an 8-bit A/D or D/A converter. (I'll address that further down too.) Use 6522 SR mode 101 to clock the data out under control of T2, or mode 110 to clock it at half the Φ2 rate. Example code is given about 80% of the way down the front page of the 16-Bit Fixed-Point/Scaled-Integer Math section. There's more there than you need for output only, but it shouldn't be hard to figure out. I want to put code on this page here when I figure out how to put scrollable windows in it so the code doesn't make the page so long.







Similar to the above, the 65(c)22 VIA's shift register can be used to expand the number of input bits, again virtually without limit. For this, use SR mode 001 to clock the data in under control of T2, or mode 010 to clock it at half the Φ2 rate. These are bits 4, 3, and 2 of the VIA's auxiliary control register, or ACR.





(The '165 signal pin not shown is the inverted serial output, pin 7.)

Althought only three 74HC165's (giving 24 input bits) are shown, you can expand the chain almost indefinitely, as long as the 165's are CMOS (ie, have high-impedance inputs, unlike 74LS or other non-CMOS). CA2 is shown as the output to load the 165's when it's low and allow them to shift the loaded data bits when it's high. (Later, when we address using the same SR for both and input chain and an output chain, we will need a 74HC126, and the CA2-high output will then also enable the '126.) Again, I like to use CA2 to leave PA and PB free for other uses where you might want all 8 bits of a port.

"So why the 74HC74 D-type flip-flop at the end of the chain?" you might be asking. The answer is that the VIA looks at the data line at the first rising Φ2 clock edge after the serial clock goes up; but the '165 also goes to the next bit when the serial clock goes up, so the first bit the VIA sees is bit 6, not bit 7. Since the '165 chain gets ahead by one bit, we delay it by one bit with the flip-flop. There are other ways to get around this, like putting the output of the last '165 around to the input of the first one and rotating in software (which is not practical if the chain spans multiple circuit boards), or offsetting the bits in the hardware connections (which might require adding an extra '165 to every board if the chain spans multiple boards).

When we get to the software, note that after you latch the data into the 165's, you have to do a dummy read of the VIA's SR to start the shifting process. Then allow enough time to finish the process before reading again—at least 16 Φ2 clocks in mode 010, or longer in mode 001, depending on what you have T2 set to. Read once more for each additional '165. To restart the process and get a new set of readings, put CA2 low to load the new set of data into all the 165's, and then put it back high again to shift, then begin the shifting again, starting with the dummy read.







The circuit at approximately the middle of the front page of the 16-bit scaled-integer math section gives the idea for combining the two uses above. The circuit there is a last-resort method to interface to the huge math look-up tables in ROM if all your other I/O is taken. Interfacing to huge memory this way is slow, but still much faster than actually calculating the various trigonometric, logarithmic, and other functions, and it's accurate to all 16 bits.







Some situations require interfacing to control circuitry that works on 12V or other voltage substancially higher than the 5V (or 3.3V, or whatever your creation works at). I have, many times, used for example 4066, 4051, 4052, and 4053 analog switches to control 12V analog circuits (mostly audio), and these ICs needed the higher-voltage logic. (Maxim has these available with 5V logic control, but it's a very expensive way to go.) Consider the common and cheap 14-pin LM339. This diagram shows going from TTL-level logic to 12V logic levels:



If you take the top of R1 to a different voltage, you will need to change the resistor value. If this goes to a separate board that only has 12V (or other high voltage) available and not 5V, you can of course take R1 to that voltage, and calculate the adjusted value. 1.4V across 22K gives 63.6µA, so take your high voltage minus 1.4V and divide that result by 63.6µA to get your resistor value. It's not critical to get the reference voltage very exact in this case, so you can round to the nearest standard value within 20% or so and do just fine. The input bias current of each LM339 section is typically only 25nA, not enough to make a dent in the calculation, so we can leave it out for simplicity's sake, even if you run the same reference to many LM339 sections.

For going the other direction, ie, higher voltage to 5V logic, you can do something like this:



It does not have to be 12V of course. The LM339 can work up to 36V, and although its power supply voltage needs to be at least 2V, the input & output can go even much lower than that. If 12V (or similar) logic is driving the input, you will do just fine if you want to connect the reference to the 5V supply, and then you won't need to get the reference from resistors.

A comparator is similar to an op amp, but it's not an op amp. The comparator is made to have its output hit the rails and recover more quickly than an op amp of similar price can. The penalty is that the phase response is not kept under control to keep it stable in an analog output range; but that's not what it's for anyway. One of the reasons for this comparator however is the open-collector output that lets the high output voltage be independent of the comparator's supply voltage.

For best speed:

Bypass the 339's power to ground with a .1µF capacitor, like you would other logic ICs, with short connections.

Bypass the reference voltage input to ground with another .1µF capacitor.

Connect the 339's power pin to the higher voltage. (This improves the speed a little but does not affect the output voltage, as the latter depends on what you pull it up to, not on the 339's power-supply voltage.)

Do not divide the signal input with resistors, since along with the input capacitance and other stray capacitance, you would get an RxC that would slow it down. If you really must divide it down, see the forum topic at Mixed Voltage Systems: Interfacing 5V and 3.3V Devices. (It's not very long.)

Keep the pull-up resistor at around 6.8K for 12V, 4.7K for 5V, and 3.3K for 3.3V. There may be a temptation to increase the value to save power; but that will slow it down.

Minimize the capacitive load on the output.

Note that the L P 339 is the lower-power version, but it is also much slower than the L M 339.

For going between 5V and 3.3v or other low logic voltages, there are other ICs on the market that would be more suitable than the 339 (although I have used the 339 there too), like the 74LVCxx family. Additionally, after I wrote the above, someone alerted me to the 74LS06 (inverting) and 74LS07 (non-inverting) open-collector hex buffers which are much faster than the 339 and have a maximum output voltage of 30V but whose inputs do make for a greater load and are not available in CMOS.



The resistor value was chosen for a 15mA coil and making sure the transistor is really saturated even if its gain is at the low extreme of its allowable current-gain range and the circuit feeding it can't pull up to more than about 3V. CMOS will be able to pull it up higher but the overdrive is nowhere near enough to hurt anything.

If you need a lot of these, you might do better to use an IC like the ULN2803 which has 8 Darlington drivers in an 18-pin package with integral resistors and protection diodes. That would replace 24 discrete components, three for each circuit above, times eight. The only negative about it is that being Darlington, the output voltage won't come down to much under a volt. The configuration shown above however allows the output voltage to come nearly all the way to ground if the load is light enough and the bias current strong enough for the chosen transistor.







In the automated test equipment I designed, built, and programmed in approximately 1990, I drove approximately 75 relays by way of Allegro Microsystems UCN5821A (I think it was made by Sprague at the time) 8-bit, serial-in, parallel-out 16-pin shift registers which are rated for 50V, 350mA, made specifically for driving relays and other heavyish loads. It looks like Micrel sells it now under the part number MIC5821BN. These were mixed in with lots of 4094's in the same serial chain all controlled by the same three pins of the 6522 VIA.

There's also TI's TPIC6 line of logic ICs, with for example the TPIC6A595 shift register that is similar to the 74HC595 mentioned above in the section "Using the 6522's shift register for tons of output bits" but with open-drain outputs that can handle 50V when off and 350mA each when on, even though the IC's power supply and logic inputs are 5V, and it has extra ground pins to handle all that extra current (IOW, it cannot be used as a drop-in replacement for the 74HC595). Also available are the '259 8-bit addressable latch and the '273 octal D-type latch, although these last two are much more expensive, at over $4 each in singles.











You can use any I/O bits you want, but I chose the ones above for particular reasons.

Bit 0 makes a good clock bit, because as long as you know its value, you can pulse it with only two instructions, INC<port> DEC<port> to make a positive pulse, or swap them for a negative pulse, without affecting the other bit values. If you put the VIA in zero page, you could use the SMB and RMB instructions to put the clock on any other bit, or if you can keep the desired mask in the accumulator, you can use the TSB and TRB instructions, then it's still just a pair of instructions to pulse any bit in the port. Otherwise you would need to AND and OR the desired bit values. That's still perfectly doable, but it requires more instructions. New way: There is now another possibility for even faster twiddling of these bits, using illegal op codes of the 65c02. See Jeff Laughton's circuit tricks much further down the page!





to make a positive pulse, or swap them for a negative pulse, without affecting the other bit values. If you put the VIA in zero page, you could use the and instructions to put the clock on any other bit, or if you can keep the desired mask in the accumulator, you can use the and instructions, then it's still just a pair of instructions to pulse any bit in the port. Otherwise you would need to AND and OR the desired bit values. That's still perfectly doable, but it requires more instructions. There is now another possibility for even faster twiddling of these bits, using illegal op codes of the 65c02. See Jeff Laughton's circuit tricks much further down the page! Bit 7 (along with bit 6) are easily tested with the BIT instruction without affecting or needing any processor registers. BIT puts bit 7's value in the N flag and bit 6's value in the V flag, which you can then branch on with BMI , BPL , BVS , and BVC . If you have the VIA in zero page, you could use the BBS and BBR instructions and then it's only one instruction to test and branch on any bit you might want to put the data line on. Otherwise you would need to read the port into the accumulator and use AND to test the desired bit. That's still perfectly doable, but it requires more instructions. Again, see Jeff Laughton's circuit tricks, way down the page, for more possibilities.





instruction without affecting or needing any processor registers. puts bit 7's value in the N flag and bit 6's value in the V flag, which you can then branch on with , , , and . If you have the VIA in zero page, you could use the and instructions and then it's only one instruction to test and branch on any bit you might want to put the data line on. Otherwise you would need to read the port into the accumulator and use AND to test the desired bit. That's still perfectly doable, but it requires more instructions. Again, see Jeff Laughton's circuit tricks, way down the page, for more possibilities. The I²C spec does not tell how you have to apply power, but I used the method above because the VIA can be set up in software to toggle PB7 automatically by the T1 rollover, which allows you to produce a beep on a piezoelectric beeper with the software dictating the frequency but not having to babysit it in a loop; ie, the computer can be doing other things during the beep. The circuit above doubles up on PB7 (actually I use it for a third thing as well on my workbench computer), and beeping does not shut down the I²C power. You can shut it down when not beeping by setting PB7 high. Really the only reason to shut it down, even briefly, is to plug in and unplug I²C modules.

NOP

Next you might be wondering about the pull-up resistors. This is how I²C manages to send signals in both directions without bus contention. Devices can only pull it down, not up. I've done it such that the master does pull the clock line (but not the data line) both up and down, and that's normally not a problem; but the spec does allow multiple masters, and also a few devices can hold the clock low to tell the master not to send the next bit yet (which would require that the master check that the clock line has floated up before it pulls it back down). Handshaking is usually accomplished through the ACKnowledge bit though, and that's on the data line at the end of a byte.

Ok, so how do you have the VIA only pull down and not up? Simple—store a "0" in the applicable bit of the output register, then write to the data-direction register (DDRA or DDRB) for the particular port. When you want it to pull it down, you set the bit to be an output; or to let the pull-up resistor pull it up, you set it as an input, even if you don't particularly need to read it. (The interesting thing about that is that since a "1" in the data-direction register bit means "output" and a "0" means "input", pulling it down requires setting it to "1", and letting it float up means setting it to "0", instead of vice-versa.) You don't actually keep writing to the output register itself. This is one place where the 6522 is far more efficient codewise than the 6521 or 6520 which do not allow a light-footed method of quickly going from accessing the ports to accessing the data-direction registers and vice-versa.

The I²C devices are all connected in parallel. Each class of device has an address group assigned to a few bits in the address byte, and then the device might also have a few extra pins that you can connect to Vcc or ground to provide more bits in the address byte and allow several devices of the same class to be on the bus at the same time. This way you can have more than one EEPROM, more than one digital thermometer, more than one D/A converter, etc..

If you want to make module to plug in, I would suggest the I2C-6 connector standard we devised on the 6502.org forum. The page has diagrams and photos. Use of the interrupt pin is optional, but it would be good if you incorporated it on your computer so that for example if you make a module for time and date and it has an alarm capability which uses the interrupt, or a keypad controller that interrupts when a key is pressed, you're all set.

There is 65c02 assembly generic I²C code here. Various devices will have their own detailed operation which will be given in their data sheets; but there will be basic things that they all share. The link also has Forth and PIC code for 24xx I²C EEPROMs, and Forth code for the MAX520 I²C quad 8-bit D/A converter.







Wikipedia has quite a write-up on SPI. Unlike I²C, SPI can be full duplex; and because it is not limited by passive pull-ups like I²C is, SPI's maximum speeds are dozens of times as fast as I²C's. For this reason, the larger flash memories especially will be found in SPI and not I²C, as storing a 10-megapixel photograph for example to an I²C memory would take much too long. Bit-banging with a 6522 VIA as shown below will not operate SPI anywhere near its maximum speed, but it still enables us to help ourselves to hundreds (if not thousands) of great ICs on the market, made by dozens of manufacturers. (For maximum SPI speed available in a 65-family IC, see the 65SPI (not to be confused with 65SIB) I/O IC which is designed and sold by Daryl Rictor and provides direct and complete SPI support in a 65-family IC, without bit-banging.) Jeff Laughton's circuit tricks much further down this page also make possible much faster bit-banging, using illegal 65c02 op codes. For example, you can set or clear an output bit in a single clock cycle without affecting the other bits in the port!

SPI normally involves four wires for one SPI device (or slave), plus an extra one for each additional device. There's a clock line (often abbreviated SCLK for "serial clock"), a m aster- i n, s lave- o ut (MISO) data line, a m aster- o ut, s lave- i n (MOSI) data line, plus one negative-logic chip-select (CS) for each device or slave.

Each transaction is begun by putting the slave's CS line low and then sending the command, ie, telling the slave what you want it to do, whether to take the following data and do something with it, or output some data, etc.. Non-selected devices stand by and don't respond to anything on the clock, MISO, or MOSI lines. The SPI modes used by the different slaves on the bus do not need to match, as only one slave will be enabled at a time (unless you daisychain, something which will not be addressed here.)

As long as you don't exceed the device's speed limits (which you're not likely to do if you bit-bang with a 6522 VIA), there are no particular timing requirements like RS-232 has, so bit-banging it is very easy. More on that later. Here's a really basic SPI connection:





You can use any I/O bits you want, but I chose the ones above because again bit 0 of a port is the easiest and quickest to pulse in software (using INC and DEC ), and bit 6 or 7 of a port are easiest to test (using the BIT instruction).

The arrows show signal direction. Since normally no line is bi-directional, it makes it easy to convert logic voltage levels if necessary.

It wouldn't be a bad idea to put weak pull-up resistors on all the lines so that no line floats between logic levels before the VIA is set up. Non-WDC VIAs (like Rockwell, even the CMOS ones) do effectively have a pull-up resistor similar to 74LS, but WDC's are truly high-impedance CMOS in the input mode. However, at least an accidentally selected SPI slave won't do anything if the clock and MOSI lines don't toggle in a sequence that it understands as a valid command.

Depending on the SPI modes you need, you can sometimes share clock and data lines with I²C. You'll have to observe the following:

Never transition the data line when the clock is high except to produce start and stop conditions on I²C.

Keep the EN (of Microwire) or CS (of SPI) lines high (false) when addressing I²C devices.

To interface to more devices, you will need more CS lines, using more VIA output bits. This is shown below, along with the entire 65SIB (6502.org Serial Interface Bus) circuit on my workbench computer. 65SIB is basically SPI but expanded in several directions at once, making it suitable as an external bus too, and accommodating devices ranging from dumb to highly intelligent. (Don't be scared off by the complexity of the circuit. Half of it is just to feed the annunciator LEDs.)



Notes:



Solid arrows on connections (except to V R , the reference voltage) show signal direction. There's sample code at SPI.ASM. I have a ton of I/O-bit sharing on my VIAs, and the various DIP switch sections allow me to isolate the 65SIB from other circuits if it becomes necessary. You can use any I/O bits you want, but I chose the ones above because of which ones I had available on the workbench computer, and because again bit 0 of a port is the easiest and quickest to pulse in software (using INC and DEC ), and bit 6 or 7 of a port are easiest to test (using the BIT instruction). The current-limiting resistors on the IRQ and CONFIG lines a have lower value than the CLK one because these two LEDs will flash so briefly at at such a low duty cycle that they will need some help to be seen. The ±12V is not part of the SPI spec, but we put it in the 65SIB spec so that external equipment that does not consume much power could get power off the bus and not need its own power supplies. (Explanation is given in the 65SIB specification.) The configuration line ( CONFIG ) is not part of the SPI spec either. Intelligent 65SIB devices can use it, but it will have no effect on non-intelligent devices, so in that case you could use it for something else on them if desired. For example, an SPI LCD may use it for a register-select line, and a flash memory might use it for a HOLD line. I have ones here that do that.) The pull-up resistors as mentioned in the notes for the simpler diagram further up are included, to prevent ambiguous states before the VIA is initialized in software. PA6 and S7 allow both a software-controlled method and a manual method to disable 65SIB (and SPI) interrupts. I used an LM339 open-collector quad comparator mainly because of the IRQ output; but there are different ways you could handle the comparator functions here. They do not need to be super fast. The LM339 does not do well with inputs that are less than 1.5V below its Vcc, but I powered it with 12V as I have it available anyway for other things on the workbench computer. For other explanations on 65SIB, see the specification.

As of Oct 2016, we also now have the SPI-10 connector standard for small modules, similar to I2C-6 above. See the spec here. The first SPI-10 module available is a tiny flash memory one, shown here.







Starting at the simplest point, you can have a pushbutton switch from one of an I/O IC's input pins to ground, and a resistor of 4.7K or 10K to pull the pin up to Vcc when the button is not being pushed. Note that as long as you're not needing to push the button, you can use this pin for something else too. When I've designed products with PIC microcontrollers which are programmed with a serial connection with one line for clock and one for data, I've put pushbuttons on the same pins, as there will never be a need to push a button while the device is being programmed, and the programmer will never be connected when a button needs to be pushed.

Going to the next step, you can have several buttons connected to output bits, with a common connection to a pulled-up input bit, like this:

Note that the output bits can be connected to other things as well. Just don't press more than one key at a time if you keep the bits in output mode full time, or you'll have two or more outputs shorted together, unless you put a diode (preferably a Schottky one) above each key switch, with the cathode at the top. (There's another way to get around that, discussed in a minute.)

I'm partly out to get maximum function with minimum parts though, as construction takes time, and hardware is a bigger job to swap out or modify than software is.

Your software needs to bring one of the output lines down at a time, then read the input line and see if it's high or low. A high input bit means the key that is on the low output is not being pressed. Make the software cycle through them. More on software considerations in a minute.

The five-key keypad is what I have on my workbench computer, and the five VIA output bits feeding the keys also go to the LCD and my printer interface. I do the software development on a DOS PC and send code over the RS-232 line; but the keypad is convenient for various things once a piece of code is set to running. The keys can be used for anything you determine in your software, but the functions I usually give mine are:

Yes

Enter

Continue Up No

Cancel

Exit Down Help

Menu

Edit

I have this to the right of the LCD on my 6"x4.5" workbench computer, with 6mm tactile keyswitches soldered to DIP headers that are plugged into DIP sockets; but as I gave it commands from the PC on the desk opposite the workbench and turn around toward the workbench to see instant results, there were times that I wanted the keys within reach without having to get up each time; so I also made a remote keypad and added a pin header on the workbench computer to connect it with a long cord, like this:

(This is in my Tip of the Day column too.) The connections to the two keypads are in parallel, so it all goes to the same I/O bits, and a given key has the same effect regardless of which keypad it is pressed on. The pin header it plugs into has a few extra pins so I could also put a duplicate LCD on the remote module.

For more keys, even a full keyboard, you can do something like the following. (To save drawing time, I'll just use a circle to represent each switch connecting the vertical line and the horizontal line that go through that circle.)

As above, pressing more than one key at a time could cause contention (depending on which ones they are), unless you add the diodes or other means to prevent shorting outputs to each other. The software needs to lower one output line at a time, then see if any of the input lines are low, then go to the next output line, cycling through the set. It might be best to have it return a pair of bytes with one bit to represent each of the 16 keys.

Actually, there is a no-diode way to prevent the shorting-outputs problem. You can write 0's to the output bits (by writing to the VIA's ORA or ORB), and then make only one of the output bits an output at any given time (by writing to the VIA's data-direction registers DDRA or DDRB). When it's not pulling the line down, any given bit is an input. This has the effect of an open-drain (or open-collector) output. We pulled this trick above in the section on interfacing to I²C, which has sample code linked. The only pullups then are passive, ie, the resistors—and BTW, it might then be a good idea to put pull-up resistors on those as well, not just the inputs, so there won't be ambiguous logic levels at any pins when keys aren't being pressed.

16 keys is what I put on the last automated test equipment setup I designed, built, and programmed at my last place of work in about 1990:

An earlier set I did used a full PC keyboard, and it just took a lot of space on the workbench, and it, plus the big monitor, turned out to somehow make the test operator think she knew more than she did and it became a bit of a pain. The smaller keypad afforded ways to get to all the menu items and enter the needed info just fine. The blank key could be used for a shift key to give other ones more functions. These ½" square Grayhill type 87 keys are really nice because their pins fit into prototyping board and they have clear caps you can remove to put labels underneath. (As you can see, I didn't go to the effort to make the labels look really professional.)

You can get away with taking fewer bits on the 6522 VIA (or other I/O IC) if you use something like a 74HC138 3-to-8-line decoder. The above shows 16 keys on 8 I/O bits; but with the '138, you could put three 6522 outputs to the '138 and have the 8 outputs of the '138 feed 8 columns, then with only three inputs on the 6522, you could have 24 keys (3x8) with only 6 bits total on the 6522 instead of 8. You can take it further of course. A keypad is not a high-speed device by any means, and the serial I/O above, using 74HC165's and 74HC595's, can easily put an 8x8 (or bigger) key matrix on the 6522's shift-register port, along with lots of other things, and not take any additional pins on the 6522.

The 6502-based Oric computer keyboard used a 4051 instead of a 74HC138. Since it's a 1-to-8 analog switch (with three control bits), the non-selected outputs were high-impedance, so no other provision was necessary for allowing multiple keys to be pressed at once. 4000-series logic is very slow and not suitable for a lot of computer use, but you won't be making hundreds of thousands of keystrokes per second anyway. Here's the Oric keyboard schematic:

You must debounce! "What does 'debouncing' mean?", you might ask. Switches are not perfect, and if you look at the output as they make or break contact, it will not be a single, solid, fast edge, but a bunch of up-and-down for a few thousandths of a second until the switch quits bouncing and settles down. This was illustrated in the "Reset requirements" page, and it shows deboucing there in hardware because you cannot do it in software while you're resetting the processor. You have probably had the experience of a bad calculator or other keypad where it acted like you pressed a key many times even though you pressed it only once. The calculator probably had some debouncing, but not good enough for a less-than-new keypad. It's very irritating!

It is much easier to do debouncing in software than hardware. What I have done for debouncing on many products is to require that the switch be closed for at least 30ms at a time before it is recognized. It is sampled many times in that amount of time, and if any sample shows it released again, the count starts over. The same goes for releasing it.

I frequently also add auto-repeat, and, on the workbench computer, I have a variable to set the delay before repeat begins, and another variable for repeat rate. Auto-repeat is useful even on a small keypad, because you might for example want to increment or decrement something with the up- and down-arrows while watching the results, and having sometimes even hundreds of button pushes (ie, no auto-repeat) would be tiresome, slow, and also reduce the life of the switches.

Newcomers tend to do the timing in software delay loops, but I would encourage use of a real-time clock (RTC) instead, whether it's an RTC chip with adequate time resolution, or a software RTC that is interrupt-driven by timer 1 (T1) of the 6522 VIA, or other method. My interrupts primer tells how to set up the RTC using the 6522, and has complete code, starting a little over half way down the page. (Enjoy my out-of-date cartoons!) Having an RTC allows the computer to be doing something useful between key repeats, and the repeat rate stays consistent regardless of the varying size of the job between repeats.

My article on simple methods to do multitasking without a multitasking OS has example code for doing keypad debouncing and handling a shift key plus auto repeat of the cursor keys, near the end, timing things while allowing the computer to be done other things at the same time.

Two-key rollover can be done in sofware too. Just stop scanning the keypad or keyboard until the currently pressed key is released. Until then, you only watch that one key.

For other keypad- & keyboard-interfacing possibilities, I should mention that there are many I²C keypad controllers on the market that have built-in debouncing and sometimes also drivers for 7-segment LEDs and other I/O built in, and you can connect them (along with other things) to your I²C interface described above. Note also that the common PS/2 keyboard interface is very similar to I²C. Daryl Rictor shows how to interface it to a 6522 here, with code, and shows how to do it with an AVR microcontroller here, again with code, and interface it to his 6502 SBCs. (Please note that the power pins on his are slightly different from what I put at the top of this page.)

The darnedest thing I've ever seen in keypad interfacing was to connect the key switches to an arrary of resistors pulled up to Vcc and produce a single voltage to go into an A/D converter. The application was a microcontroller that was short on I/O pins but had a spare A/D input!









NOTES:

The first thing you might be thinking is "Where are D0 through D3?" They're not connected, because I'm using it in 4-bit-interface mode to save VIA pins. More below. The 10K potentiometer adjusts the viewing angle. Note that many supertwist LCDs that are more attractive need a negative voltage for this. I find people have quite a bit of resistance to voltages other than 5V, especially negative voltages, but there are various things that having a ±10 or ±12 volts (non-critical) will be useful for, so it would be good to have them available. There is not typically much current required of these. Farther down this page at "Non-typical power-supply circuits," I give easy ways to get these voltages without having to change your power supply. Here we tie the R/ W to ground. The main reason to read the LCD is to see if it's still busy with an operation before giving it something more to do; but if you don't need to go absolutely as fast as possible, you can just wait the maximum amount of time the data sheet says the various operations, and you'll be assured that you're in the clear. Otherwise we only need to write to it. It might be a bigger deal for a large display, but here we can save a pin. NOTE: It may be tempting to put a resistor between this pin and ground in case you later want to drive it. A jumper selection would be better, because it's like a LSTTL input which takes a lot to pull down. I wasted some time and discovered this the hard way. Sample code to operate this is at LCDcode.asm. Note again the doubling up (or in this case, tripling up) on VIA pins' usage. In another product I developed, the lines were also shared with a D/A converter. Since a small display like 1x16 (one line of 16 characters) does not need the data fed super fast, you might even choose to feed it through 74HC595's on a serial chain as described above. It would partly pay for itself in that there would be little or no need to go with the 4-bit interface to save pins (as opposed to the 8-bit interface). The 8-bit would make the software a little simpler, not having to split bytes and feed the nybbles separately. Pin-out may differ slightly between manufacturers, especially by swapping ground and Vcc, so do not use the pinout of mine without checking your data sheet.

I have only a mild interest in graphics. I made a very simple circuit and software to do raster graphics on an analog oscilloscope. See http://forum.6502.org/viewtopic.php?p=15348#p15348.

I also have a small 128x64-dot graphics LCD that is interfaced by SPI, so I put it on the 65SIB. I have working sample code for it here. Here's a few seconds of demo video done for experiment purposes, using a random number generator to produce sets of 25 random segments then displaying the screen memory:









The timing diagram from the manual for one of my several parallel printers shows:





The lower two lines, data and strobe, are computer outputs to the printer, while the upper two, busy and acknowledge, are computer inputs from the printer. You do need to pay attention to the busy line, but I've never paid attention to the acknowledge line. Technically, the busy line is not an acknowledgement that the byte you just sent it was actually received; but this is the way I have done it for 23 years, with a half-dozen different printers from four different manufacturers, and it has always worked flawlessly. the reason I started doing it this way was probably to save another VIA I/O bit. That additional line would have to be on one of the CA or CB lines for the edge-sensitivity since the ACK\ pulse is narrow enough that it might get missed if I only sampled it.

Since there's no hurry with printers and the occasional poll will stay ahead of the printing and keep data in the buffer anyway, I've never put them on interrupts. I might if I were spooling a super long printout, but I've never done more than a couple of pages at a time with the workbench computer and the automated test equipment at my last place of work; and in those cases, an interrupt-driven process could continue while the non-interrupt-driven print routine babysat the printer.

Your software needs to check the printer's status before every single byte. Before stopping with an error condition saying the printer is not ready, your software should allow a couple of seconds for the printer to say everything is ok. Especially in non-graphics mode, you will be able to send data much faster than the printer can print it, meaning you may fill up its buffer and have to wait for the printer to be ready to take more data.

Parallel printers normally have a 36-contact female Centronics connector, and for reasons unknown to me, computers with a parallel printer port usually have a female DB-25 connector on them, so the standard printer cables (before USB) are generally terminated in the male of each of these two connector connector types. I put a 16-pin header on my workbench computer since it takes so much less board space, then made up an adapter cable to go from IDC (to plug into the pin header) to DB-25 (to mate with the standard printer cable). The connections go this way:

IDC

pin DB-25

pin Centronics

contact function (Note 1) into or out

of printer? Notes 1 1 1 strobe IN 2 2 2 D0 IN 3 3 3 D1 IN 4 4 4 D2 IN 5 5 5 D3 IN 6 6 6 D4 IN 7 7 7 D5 IN 8 8 8 D6 IN 9 9 9 D7 IN 10 10 10 ACK OUT 2 11 11 11 Busy OUT 12 12 12 Paper out OUT 2 13 13 13 -- -- 14 14 auto feed IN 3 15 15 32 Error OUT 16 16 31 init IN -- 17 36 select IN 3 14 18 33 twisted pair ret Gnd 14 19 19 twisted pair ret Gnd 14 20 21 twisted pair ret Gnd 14 21 23 twisted pair ret Gnd 14 22 25 twisted pair ret Gnd 14 23 27 twisted pair ret Gnd 14 24 29 twisted pair ret Gnd 14 25 30 twisted pair ret Gnd

Note 1. Note the six negative-logic signals. (Their overbars are not very visible.)

Note 2. These pins on my workbench computer's IDC are not connected.

Note 3. I did not connect these, but they have DIP switches in the printer so external connection is not always necessary.

Columns 2 & 3 represent the standard parallel printer cable. Columns 1 & 2 represent a ribbon cable I made up, but you might want to just put a DB-25 on your computer board for the purpose.

From the other things on this page, you're probably getting the idea by now of how to connect this to a 6522 VIA or even through shift registers. (After all, a line printer is another thing that's not exactly fast. Putting it on the VIA's SR is not going to be a burden speedwise!) To get as many things on the first VIA on the workbench computer, I even have to split the eight bits of printer data between five bits of port A (shared with the LCD and keypad, as shown in the schematic in the display section above) and three bits of port B. (I know it sounds like a mess, but once the software for it is written, it just does its job and you forget all about it.)

I might post code later. All I have now is Forth code from various projects—not a single one in 6502 assembly.







STA VIA_PB



Notes:

As shown, an input of 0 gives 0V output, and $FF gives 5V*255/256 output (ie, 4.98V if your supply really is 5V even). It is quite accurate throughout the range. Each step is about 19.5mV. The ±10V shown is not critical at all. I keep it in the neighborhood of ±9V, but the actual value does not affect the accuracy. The op amp also uses it to perform well all the way down to ground on the output, and to output $FF which is 5V*255/256. The DAC needs a negative voltage anyway, and using the higher voltages makes the op amp work better than 5V rail-to-rail ones do. (Remember it was mentioned above for some supertwist LCDs' backplane bias, and I said it's good to have the ± voltages for several things. This is one.) I tell how to derive these voltages from 5V farther down, under "Non-typical power-supply circuits." Purists would probably want you to use 1% resistors, but I've found the 5% carbon-film ones are usually not more than 1% off anyway. The important thing is not so much their absolute value, but their matching. As shown here, the 5V power supply is the reference; and if your +5V is within 1% or better, half lsb which would be 10mV, congratulations. If it's not close enough for your liking, you can adjust for power-supply inaccuracy by trimming R5. C1 and C2 filter out noise that's on the power-supply line so the reference for the DAC is quiet. The reference voltage input on the right end of R3 usually is left unconnected, but you can use the DAC as a multiplier for that voltage, even tens of thousands of times per second, so you can put not only DC on it but also an AC signal if you want. For applications where you don't need much speed (like for setting a power-supply voltage), nearly any op amp that can work with the voltage range will do. I started with a common LM358 dual, but later changed to an LT1124 (also a dual) which is about ten times as good in every way (but very expesive, which was ok since I only needed one). The LM358 has a little crossover distortion when trying to drive the 2.2K R5 at higher speeds unless you bias the output pretty heavily with a pull-down resistor. I use the other half of the dual op amp in my A/D converter circuit (which I will present further down). C4 is not critical either. With the 2.2K R5, 330pF puts the -3dB frequency at about 220kHz (actually a little lower since the op amp is not perfect). Since there's a little capacitance in the circuit at the inverting input, just make it standard practice to put in at least 22pF or so to get rid of the pole that could otherwise make the op amp oscillate. The 220-ohm resistor at the output serves a similar purpose. Since the op amp's output does have some resistance, capacitive loading produces another pole, and the feedback circuit gets its signal source phase-shifted accordingly. If the capacitive loading is heavy enough, you can lose enough phase margin for the op amp to oscillate. The 220-ohm load is enough to prevent that under all load conditions, and, at the operating frequencies of interest, probably won't have any effect on the circuit you're driving. Actually, depending on the op amp selected, you might be able to go as low as 100 ohms or even less. Note the single-point ground connection. If the circuit you're driving with the DAC is not practically shoulder-to-shoulder close to this circuit, it would be good to put ferrite beads over the pair of wires, voltage output and analog ground (labeled AGND in the diagram), ie, two wires through the same bead, to cut common-mode signals and force the analog ground wire to carry the equal and opposite return current. You can see a bunch of ferrite beads in the first picture on the primer page here for construction for AC performance. The ferrite bead I use for so many things is the Fair-Rite 2643000801 which is 0.3" long and 0.3" diameter and has a minimum impedance of 50 ohms at 25MHz and approximately 90 ohms at 100MHz. Remember that even a 2MHz square wave has substancial harmonic content around 25MHz. This bead has a few ohms' impedance even at 1MHz, and of course does not affect DC. If you put two wires through it, differential-mode siganls, ie, where equal but opposite currents flow in the two wires, will be unaffected. In fact, the ferrite bead will try to make the two AC currents equal and opposite. The bead is $.08 each in singles at Mouser. Keeping digital noise out of data converters is a science, but fortunately not too difficult for 8-bit. The various A/D and D/A manufacturers have lots of ap. notes on it, and each data sheet will have a little on it.

If you need more sharing of VIA ports, like to be able to use the same VIA bits to talk to something else without changing the DAC's output, you could add an 8-bit latch such as the 74HC373 or '374 and use just one extra bit from the VIA to trigger the latch after putting the desired bit pattern out the 8-bit port. The point of this particular DAC however is speed; so if you want to go with serial (which is much slower) to further extend the number of things the existing VIA pins can interface to, you might as well go with the I²C MAX520 or 521 discussed below which have 4 and 8 DACs and need virtually no support parts.

This DAC0808 BTW is the same converter I used on my analog oscilloscope raster graphics circuit to take the counters' outputs and drive the X and Y inputs of the oscilloscope.

In the automated test equipment I designed, built, and programmed at my last job in about 1990, I used DAC1220's which are nearly the same but in 12-bit, in an 18-pin DIP. I used these to set power supply and other voltages and to control signal amplitudes. The DAC1220 is apparently no longer made, but the AD7541A is nearly the same thing, somewhat improved.







The MAX520 quad I²C 8-bit DAC in 16-pin DIP I mentioned earlier needs virtually no external components, so there's really no circuit to draw. There's simple Forth code to drive it about 2/3 of the way down the GENRLI2C.ASM file which starts with general I²C-driver 6502 assembly code. I might recommend the MAX521 in 20-pin DIP instead though, since it has 8 DACs instead of 4 and it has output buffer amps so it can drive a decent load without the output voltage drooping seriously. The 520's outputs should only drive high-impedance loads.







For a super-simple 9-level D/A converter, remember you can use the VIA's SR data output as mentioned earlier:









shown with a little more explanation at http://wilsonminesco.com/6502interrupts/index.html#3.3 in my interrupts primer (where this figure came from). Driver code is too simple for a separate file, so here it is:

INIT_SR_DAC: LDA #00010000B ; Set SR to shift out free-running at STA VIA_ACR ; T2 rate. (T2CH does not get used.) STZ VIA_T2CL ; Zero the T2 counter's low-byte latch for fastest RTS ; possible shift rate, to make it the easiest to filter. ;------------------ _3BIT_TBL: ; "DFB" in C32 is "DeFine Byte." DFB 00000000B, 00010000B, 00100010B ; The point here is to distribute the bits DFB 01010010B, 10101010B, 10101101B ; such that the toggling frequency is kept DFB 11011101B, 11101111B, 11111111B ; as high as possible for easy filtering. WR_SR_DAC: ; Start with number in the range of 0-9 (inclusive) in A. TAX ; Skip the TAX if you already have the number in X. LDA _3BIT_TBL,X ; Get the distributed-bits pattern from the table, STA VIA_SR ; and write it to our make-shift D/A converter. RTS ;------------------

c







Notes:

As shown, an input of 0V gives an outut of 0, and an input of 5V*255/256 gives $FFH. Each step is about 19.5mV. The ±10V shown is just for the op amp power supply and is not critical at all. I keep it in the neighborhood of ±9V. If you want to try to use a rail-to-rail 5V op amp, be my guest. I always have several things that use the higher voltages, so I think it's just as well to have them available than to try to avoid everything outside of +5V. I tell how to derive these voltages from 5V farther down, under "Non-typical power-supply circuits." The op amp bias resistors shown are for when you want capacitive coupling. The trimmer pot is for adjusting the zero-input-signal output value. As shown, you can get approximately $0C above and below the $7F value. You can typically go a little more one side than the other depending on your op amp choice, because of its input bias current. For applications where you don't need much speed (like for measuring temperature), nearly any op amp that can work with the voltage range will do. I started with a common LM358 dual, but later changed to an LT1124 (also a dual) which is about ten times as good in every way (but very expesive, which was ok since I only needed one). The latter was more for the D/A (presented above) than this A/D. The ferrite bead shown on pin 12, along with the .1µF capacitor there, keep digital noise on the power supply line from affecting the readings. If you use a separate voltage reference, this may not be an issue. The ferrite bead I use for so many things is the Fair-Rite 2643000801 which is 0.3" long and 0.3" diameter and has a minimum impedance of 50 ohms at 25MHz and approximately 90 ohms at 100MHz. Remember that even a 2MHz square wave has substancial harmonic content around 25MHz. The impedance at even 1MHz will, along with the .1µF capacitor, give a lot of attenuation. There might be a temptation to use a resistor instead, but that would affect the reading a lot, since the ADC may have as little a 1K between Vref+ and Vref-. In that case, even a 4.7-ohm resistor would change it by 1 lsb. The ferrite might have more than that at 2MHz but with no DC resistance. It costs $.08 each in singles at Mouser. The reference voltage output on the right is usually left unconnected, but may occasionally have a use, as in ratiometric measurements. The .01µF capacitor and 330-ohm resistor at the ADC's input have a -3dB point of 48kHz, so it's essentially flat throught the entire audio range. The 330-ohm resistor also helps preserve the phase margin on the op amp to keep it stable when there's a capacitive load which would otherwise produce a pole. The Schottky diodes just help protect the ADC's input from excessive current from voltages outside the 0-5V range. I used 1N5817's because I had a lot from making switching power supplies. Consider pins 10 & 11 to be the center of the star of the grounding system for the ADC. For best AC performance, capacitor leads should be as short as you can make them, since lead length adds inductance. If the circuit you're connecting to the ADC is not practically shoulder-to-shoulder close to this circuit, it would be good to put ferrite beads over the pair of wires, voltage output and analog ground (labeled AGND in the diagram), ie, two wires through the same bead, to cut common-mode signals and force the analog ground wire to carry an equal and opposite return current. You can see a bunch of ferrite beads in the first picture on the primer page here for construction for AC performance. Keeping digital noise out of data converters is a science, but fortunately not too difficult for 8-bit. The various A/D and D/A manufacturers have lots of ap. notes on it, and each data sheet will have a little on it.



The code to set it up and to read it is:

INIT_AtoD: LDA #00001110B TSB VIA_PCR ; Set CA2 high. I put the CS\ of the A/D on CA2. STZ VIA_DDRB ; Make all bits of Port B to be inputs. ; First reading after power-up is inaccurate, so get it out of the way, by continuing: READ_AtoD: LDA #00000010B ; No entry requirements for READ_AtoD. At end, A/D result is in Y. TRB VIA_PCR ; Set the CS\ of the A/D low. LDY VIA_PB ; Read the A/D result. At 5MHz phase-2 clock freq, you'll need one NOP between TRB & LDY. TSB VIA_PCR ; Set the CS\ of the A/D back high. RTS ;-----------------------

What I have not shown above is the sockets for plug-in modules for anti-alias filters and other signal conditioning. (Depending on what you're doing, you may not initially have any need for that.) I have various places to connect to the outside world. I put 3.5mm plugs on the front panel which I can plug audio straight in, other projects have been plugged into the module sockets, and some went through the board-edge connector at the back. The monitor speaker amplifier can be switched between A/D input and D/A output, both post-anti-alias filters.

I originally had an ADC0820 in the circuit which is nearly pin-compatible but went to the MAX153 which is faster. (I needed more NOP s with the ADC0820.) It required very minimal change to the circuit.

For a home project I have not done yet, I piggy-backed a second MAX153 to get two in the space of one:





This only takes one extra VIA pin to interface it, for the second CS. The data bits' connections are common. The point of this particular ADC is speed and getting exact timing on the readings; so if you want to go with serial (which is much slower) to further extend the number of things the existing VIA pins can interface to, you might as well go with one of the countless SPI or Microwire ADCs on the market rather than try to put parallel converts like this on shift registers.











With something like an I²C data converter, the exact time of the sample may be difficult to nail down accurately; but we would probably not be using that interface for a signal that changes quickly, let alone for audio. SPI is considerably faster; but the parallel converters here would offer much better timing for audio if we interface them to a 6502 with timing based on interrupts.

If the sytem clock (ie, Φ2) jitter and aperture jitter are negligible such that variations in interrupt latency make up basically the whole picture, the total RMS jitter for record-playback is the square root of the sum of the RMS record jitter squared and the playback jitter squared:





6502 instructions are mostly 2-6 cycles (7 is rare) and the average is 4. (Caveat: See the forum topic "A taken branch delays interrupt handling by one instruction.") Since all instructions take at least two cycles, an interrupt could hit during those last two on any of them; but as the cycle count increases, there are fewer and fewer instructions where an interrupt could more and more cycles before the end. With an equal distribution of 2-, 3-, 4-, 5-, and 6-cycle instructions (which is perhaps not the safest assumption), the median time from interrupt to the end of the instruction execution is 2 cycles, and the RMS jitter is 1.8 cycles for record and 1.8 again for playback, meaning a total (if you need both) of a little over 2.5 cycles. At 5MHz that's 500ns. The S/N ratio is:

where f is the analog input frequency, and t j is the RMS jitter time, or 500ns in this case. So at 4kHz input frequency, the noise resulting from jitter will be about 38dB below the amplitude of the signal, which means you're good to a maximum of about 6 bits (ratio of 80), even if you used a 16-bit converter. This applies no matter how high you get the sampling rate, even if over 100,000 samples per second, because jitter is separate from sampling rate. If you wanted to get that S/N ratio at 16kHz analog input frequency, with the sampling being interrupt-driven on a 6502, you'd need a 20MHz Φ2 (with no wait states), which is about as fast as you can reliably get with current-production, off-the-shelf 6502's.

If you don't plan to play it back, like if you only record in order to do a mathematical analysis on it (as I've done many times), you're only dealing with the recording jitter; so the 4kHz input frequency for a 5MHz Φ2 and the jitter-induced noise being 38dB down from the signal becomes 5.66kHz. Since I was doing spectrum analysis on aircraft and vehicle noise coming through headset microphones whose frequency response took a nosedive at 4kHz, it worked out fine. (I also used a 5th-order 5.6kHz anti-alias filter.)

That's really not bad for an 8-bit converter, considering that audio amplitudes tend to be very inconsistent and you probably won't be able to keep the signal in the top 1½ bits of the converter's resolution anyway. As the top end of your input signal's frequency spectrum drops, so will the problems resulting from jitter. The maximum S/N ratio you can get with a perfect 8-bit converter and no jitter is 50dB (from 8bits * 6.02dB + 1.76dB). (That's while there's signal. With no signal, the converter's output remains constant, with no noise at all if the reference voltage is quiet; so it's not like cassettes which gave tape hiss between songs.)

Without stopping the 65c02 using the WAI t instruction for the immediate interrupt response it affords (at the expense of doing something useful while it's waiting), you can still reduce the jitter in an interrupt-driven sampling time driven by a timer like the VIA's T1, by putting code in the ISR to read T1 and see how many cycles ago it timed out and adjusting the delay before doing the next data conversion. It makes for more delay in the ISR, but you can make that delay consistent, getting rid of the jitter. I will post code later.

There's a tutorial on jitter and ENB (effective number of bits) here, and an excellent lecture and demonstration of what is and is not important in audio, and down-to-earth proofs of the "golden ears" baloney, at http://www.youtube.com/watch?v=BYTlN6wjcvQ. Yes, it's on YouTube which compresses the audio and loses information, but he gives the URL where you can download the raw wave files if you want to, otherwise see what he does with various experiments right there.

If you are sampling AC signals, it would be expected that you have at least a basic understanding of the Nyquist frequency and aliasing. I might comment however that there are times that it is appropriate to undersample if you are dealing with a limited bandwidth of for example 200-220kHz. You don't necessarily have to sample much above 40kSPS; but the jitter still needs to be suitable for a 220kHz signal, not 20kHz. Just timing the samples off of timer interrupts on a 6502 won't get you there. But outside of that, (read on...)

How many samples do I need? and Do I need smoothing to produce a clean waveform? The answer might surprise you. Let's say you try to synthesize a sine wave with only 32 samples per cycle, or 8 samples per quarter cycle (90°). With no filtering, the first major harmonic distortion products will be the 31st and 33rd harmonics, down about 30dB from the fundamental. IOW, for a fundamental frequency above 600Hz, these will be out of the hearing range. The hottest harmonic distortion product below that is the 5th harmonic at about 54dB down from the fundamental. For a 16-sample sine wave, ie, only 4 samples per 90° (does that seem atrocious?), the first major harmonic distortion products are the 15th and 17th harmonics, down about 24db below the fundamental. The hottest harmonic distortion product below that is the 7th harmonic, at about 55dB down from the fundamental. If you go up to 64 samples per cycle, which is 16 samples per 90°, the first major harmonic distortion products are the 63rd and 65th harmonics at about 36dB below the fundamental. If you want to filter it, it will be easy, not requiring a many-order brick-wall filter, as long as you're not nearing the Nyquist frequency.

How many bits do I really need? As mentioned above, an 8-bit converter can theoretically reach a SNR of 50dB when signal is present, and have an output as quiet as that of a 24-bit converter if there's no signal, since the number won't be changing. There were some fine-sounding music cassette tapes before CDs took over, and their SNR was virtually no better than what an 8-bit converter can give you; yet the cassettes' frequency response and distortion at high record levels were quite inferior to what you can get with a good 8-bit converter and adequate sampling rate! I know it's unlikely anyone will ever use 8-bit recording for music for enjoyment, but this might put things in perspective. For things like machine control and measurements in test equipment, you will have to determine how accurate you need them, and whether external scaling and offsets will let you get by with fewer bits than you might think. There is some discussion of this in the first quarter of the front page about large look-up tables for hyperfast, accurate, 16-bit scaled-integer math. I'm definitely not against using converters of 12, 14, 16, or more bits when they are right for the application (in fact, I'm about to start shopping for a multi-channel SPI A/D of at least 12 bits), but I always like to point out what might be a pleasant unexpected discovery of what can be done with less.







Ultra-fast output port using 65C02 illegal instructions:

This is a 5-bit parallel output port for 65c02 that uses the illegal op codes (65C02 NOP s which are only informally but not thoroughtly documented by the manufacturers) in the _3 and _B columns to get single-cycle output, twice as fast as the otherwise 2-clock minimum for instructions, and you don't need extra instructions to first load a register. However, all five bits will be affected at once per the illegal op code used, unlike the 4- and 8-bit I/Os further down.



See the description on the forum for more info.





Get 4 bits of 65c02 single-cycle output, plus 4 bits of input requiring a single cycle to conditionally set the oVerflow flag to branch on afterward. (For the input, do a CLV first)



This one (and the next one, below) lets you address one bit at a time, independently of the others, unlike the 5-bit one above.



Get 8 bits of 65c02 single-cycle output (see the note on the usability of bit 6), plus 8 bits of input requiring a single cycle to conditionally set the oVerflow flag to branch on afterward. (For the input, do a CLV first.)



This one also lets you address one bit at a time, independently of the others. Note again that if you have a 65c02 made by WDC (as opposed to Rockwell, GTE, CMD, Synertek, etc.), the added STP (SToP) and WAI (WAIt) op codes ($DB and $CB, respectively) make output bit 6 usable only to indicate that there was a STP instruction executed. It will normally come out of reset as low ; then if a STP is executed, it goes high and stays high until there's a reset. Since STP stops the processor, a subsequent WAI will not happen before there's a reset. Neither IRQ nor NMI will wake it up from a STP . If you would prefer to give up a input bit 6 rather than than output bit 6, you can use this version instead.





Re-mapping Op-codes (eg: to create virtual instructions)

This causes illegal op codes (or any opcodes you choose!) to trigger a BReaK or other opcode, leading to clever software (supplied by you) to simulate new instructions.

See the description on the forum for more info.









Notes:

The unmarked capacitors are .1µF. When I breadboarded this to check it, I used decades-old ceramic disc capacitors.

The diodes can be 1N4148, or, to get more output voltage, Schottky diodes. I tried this circuit both with 1N4148 and 1N5817. I have a lot of 1N5817's here (which I use in switching power supplies), but if you need to shop for Schottky diodes, you might as well get something smaller for this application. Each Schottky diode will drop at least 0.3V less than plain silicon diodes, and the circuit above gave me almost three more volts output with the Schottky diodes (17.35V versus 14.68V connected as shown, with Point A fed by the VIA's PB7).

Point A is fed by (typically) a 5V square wave, possibly generated by a 65c22. Note however that it does not have to have a constant frequency or duty cycle, so even the unused SYNC output of a 65c02 would work. For more power, see the circuit below, with a dedicated oscillator feeding paralleled inverters for more drive strength.

Point B typically goes to ground (if you want the output to come all the way to ground when you stop the signal), or you can connect it to something higher (like 5V) if you want the output higher than it would otherwise be. (Connecting point B to 5V will add less than 5V to the output for a given load resistance though, since the higher voltage will spell higher output current, so there will be greater voltage drops in the circuit.

Point C may go to ground (as shown), or, for higher output voltage with a given number of capacitors and diodes, it can instead be connected to a square wave source that's out of phase with the one feeding point A.

The input frequency is not critical. 100kHz works well for many applications, and you can stray from that with half an order of magnitude with negligible effect. Higher frequencies allow smaller capacitors, but I already used 0.1µF's, so you won't get much smaller physically; and lower frequencies tend to make it more efficient. But again, you will see little or no difference from 30kHz to 300kHz, and very little difference going up to 1MHz or more.

For experimenting, I fed this one at point A with PB7 of a Rockwell VIA, run by T1 toggling it in free-run mode, and 100kHz worked best (although that was on a solderless breadboard, with wires feeding it from the workbench computer). To set it up, do this: LDA VIA_DDRB ORA #$80 STA VIA_DDRB ; Set the high bit in DDRB, to make PB7 an output. LDA VIA_ACR ; Set the two high bits in the ACR to get the ORA #$C0 ; square-wave output on PB7. (Don't enable the STA VIA_ACR ; T1 interrupt in the IER though.) LDA #$17 ; Set the T1 timeout period. $17 is for 100kHz if STA VIA_T1CL ; the Φ2 rate is 5MHz. To get it going, write STZ VIA_T1CH ; first to the ACR (above) and then to the counters. ; You can write directly to the latches later if desired. (The LDA / ORA pairs above could be replaced with single LDA# 's if you don't already have other bits set up in DDRB and the ACR that you want to preserve. The PB7 frequency is: f PB7 =f Φ2 /2(n+2), where n is the T1 latch value. Another option to feed it a continuous signal from the VIA is to use CB1 which is the clock output of the shift register, using mode 100 to shift out free-running at the T2 rate like above under "Digital-to-analog converters," after the second diagram, where it says, "For a super-simple 9-level D/A converter." (For that matter, you could also use the data line, CB2.) Set it up like this: LDA VIA_ACR AND #11110011B ; Clear bits 2 & 3, and ORA #00010000B ; set bit 4, to put the STA VIA_ACR ; SR into mode 100. LDA #$17 ; Set the T1 timeout period. $17 is for 100kHz if STA VIA_T2CL ; the Φ2 rate is 5MHz. There is no need to write ; to T2CH, as it has no effect on the SR clock rate. LDA #10101010B ; These last two lines are only for if you want STA VIA_SR ; to use CB2, whether you also use CB1 or not. The CB1 frequency is: f CB1 =f Φ2 /2(n+2), where n is the T2 latch value.

(The / pairs above could be replaced with single 's if you don't already have other bits set up in DDRB and the ACR that you want to preserve. The PB7 frequency is: f =f /2(n+2), where n is the T1 latch value. The CB1 frequency is: f =f /2(n+2), where n is the T2 latch value. The 100K load resistor is really just to bleed off the output resistor when you stop the signal input. FET bias, if that's what you want it for, takes negligible current—not even 1µA (which also means power efficiency is a non-issue). With no load at all, the voltage will slowly rise much higher than expected, because of the peaks in the ringing.

The number of stages can be varied depending on how much output voltage you need. You can add sections ad infinitum for higher voltages; but make sure the parts' voltage ratings are adequate, and remember that the available output current will drop as the number of stages increases.

Reversing the direction of the diodes will make the output voltage negative instead of positive. Don't forget to turn the output capacitor around also, if it's polarized.





I have used charge pumps for up to 8 or 10 watts. Sometimes it's not very efficient, but it may be worth it anyway. Here's a circuit I breadboarded recently to get -9V from +5V. This circuit puts out over -19V with a very light load, but I put the output through an LM337T adjustable negative linear regulator set to put out -9V at up to -200mA from this.

Notes:

There are 12 parallelled sections of 74AC14 for the top driver, and 11 for the bottom. (The 12th one is used for the 160kHz oscillator.) I used four 74AC14's, stacked, so all the inverters combined only took one 14-pin DIP socket space. This shows how: The reason for the '14 instead of the '04 is that the oscillator needs to be Schmitt-trigger. The reason for the 74AC (instead of 74HC) is that AC has a much stronger output. Even 74ABT is not significantly stronger for this one, and 74ABT cannot pull up to 5V like 74AC can anyway, which is a big disadvantage.

The oscillator frequency will depend partly on the size of the hysteresis of the particular IC; but it will not vary enough to cause any significant effect on performance. With different ICs, the 27K and 220pF got me anywhere from 140kHz to 220kHz.

The unmarked capacitors are 47µF. I used plain aluminum electrolytics; but the best for switching or charge-pump power supplies is OS-CON. They're kind of expensive, but much better than even tantalum for having low impedance at high frequencies. This is a type of capacitor (Organic SemiCONductor), not a brand. It is made by a few different companies. They look like an electrolytic with epoxy on the bottom.

The diodes are 1N5817 Schottky diodes. Plain silicon diodes (like 1N4001) will have a lot more voltage drop, so the output will be a couple of volts less.

D1 through D4 are there to protect the 74AC14 outputs in the event the load changes suddenly and makes the capacitors discharge into them. (I found out about the need the hard way.)

Efficiency of this circuit (not counting the LM337 linear regulator I followed it with) was near 90% at light loads, and dropped below 50% near maximum load (near -200mA output).

Note that non -CMOS 74xx14's will give a much lower output voltage, because they cannot swing rail-to-rail like CMOS can, and they also cannot pull up hard like CMOS can.

There are lots of ways to do the above, and I can't cover them all; so if you have an idea for a different one, about all I can say is to breadboard it and try it to see if you can get the needed voltage and current from it. If you want my opinion on a circuit idea, you can email me at wilsonmines@dslextreme.com.



For other needs of doubling or negating a voltage, sometimes the 7660-related 8-pin DIP ICs are appropriate. These are capacitive charge pumps. There are many variations, each with their own strength; for example, some have a higher maximum operating voltage, some have higher operating current, and some even have regulators built in. See the data sheets for the various operating modes. Ones I have tried are (with a brief description of the part and a few actual numbers from my own experience with it) are:

Linear Technology LTC1044A: 1.5 to 12V input, and I used it in the inverting configuration to get an output that was the negative of the input. With 15µF/20V OS-CON capacitors and 8.3V in (from a 9V battery), and 8 to 25mA load, I got an output ripple of 32mV peak-to-peak @ 10kHz switching frequency (10mV @ 60kHz), and a maximum output impedance of about 35Ω, less under some conditions. The LTC1 1 44 works to a higher voltage: up to 18V in.

44 works to a higher voltage: up to 18V in. TI LT1054: +6 to +15V input, -5V output (in the configuration I built up, but the voltage can be changed), 100mA max. With an 87mA load, I got a 0.67Ω output impedance (while able to regulate), with a 15mV peak-to-peak ripple @ 30kHz, with 15µF OS-CON capacitors for the charge pump, and 150µF OS-CON on the output. Wow! At the same time, I have used its pin-1 square-wave output to feed another diode-capacitor voltage multiplier to get +17V; so I got -5V and +17V from a 9V battery.

National Semiconductor LM2662: 1.5V to 5.5V input (2-3 AA batteries, including when they get really low), and it inverts the voltage. The specs say 3.5Ω typical R output (6.5Ω @ 1.5V in), 86% typical efficiency @ 200mA, with 5.5V input. 16kHz typical operating frequency. I built it up but misplaced my test results. I'll post them later if I find them or have a need to use the circuit again and make measurements.



Switching regulators (which use inductors), whether buck or boost, are typically a better way than charge pumps to do power supplies, due to efficiency, particularly when regulation is needed; but they tend to do poorly on breadboards, and their PC-board layout is not for beginners. (The one I've used most in our commercial products is the MAX732 which allows you to start with anywhere from 4V to 9.3V input and get 12V out. Another is the MAX669 which let us start with a pair of AA batteries and get 12V out.) One way around the difficulty that switching regulators present for the hobbyist is to use integrated switching regulators which put all the difficult stuff in a small pre-made module, sometimes with the same pinout as the popular 7805 for example. Power Trends (now unfortunately taken over by TI) is one supplier whose 78SR112 (link to .pdf) module I designed into a product in 1993 and they continue to have a wide range of offerings. I've used their 78ST105 5V regulator too. Pololu Robotics and Electronics, MicroPower Direct, and Traco Power are three other such suppliers.









