Jump to content

User Port Dreams: SPI


BruceMcF
 Share

Recommended Posts

[Edited to reflect the news from David on Facebook about on VIA2 being an optional expansion card rather than built into the board.]

So, we are supposedly getting I2C. I am told (see below) that the CX16 at 8MHz when it's running its I2C without clock stretching gets about 150kbps-175kbps, or 18-22KB/s (raw throughput, minus bus managements & device command overheads).

And while I2C has been around for a long time, there is another fairly widely used communications protocol to talk to things like sensors and LCD panels and Real Time Clocks, which is the Serial Peripheral Interface, SPI. SPI has been used for letting micro-controllers talk to all sorts of peripherals at speeds appropriate to the application for decades now. Plus for anything where those not in the project might wish to "cheat" and have a RPi Nano or RPi Zero do, SPI is a very convenient way to talk to a Raspberry Pi board, typically substantially faster than RS232 serial.

So the I2C -- which I now know is running at around 150-175 kbps -- is what I was holding in mind as the built-in alternative to SPI over the User Port.... especially since I2C is more widely supported nowadays in most areas.

So I did some sketches. None of the code is tested, including because none of the hardware it would run on to be tested with exists, so the clock counts are only rough ballpark figures.

One challenge with all the approaches, but especially the fastest approaches, is that SPI comes in four flavors. First is the clock polarity. Mode 3 and Mode 1 work the same, except with an inverted clock. Mode 0 and Mode 2 work the same, except with an inverted clock. The "even" modes start and end their cycle of sending data (byte, normally) with the clock low, while the "odd" modes start and end their cycle of sending data with the clock high.

The thing is, to cope with a device with the "wrong clock polarity", you can just run the serial clock line through an inverter and bring out both the original serial clock and the inverted serial clock, and connect the one that gives the desired clock polarity. That can be done with an inverting line driver or by sending the same signal through both inputs of a two input NAND or NOR gate, so that is not something that would actually have to be dealt with in software if we are thinking about some interface board being developed to allow talking to some SPI peripheral chip..

Requiring more thought is the clock phase.

SPI works by having the master pull down a select line, then driving a clock line while it sends output to the servant device on the Master Out, Servant In line (MOSI, often pronounced "moe see"), and also receiving input from the servant device on the Master In, Servant Out line (MISO, sometimes pronounced "my so" and sometimes pronounced "me so"). So to talk to only one device, only four lines are required. Adding devices only require adding one more select line, since the clock, MOSI and MISO lines can work as a serial bus.

Any synchronous serial connection can work in one of two ways. You can assert the data and then toggle the clock, using that to latch the data, then toggle the clock back, using that to shift the data. Or you can toggle the clock, using that to shift the data and assert the data, then toggle the clock back, using that to latch the data.

What's going on is that if you want this kind of simultaneous swap of data using a serial shift register at the servant, you need an extra carry bit. However, it can hold either the bit being shifted in, or the bit being shifted out. If it is holding the bit being shifted, you connect MOSI to the carry and MISO directly to bit7 of the servant Serial Shift Register (SSR). The data is on the SPI bus if the device is selected, so toggle the clock to latch it, then toggle it back to do the shift, with the contents of the carry register going into bit0 and the old bit7 being over-written.

If the carry register is connected to the bit being shifted out, you connect MOSI directly to bit0 of the servant SSR, and connect MISO to the carry register. Now, the data in bit0 of the servant is in the way of the incoming data, so you shift the data left by toggling the clock, which opens up a space in bit0 of the servant register and puts the prior bit7 contents into the carry register, and then you latch the data on both sides by toggling the clock back to where it started.

Basically, there are 16 different ways to accomplish the kind of transfer that SPI is doing: select by going high or going low, shift right or shift left, start the clock high or low, and latch first or shift first, and all that SPI has standardized is select by going low and shifting left, leaving four different ways that different peripheral devices might implement their SPI. So the clock polarity is given a value of 0 if it starts low, 2 if it starts high, the clock phase is given a value of 0 if it latches first, 1 if it shifts first, and add them together to get the "Mode" of the device: Mode0 through Mode3. The SPI required to be supported by SD cards is the start low, latch first kind of SPI, so its Mode0. The VIA SSR is start high, shift first kind of shift register, so the most natural mode for the CX16 is Mode3. And while inverting clock polarity is easy to do in hardware, so connecting a Mode1 servant to a Mode3 master is no issue, connecting a Mode0 or Mode2 servant to a Mode3 Master is trickier.

I don't know enough about electronic circuits to know how to get around that problem for the fastest User Port approach I could come up with, which relies on a VIA serial shift register configuration that drives the serial shift register at half the system clock speed. But for slower approaches, which drive things a bit at a time, I think I've worked out how to handle it. The thing is, when you swap BOTH phase AND polarity, between the VIA "Native" Mode 3 and the SD card Native Mode 0, they both latch data on the rising clock edge and shift data on the falling clock edge. The only thing is that the Mode3 needs an "extra" half clock at the start, to shift out the first data, where Mode0 starts with the first bits of data already asserted. And Mode0 needs an "extra" half clock at the end, so the servant will shift the last bit of MOSI data in.

If generating the clock in software, that is not hard to handle. However, even with VIA hardware generating individual clock pulses, it is still straightforward to do. With literally a single logical gate connected to a GPIO, you can set up an SPI bus that allows a Mode3 Master to imitate a Mode0 master on the SPI Bus. This relies on a an extra output pin that masks out the Master's Serial Clock and holds the SP bus clock low. When accessing a Mode3 Servant, the mask is left off. When accessing a Mode0 servant, for a Mode0 device, the SPI bus clock line stays low until the mask is released, and the SPI clock goes high, so the servant device "doesn't know" that the Master's serial clock did an enter cycle, and instead sees the release of the mask as the first half of a cycle. At the end of the process. for a Mode0 Servant, the mask is pulled down into place again, and that gives the last half cycle the Mode0 servant needs to latch its data.

So that was the idea. How did it go? This also gives a baseline idea about how slow it might be.

The first sketch is about what can fit into the resources now available on VIA1. In the current version of the board, all spots on VIA1 are in use by the system, but removing the PS/2 interface to an MCU frees up four VIA pins. Those pins could be brought out to a block header, alongside the CA1 and CA2 handshake lines that the system does not use. And that is enough to provide a "bit banged" SPI interface: SCLK, MISO, MOSI, Select. Indeed, it is enough to support a generic SPI interface that can be fanned out to support multiple SPI devices, because CA2 can be set to be low or high, so it works as an additional output line. And a second select plus two glue logic chips on the other side of the interface (a "NOR" gate and a Serial Shift Register) supports using the SPI bus to write a select byte for up to eight devices into a serial when the CA2 is selected, and then wire the primary Select line from Port A or B to the serial shift register Output Enable, so the loaded selection from up to eight choices is asserted by a single GPIO.

In data port A, pin 0 is the serial clock, pin 1 is Master Out, Servant in, in Port B pin 0 pin 7 is Master In, Servant Out, and pin 6 Select. CA2 is alternative select, and CA1 is available for an /Alert signal for those SPI devices that have an extra pin to send an interrupt or error warning. No actual serial clock mask is needed for the serial clock line, since the serial clock line is being driven entirely from software.

In the words of Crocodile Dundee, "you can live on it, but it tastes like sh*t". There's no guarantee that I have the fastest possible code, but I think its pretty close, and I get a routine to send/receive a byte at 311 clocks in Mode0, 306 clocks in Mode3, which is an effective maximum bandwidth of around 200Kbps, or just a touch faster than the CX16 I2C bus -- from 14%-33% faster, depending on where it lands in the 150kbps-175kbps range for stable operation.

Now, suppose that you bought that expansion User Port card. In that case, you have all of a VIA to work with, so it's possible to "builda better big-banger" than this. The next sketch builds the input byte "in place" by setting Port B pin0 to input, connected to MISO, and the rest of the PortB pins to output, not connected but acting as bit registers. The output byte is shifted left to put the first output bit into the carry flag, and the rest stored in PortB, so the input is shifted into place at the same time that the output is shifted to the carry flag. MOSI, Select and Alert are in PortA along with 5 device selects. The Device Select routine creates complete Bit=0 and Bit=1 states for Port B, including the correct select flag and the clock low so storing the value of the MISO bit also starts the clock cycle. The clock pin is PortA pin0, so an "INC PORTA" instruction toggles the serial clock line back up again  The Bit0 and Bit1 states for PortA are loaded into registers A and X at the beginning of the routine and Y is used for the loop index.

This is a bit better, with around 229 or 234 clock cycles per byte, depending on Mode, so between 34KB and 35KB per second, or between 270 and 280 Kbps throughput ... now 50% faster than CX16 I2C. The wiring is every bit as easy as the PortA-only bit banged approach, just connect MISO, MOSI, SCLK and the Selects to the correct pins on the UserPort header.

Next faster is to have the VIA generate the Serial Clock itself using the handshake lines of Port A. You can put PortA into a mode where it puts a pulse out when Port A is written to. So this approach dedicates PortA to Master Out, Servant In, with Pin7 connected to MOSI, with the bits shifted in by simply shifting them, "ASL CIA2_DataA". The shift works because the 65C02 processor does a shift in memory by reading the RAM, then doing a redundant read of the RAM while shifting the data, then writing the RAM. The NMOS 6502, or 6510 in the C64, does a write for the second clock cycle when the shift is actually being performed, so it would trigger the pulse twice for each shift. MISO is connect to Port B Pin7, which is shifted out. Port B also It holds two select lines and the serial clock "mask" line in pairs of pins, which answer the question of how you shift the MISO data out without messing up the status of the other pins in the register. Pins 6+5, 3+4 and 1+2 are wired together, with pines 6, 4 and 2 being set up as outputs, and 5, 3 and 1 being set up as inputs. When the data is shifted, the input pins don't care, directly, but their neighbor that has data written to it is reflected in the input pin. Then when the shift instruction picks up the data in the Read cycle of Read-Modify-Write, the shift puts the correct value back into the output pin, and then the wire brings that value back to the input pin.

Finally, the SPI bus clock mask to support Mode0/Mode2 devices can be provided with a single two-input AND gate, with one input connected to the Mode3 SCLK and another connected to Pin6 of PortB, works to mask the serial clock line. An alternative is to use a NOR gate. That inverts the clock, but running the inverted clock through both inputs of another NOR gate will bring it right way up again, so the hardware to handling the Mode0, Mode1 and Mode2 translations is a single quad two-input NOR gate, with two of the gates still free.

Anyway, after the first bit is managed "by hand" to handle the store to PortA rather than shifting it, and "unmasking" of the serial, the following bits are "ASL PortA : ASL PortB : ROL", which speeds things up a bit. If I "unroll the loop" by putting that three instruction sequence into the code seven times, I get about 195 clocks for Mode3, 179 clocks for Mode0, which at 8MHz is about  325-350 Kbps. So offloading the work of bit banging the Serial Clock doesn't quite get us up to I2C fast speeds, ... but it's 85%-100% faster than CX16 I2C.

But in any event, all three of these are competing against the I2C routine with one hand tied behind their back, since they aren't actually making use of the serial shift register built into the VIA. So the fourth approach uses the VIA SSR as MISO. It puts the Serial Shift register into the mode where it is driven by an external serial clock, and uses the PortA handshake line to generate that serial clock.  That is the approach that runs Mode0 in 99 clocks for a byte and Mode3 for 89 clocks for a byte, or roughly 640-720Kbps bandwidth, in the neighborhood of four times faster than CX16 I2C. .

One quirk of that approach is that SPI works with the Most Significant Bit first, while the VIA SSR is Least Significant Bit first. So part of the third approach is a 256 byte table that gives the mirror image of the MISO byte in the shift register ... because it will finish the eight SPI serial cycles with Bit7 where Bit0 is supposed to be and Bit0 where Bit7 is supposed to be.

Now, it can get faster. You can add an external serial shift register, and use the "byte single shot" mode of the built in CIA SSR to put out a byte within 20 clocks of being stored to the VIA serial shift register ... getting throughput as high as 1Mbps (over 128KB/s), like one of the newer higher speed variants of the I2C bus. But what I'd want is a four mode SPI interface ... and I can only see how to make that work for a Mod3/Mode1 interface. I don't know enough about hardware circuitry to build the circuit to pin the mask down, then release the pin but continue to hold the mask down until the native SCLK line goes high and then release it. I know there is some combination of bit latches and flip flops than can do that, but I don't know exactly what combination that would be.

Still, for most of what I would want to talk to through the interface (up to and included cheating and using an RPi W to copy files from my PC to my Commander X16), I think that 80KB/s might well do me. Plus, being able to support all four modes with two, two-input NOR gates is really appealing, since the standard "74" glue logic comes with four two-input gates per chip.

 

SPI_VIA_Version2.asm

Edited by BruceMcF
  • Like 1
Link to comment
Share on other sites

Just wanted to clear up the I2C speed question - using nearly every trick available, I2C is running stable at 150-175KHz. It might be possible to run it faster, but not by much. It does support clock stretching so slower I2C devices that can't go over 100KHz shouldn't have a problem (as long as they apply back-pressure by stretching the clock).

Link to comment
Share on other sites

I should also add that both Kevin and I are leaning towards making VIA2 optional using a header on our respective boards. There may be enough pins on VIA1 now to do SPI there. I briefly mentioned this to Kevin, but he is generally cautious about adding new functionality.

  • Like 1
Link to comment
Share on other sites

On 9/20/2022 at 3:35 AM, Wavicle said:

I should also add that both Kevin and I are leaning towards making VIA2 optional using a header on our respective boards. There may be enough pins on VIA1 now to do SPI there. I briefly mentioned this to Kevin, but he is generally cautious about adding new functionality.

Wait, what? No User Port?

There are enough pins on VIA1 to do SPI by pure bit-banging, but it would of course be slower. Indeed, while four more GPIO on VIA1 would be handy in a number of User Port applications, on their own then a slow SPI bus is about all they would be useful for.

To support a slow SPI bus on VIA1, you'd want to move NES Latch and NES Clock to PortB, so that the four NES lines in PortA are all input lines, Then you can bit bang PA.0 as SCLK, PA.1 as MISO, PA.2 as MOSI, and PA.3 as /Select, with CA2 as an extra select output line via the PCR register at BASE+$0C =%xxxx11bx, allowing either 2 selects if used directly, or 3 if fed through a 2-4 active low decoder with one line unused.

The main bit transfer cycle would be something like the following, where:

  • "MOSI" is a zero page location shifting the output byte out and the input byte in
  • CPOL is a general location storing the clock phase, $00 or $01
  • MOSI0 is a zero page location with the byte to store to PortA for a zero bit
  • MOSI1 is a zero page location with the byte to store to PortA for a one bit
  • The Select routine and the end of this routine implies the Serial Clock starts high if this is a mode3 transfer, or low if it is a Mode0 transfer

SPI_BYTE: ; Called with SPI output byte in A, returns with SPI input byte in A, uses X, Y.

  ROL : STA MOSI : LDX MOSI1 :  LDY #7

  - LDA MOSI_bit0 : BCC + : TXA : + STA VIA1_PA : INC VIA1_PA : LDA VIA_PA : LSR : LSR : ROL MOSI : DEY : BNE -

  LDA MOSI : ROL : LDX CPOL : BNE + : DEC VIA_PA : + RTS

Clocks ~= 10+[7*35-1]+12+11 = 277 clocks for Mode3, 283 clocks for Mode0, 220-230 Kbps

So long as you have 2 select GPIO available, with a little more work you can get a generic number of selects available, since you can use an AND gate with the SERIAL clock and one of the selects to drive the clock of a tri-state parallel out Serial Shift register when the select is High, and load a device select byte into the SSR, then pull that select low and have the other select be tied to the OutputEnable on the serial shift register. That would require a new SPI_SELECT subroutine, storing the last select byte to work out whether it is necessary to write a new value into the Select SSR ... but the SPI_BYTE subroutine would not change.

Edited by BruceMcF
Link to comment
Share on other sites

On 9/20/2022 at 5:14 AM, BruceMcF said:

Wait, what? No User Port?

It's under consideration. Nothing definitive yet, but if removed from the base design, there will be a dedicated header for those who would like to add it back in. Its location in IO space will not change.

Link to comment
Share on other sites

On 9/20/2022 at 11:29 AM, Wavicle said:

It's under consideration. Nothing definitive yet, but if removed from the base design, there will be a dedicated header for those who would like to add it back in. Its location in IO space will not change.

A header or a socket?

I mean, I could live with an empty socket, for those who don't want to make use of the User Port. The kind of people who are going to use the User Port are also not likely to complain TOO much about having to buy a VIA and install it for their first User Port project.

But the two big appeals of the User Port over an extension board for the CX16 (or a cartridge for the C64) are the source of the ease of use in projects that are based on it. First, the interface TO the user port is standard. Whether its a card edge or a block header is neither here nor there to me, but having a block header with the VIA lines at set locations avoids the headache of project #1 wires the VIA this way and project #2 wires the VIA that way. And second, it's on the "low frequency" side of the VIA, so the frequency domain can never get higher than PHI2/2 and can be as slow as the driving software lets it be.

It's a stable platform for hardware projects in the way that a stable Kernel API is a stable platform for software projects, and not having to hit PHI2 bus timings makes it more robust for beginners to hardware projects.

Shuffle the NES Latch and Clock to VIA1.PB0 and VIA1.PB1, to consolidate the "free" GPIO into VIA1.PA0-VIA1.PA1, put a header with +5VCC, GND, VIA1.PA0-PA3, VIA1.CA1/2, and any VIA2 lines not used in a parallel port interface (I forget ... maybe VIA2.PB6/7, and VIA2.CB1/2), and another header with the PC parallel port layout from VIA2.PA0-7, VIA2.PB0-?, CA1/2.

Then there is a "teaser" User Port that can be used for a limited range of projects, and installing a VIA2 in the socket gives a standard parallel port and bit-banged serial port on two separate block headers, or an ability to use two ribbon cables to plug into a breadboard that has 20 GPIO and 3 pairs of handshake lines, including one pair that can connect to a shift register.

 

Edited by BruceMcF
Link to comment
Share on other sites

I have little to contribute, except that I have worked with I2C on the RPi, gathering data from an ultrasonic motion detector, and a range finder.  I found I2C on the RPi to be easy to work with, enabling fun.

I've *seen* the SPI pins on the Pi as well, and I just assume it is also easy to work with. 

Because I'm not a hardware guy, I look for Ease Of Use when it comes to hardware projects.  I can solder a header onto a Pi, if I'm careful.  That's about my limit.

 

Link to comment
Share on other sites

On 9/20/2022 at 1:46 PM, rje said:

I have little to contribute, except that I have worked with I2C on the RPi, gathering data from an ultrasonic motion detector, and a range finder.  I found I2C on the RPi to be easy to work with, enabling fun.

I've *seen* the SPI pins on the Pi as well, and I just assume it is also easy to work with. 

Because I'm not a hardware guy, I look for Ease Of Use when it comes to hardware projects.  I can solder a header onto a Pi, if I'm careful.  That's about my limit.

AFAIU, I2C is easy to use if its built into the thing you are using, and it is the most economical in pins of the general purpose serial buses ... its easier for SPI to go fast (note what Wavicle reports about I2C speed being around 150-175kbps, while I think (as shown above) by being more careful in the programming of the big banged interface, even a bit banged SPI can go moderately faster, and if the VIA Serial Shift Register can be put to use, it could well be 3-4 times faster.

So for talking to keyboard or a mouse, I2C is just fine. For talking to a RPi Nano acting as a USB Flash drive bridge, I'd rather use SPI, and then rather have enough of the VIA resources to give it 80KB/sec bandwidth.

Edited by BruceMcF
  • Like 1
Link to comment
Share on other sites

On 9/20/2022 at 3:35 AM, Wavicle said:

I should also add that both Kevin and I are leaning towards making VIA2 optional using a header on our respective boards. There may be enough pins on VIA1 now to do SPI there. I briefly mentioned this to Kevin, but he is generally cautious about adding new functionality.

____________________________

[NOTE: I have edited the original post to reflect the information that has come to light in this discussion -- and also the improved "four line big banger" routine that I came up with along the way.

____________________________

What Dave said on Facebook was an expansion card. If the expansion card had one VIA soldered in with Port A and Port B block headers with power in each, and a socket for a second VIA with two more Port block headers ... that would be partial compensation for losing VIA2 from the motherboard.

An expansion card available from the project itself is better than a block header on the motherboard, because one of the big appeals of a User Port over the expansion bus is that its a stable base for the hardware projects that attach to it. The block header is going to lead to a lot of different things that plug into it, and being after--market hardware they won't all be compatible with each other, splintering hardware project designs that rely on the thing plugged into the block header.

If there is a User Port expansion card that is available  for pre-order alongside the CX16 itself, that pre-empts the after-market reinventing that wheel for adding one or two VIA's to the CX16 system.

Edited by BruceMcF
Link to comment
Share on other sites

On 9/21/2022 at 12:28 PM, Wavicle said:

Kevin posted an update to Facebook on the topic of 6522s and user port: Commander X16™ Prototype | Facebook

The most encouraging part of that post was the statement that this was not finalized, so hope still springs (for the moment, if not springing eternal) that the SNES Latch and SNES Clock outputs might be able to get swung around to PortB.

I can't use the pulse PA2 mode for SPI if there is an interrupt and the system toggle the SNES Latch and then starts banging on the SNES SCLK, all the while my SPI Select is still sitting low and my SPI SCLK is reacting to each write of PortA. Now, the Kernel could work around that by using the BASE+$0F register address that does PortA writes without triggering handshake, but I would rather avoid problems over editing the Kernel to work around them.

Link to comment
Share on other sites

On Facebook, Kevin has proposed a "VIC-20 style" user ports, where effective use of many of the pins would require disabling the SNES Controller and the IEC port.

Some on Facebook have objected to this approach. They don't want the lines used by the system on the User Port. I am not in one or the other camp on this, but I do have a lot of sympathy for this position.

I have argued for my swap around that puts the NESDATA lines on PortB4-7, has four free GPIO on PortB0-3, and uses the CA2 handshake line as the NESLATCH output.

With the news of an official VIA Expansion User Port, I would grudgingly accept a small Motherboard Baseline user port of [PortX0-2 + CA1] or [PortX0-3] + [CB1 & CB2[ + VCC + GND., on either a single or double row pin block header.

Link to comment
Share on other sites

On 9/24/2022 at 10:59 AM, Fabio said:

it seems reasonable from a programmer perspective to have all the via lines dedicated to user port to originate frome the same side.of the via

Yeah, I didn't think that through.

PA0=I2CDAT / PA1=I2CCLK / PA2=NESLATCH / PA3=NESCLK / PA4=NESDAT3 / PA5=NESDAT2 / PA6=NESDAT1 / PA7=NASDAT0

PB0=User / PB1=User / PB2=User / PB3=SERATO / PB4=SERCLKO / PB5=SERDATO / PB6=SERCLKI / PB7=SERDATI

CA1=User / CA2=User / CB1=User / CB2=User

An FTD TTL-Serial to USB cable ($6-$10) has a 6 SIL that is GND / CTS / +5V / TXD / RXD / RTS so if CTS is on the output-only CA1 and RTS is on the edge detect input CA2, the User Port pin header might be (TTT is a clippable pin allowing a keyed 2x6 block header with the keyed pin filled):

  • GND / TTT
  • CA1 / CB1
  • +5V / CB2
  • PB0 / +5V
  • PB1 / PB2
  • CA2 / GND

 



 

Link to comment
Share on other sites

OK, so this approach can be summarized, "I2C bus and NES controllers on Port A, User Port and IEC lines on PortB, and all the handshake lines on the User Port". I really prefer putting the I2C bus on Port A, swapping the IEC to Port A, keep NESLatch and NESClk on Port A, using CA1 for NESLatch, but swapping NESDat to PB4-PB7 ...
... which would allow a "clean" borrowing of NES lines for Controller 3 and 4, which on many systems are not going to be in use.

However, that would require buy in from Micheal Steil, because the Kernel would have to be reassembled with the new locations of things.

It is simplest to place the I2CDATA/I2CCLK where PS2KDAT/PS2KCLK used to be, free up what used to be PS2MDAT/PS2MCLK and the single I2CDAT GPIO when the SSR was used for I2CCLK, setting up PB0-PB2 as the User Port GPIO User0-2, and having the use of CA2 as a fourth line that is output-only.

An advantage of this is that whether or not the system is going to drive any IEC lines is under software control after the autoboot has completed -- that is the Kernel doing something because you called the Kernel and asked it to do something, If you don't want it to happen, leave it be. So in addition to using CB1/CB2 as the serial port, there is the option of setting it up in write handshake mode, as long as you remember to turn write handshake mode off (or deselect the thing that is listening to the CB2 write handshake mode) when you are done.

Over on Facebook I sketched out some things that could be done with the User Port even without a VIA#2 expansion board. But things in FB discussions are often eventually lost in the mists of time, so I thought it best to copy them over here for something closer to safe keeping.

First is bit banged serial. C64 bit banged serial is 1200baud, and if it weren't for VIC-II badlines, it would be able to hit 2400baud. So at 8 times the clock rate, then I would suppose certainly 9600bos, maybe 19200 -- especially if using hardware flow control, so you could wrap the VBlank IRQ routine with a "CTS" as not ready, and restore its state on return from the original IRQ routine.

Second, a DB-9 Atari Joystick / Sega Two Button controller port. Using a parallel-in, serial-out (PISO) serial shift register connected to the CB1 and CB2 for its clock and serial output, and User0/User1 to latch the data and to pass through the serial clock, you can have a small board with a right angle DB9 input and a ribbon cable to the User Port to support traditional C64 joysticks. Also, an alternative Sega 6 button controllers could be supported by user User2 or CA2 as the output to the controller to get the rest of the button data (alternative because that design is incompatible with the Atari Joystick design).

Third, 2 or 4 DB9 Atari Joystick / Sega Two Button controller ports. Using a 2-4 decoder and a 4-1 selector you can use 2 GPIO to select PISO0-PISO3 and the third as the active select. I don't have time to go back into the Sega 6 button controller info to see if the Motherboard User Port has enough outputs to handle that as an alternative.

Fourth, an SPI interface for modern geegaws.

Fifth, a ribbon cable to bring the lines out to a breadboard and a set of serial shift registers and encoder/selecter to put 8 or 16 input and/or output lines on a breadboard.

So, as long as the CB1/CB2 lines are available and there are 3GPIO and an additional output, I think that is a User Board that has enough use cases to warrant including.

Edited by BruceMcF
Link to comment
Share on other sites

A couple years ago someone made a joke video about this project, and it showed a breadboard sticking up out of the case. Now you're telling us: this is actually possible, as a daughter board.

It's a little late for design changes here, but if this makes sense to @Michael Steil and @Frank van den Hoef the applications of this little machine expand from teaching programming to also teaching electronics. 

  • Like 1
Link to comment
Share on other sites

On 9/26/2022 at 1:49 PM, Ed Minchau said:

A couple years ago someone made a joke video about this project, and it showed a breadboard sticking up out of the case. Now you're telling us: this is actually possible, as a daughter board.

It's a little late for design changes here, but if this makes sense to @Michael Steil and @Frank van den Hoef the applications of this little machine expand from teaching programming to also teaching electronics. 

If used for general teaching electronics, I'd suggest that the Expanded User Port from the VIA#2 on an expansion board is a no-brainer ...

... but the thing about including "whatever is available and not otherwise used" on a User Port is that it is a teaser which can give a taste of what can be done without having any additional barrier to entry. If you can plug a $6 USB cable into the User Port pin header and talk to a laptop at 9600 bps bit banged serial, then the experience of doing that is going to sell more VIA#2 expansion boards when people ask around, "can't it go any faster"? "Oh yeah, sure, with a VIA expansion board you can run that serial interface 6 times faster at 56Kbps".

And "whatever is available and not otherwise used" is really what Commodore was doing, after all -- the absence of Serial Shift Register in the VIC-20 User Port and presence of two in the C64 User Port had to do with what was left over when finished using the VIA/CIA for reading keyboard, reading joysticks, and etc.

Note that this evening I did have enough time to look at the SPI_OUT and SPI_IN routines for the pure bit-banged SPI serial, which is about 200kbps in the two-way Out and In routine ... its 230-240 kpbs for just inputting a byte from the SPI bus with a dummy $00 going out, and 290-300 kbps for just outputting a byte to the SPI bus with a dummy $00 received. So the only 14% faster than I2C is while sending a byte of data in both directions, rather than one way ... just pulling data in, it's 38% faster and just putting data out, it's 70% faster.

Now, using the hardware SR and a NAND gate for Mode0 / Mode1 / Mode2, I get about 290kbps either both ways or output only, and 575 kbps input only Mode3 (540bps Mode0). That's with only the C1 pin free for a /Select line, but if talking to a RPi Nano being used as a USB flash drive bridge, 70KB/sec download is a lot better than the 1541 that I started this whole journey with, some 41 years ago.

Edited by BruceMcF
Link to comment
Share on other sites

For the expansion User Port based on the VIA on an expansion port slot:

PortA: MOSI. PortA7 tied to MOSI, PortA0-PortA6, DNC but set up as outputs, so they work as registers.

PortB:

  • PortB0-PortB5, /Select0-/Select5
  • PortB6: "SMASK", this is tied to an AND for Mode0 devices to convert a Mode3 SPI system based on the VIA into a Mode0 SPI bus
  • PortB7: "/Alert", an input that the SPI device holds low to send a special signal. Note that there are two spare NAND gates in the circuit to implement the Mode0/Mode1/Mode2 support, so if the device raises a line to send a special signal, that can be easily converted to a pull down /Alert.

CA2 is put into Pulse Output mode, and acts as SCLK (the Mode3 internal serial clock). The pulse is one full PHI2 cycle wide, so this particular interface is for SPI servant devices that can handle 4MHz or higher SPI clock (that is a fairly common top end speed for medium-fast SPI chips).

CB2 is the serial shift register input, with the SSR configured as driven by CB1.

CB1 is the serial clock input into the serial shift register. It is tied directly to CA2, not to the SPI_SCLK, since the VIA SR is a Mode3-only MISO.

CPOL is a location in the program memory that is $00 if Mode3/Mode2 is desired, set to %01000000 if Mode0/Mode1 is desired. This works as a mask for TSB/TRB operations to pull the SMASK bit up and put it back down, but only if Mode0/Mode1 operation is desired.

SPI_BYTE: ; Output byte in A, Input Byte returns in A, returns CC if /Alert is not sent, CS if Alert is sent
  STA VIA2_PORTA : LDA CPOL : TRB VIA2_PORTB
  ASL VIA2_PORTA : ASL VIA2_PORTA : ASL VIA2_PORTA : ASL VIA2_PORTA
  ASL VIA2_PORTA : ASL VIA2_PORTA : ASL VIA2_PORTA
  LDA CPOL : TRB VIA2_PORTB : LDA VIA2_SR
  BIT VIA2_PORTB : BEQ + : CLC : RTS
+ SEC : RTS

I get about 89 clock cycles (90 is alert is sent), so 89.888KB/sec, 719kbps, 4-5 times faster than the I2C bus.

For something like a MAX3001 SPI UART, this seems like it would be plenty fast to run it at very high serial port baud rates. For something like a Raspberry Pi Pico used as a Flash USB drive loader, it would copy fairly large files into the SD card fairly quickly as well as being a quite reasonable "keyring Disk9" to complement the built in SD card Disk8. And stepping up to a RPi Zero-W makes for a budget WiFi internet modem option.

Link to comment
Share on other sites

I am copying my recent post to the Facebook post for safekeeping, since it is so easy to get lost on FB:

Note that a "retro" edge card user port is available simply by having an expansion card with an edge connector. As an expansion card, this doesn't have to be built right away, but having it "on deck" addresses desires for a retro VIC-20 / C64 style edge card User Port without requiring that to be built into the main CX16 board design.

Implementing the Serial Port parts of a C64 User Port top edge would be straightforward if there is a pin header on the main board bringing out VIA1_CB1 and VIA1_CB2.

Implementing a reasonably C64-alike User Port bottom edge would be straightforward on an expansion card: put out PortA (rather than PortB), so that CA1 and CA2 can serve as the handshake lines, PB2 and /Flag. This actually brings the output handshake full circle, since in the Vic-20 User Port the bottom edge is PortB and the VIA handshake or serial port lines CB1 and CB2. I think if old hardware might be plugged in here, the +/-9VAC lines should be DNC.

1: GND
2: +5V
3: /Reset
4: VIA1_CB1
5: VIA1_CB2
6: CB1
7: CB2
8: ???
9: ???
10: [DNC]
11: [DNC]
12: GND

Since VIA#2_CB1 & 2 are brought out on the C64 User Port Serial Port lines, they are not available as 8bit write handshake lines. Also, VIA PortA has both hardware read and write handshakes, making PortA the more flexible choice for the bottom of the User Port. Now rather than the C64 use of a GPIO on the output handshake line, the output handshake can be a hardware handshake line as on the Vic-20 User Port:

1: GND | A: GND
2: +5V | B: CA1
3: /Reset | 😄 PA0
4: VIA1_CB1 | 😧 PA1
5: VIA1_CB2 | E: PA2
6: CB1 | F: PA3
7: CB2 | H: PA4
8: ??? | J: PA5
9: ??? | K: PA6
10: [DNC] | L: PA7
11: [DNC] | M: CA2
12: GND | GND

Now there are only two lines to place. In the VIC-20 User Port they are cassette switch and IEC-ATN. In the C64 User Port, they are the CIA#2 output handshake line and IEC-ATN. As with the C64 User-Port putting a GPIO where the Vic-20 has a VIA handshake line, these would best be filled by PortB lines. As swapping PortB and PortA means that the PB6 and PB7 pins which interact with the timers in some timer modes are no longer available, PB6 & PB7 are likely the highest priority. These have the added advantage that they can be tested directly by "BIT PORTB" without setting up a Bit Mask, as the BIT operation sets the Sign and Overflow flag based on the values of bit7 and bit6, respectively:

1: GND | A: GND
2: +5V | B: CA1
3: /Reset | 😄 PA0
4: VIA1_CB1 | 😧 PA1
5: VIA1_CB2 | E: PA2
6: CB1 | F: PA3
7: CB2 | H: PA4
8: PB7 | J: PA5
9: PB6 | K: PA6
10: [DNC] | L: PA7
11: [DNC] | M: CA2
12: GND | GND

This fills the use case for the pin header layout in the OP, so the main board User Port can focus on simply bringing out the "spare" VIA#1 pins. Rather than trying for a "legacy" layout, it is laid out to a 6pin SIL to TTL serial cable can plug directly into one side of the pin header, and so that power lines are are identical for 5-pin SIL plugged into the second side of the pin header with "pin2" as Pin1.

1: GND | 2: GND
3: CA2 (CTS) | CA1 (/ACK)
5: +5V | 6: +5V
7: PB0 (TXD) | 8: CB2 (SDATA)
9: PB1 (RXD) | 10: CB1 (SCLK)
11: PB2 (RTS) | 12: DNC

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

Please review our Terms of Use