There were a couple of errors in that schematic - the WDC 65C51N part has an open-drain IRQB so it needs a pull-up resistor, and some of the other signals like ROMDIS and /ENDSUPER also need pulling one way or the other to ensure they default to sensible states before the VIA pins are in output mode.
I wasn't sure whether to just do a PCB layout for this, or whether to prototype it first. I decided to prototype it, so yesterday I went ahead with that. I tend to plan the layout in KiCad, even for solderless breadboards - the rats' nest is very helpful for this. My priority is generally to redure the length of wiring, rather than signal integrity, as the signal integrity is going to be bad either way, and long wires are hard to manage on breadboards. Here's the planned layout:
Attachment:
6502multitaskingcomputer-breadboardlayout.png [ 172.22 KiB | Viewed 6182 times ]
The top row contains the watchdog timer, the pair of flipflops that manage the SUPER state, some glue NANDs, an oscillator for the ACIA, the ACIA, and the ROM.
The second row contains the reset switch, main oscillator, CPU, a '138 decoder, and the sytem VIA.
The third row contains another '138 decoder, the multiplexers for the pagetable RAM address bus, a '139 decoder pair, and some AND gates.
The fourth row contains the pagetable RAM, the system RAM in two ICs, and the transceiver to connect the pagetable data bus to the CPU data bus during pagetable read or write operations.
I laid these out on the breadboard according to this plan, but I squeezed the top row to the left to make more space around the EEPROM so that it would be easier to access, and I also made room for some schmitt inverters on the third row for and RC reset circuit as I can't find my DS1813s. I've also only put one system RAM IC in, and it's a small one - I'll test it like this first, and if it works then I'll steal larger RAM ICs from one of my video circuits.
Next I laid out a ground grid, and less importantly, a power grid, and I routed ground to all the ICs, and power to the subset I'm going to test first - essentially everything except the RAM interface:
Attachment:
20231212_223007.jpg [ 3.21 MiB | Viewed 6182 times ]
Next I wired up all the connections:
Attachment:
20231213_003729.jpg [ 3.29 MiB | Viewed 6182 times ]
I was not feeling very patient, so I reused long premade Dupont cables for most of the buses. Some of these I made in the past by crimping my own connectors onto the wires, others using pre-cut, pre-crimped wires. Having lots of these wires in one housing makes it very easy to plug and unplug them - the data bus is 8 wires in one connector, the address bus is one 8-way connector, one 4-way, and loose wires for the rest. I have a special splitter for the data bus going to the ROM which is a 1x8 socket on one end and two sockets on the other end, one a 1x3 and one a 1x5. I also use a special cable for A8-A11 from the CPU to the ROM which has four wires but goes into a 1x5 socket at the ROM end, with pins in the order: A8 A9 A11 NC A10. This makes it very easy to hook these up without needing to remember how they go.
Other than that, I just used extra-long wires for long connections. This was partly because I do the routing methodically, starting with power and ground, and then working through each IC in turn connecting it to others. Being methodical is important because it's very easy to miss a connection otherwise, especially in busy areas. Long distance connections don't play well with that because if you try to route them tidily they end up trapped under a lot of shorter connections later on - so I chose to just make these extra long, so that at least the connection was there, and consider going back to optimise them after all the short connections were in.
With that all in place I made a quick test program to initialise the VIA and write increasing values to port A (the PID). It didn't work very well - diagnosis revealed issues with the data coming from the ROM. This was due to my ROMCS signal being gated by PHI2, meaning the ROM has only half a clock cycle to present its data - it was doing so around the falling edge of PHI2, so not quite early enough. This was with an 8MHz CPU clock, so ROMCS was only asserted for about 60ns, meaning that as far as the ROM was concerned it looked more like a 16MHz clock, which is too fast. I downgraded the clock to 4MHz (120ns high period) and it worked. This tallies with my past experiences - it's equivalent to 8MHz with an ungated ROMCS, which is generally fine although the EEPROM is specified to want 150ns; but clock speeds any higher than that are more risky.
Next I took some code from Garth's RS232 and 6502 primers to set up the 65C51 as I've never used one before, and got it to send and receive data, with a view to having it load code that way for faster iteration, when the system has RAM to load it into!
So the CPU, ROM, VIA, and ACIA were working fine. Before moving on to add RAM, I wanted to test the more bespoke features of my circuit - even with just this subset it should be possible to test the transitions between user and supervisor modes, and the ability to page out the ROM. In both cases the system is going to try to run code from RAM, and there is no RAM, but at least we should see that happening.
Here's an oscilloscope trace showing the transition from supervisor mode to user mode:
Attachment:
20231213_111901.jpg [ 4.15 MiB | Viewed 6182 times ]
The bottom, yellow trace is a VIA pin that's changing state during the CPU operation that triggers user mode (writing to VIA_PORTA, with pulse output on CA2 enabled). The blue trace is the CPU's SYNC pin, showing when instructions start and how long the clock cycle is. The red trace is CA2, the asynchronous reset input to the first D flipflop, which holds the pending mode change until the end of the next SYNC. The green trace on the top is the second D flipflop that outputs the user/supervisor state, and this is the signal that changes all the address decoding.
It's interesting to note that the handshake pulse from the VIA is maintained over the falling edge of SYNC. This means that I could probably have not used the first D flipflop here - I could probably have just used an AND gate to make the second D flipflop blend its current state with the CA2 state, so that it would stay high only if CA2 was not low, and after going low, it would stay low until forced high again.
In the traces above we can see that the system is going into user mode, and then executing a series of 6-cycle instructions. I am not sure what these were exactly, nothing was driving the bus but evidently it floated to, or persisted at, a level that corresponded with a stable state. To check this better, I added a pull-down resistor network to the data bus, so that it would give a BRK opcode if no device was driving it, and this made it easier to confirm things were working as they should:
Attachment:
20231213_113259.jpg [ 4.33 MiB | Viewed 6182 times ]
I moved the red probe (second from top) to the CPU's VPB pin this time. Now we can see the user mode state starting at the falling edge of the SYNC for the RTI instruction, and then another instruction starting which will be a BRK due to the pull-down resistor, and then VPB goes low for two cycles, forcing the system back into supervisor mode. After that a couple of instructions execute - this is my IRQ handler which just loads X with a specific value, writes it to port A, and issues a STP instruction. The write to port A brought the yellow line low again and STP meant no more sync cycles occured after that point.
So clearly the address decoding has responded to the transition to user mode, and the ROM is no longer mapped; but also, on VPB, we do return correctly to supervisor mode in time for the IRQ vector to be read from the ROM.
Finally I wanted to test the ROMDIS signal, which is used to disable the ROM and have high-bit-set operations read from the pagetable/private supervisor RAM instead. Again that RAM is not present, but my pull-down resistor network will cause BRKs in its place:
Attachment:
20231213_114603.jpg [ 4.35 MiB | Viewed 6182 times ]
The yellow line at the bottom is now the ROMDIS signal, which is just an output pin on the VIA - when high, ROM is disabled. It's transitioning right at the end of an instruction, in time for the instruction fetch during the next SYNC to occur from pagetable RAM instead. We get a BRK, and see VPB (red trace, second from top) go low as expected a few cycles later. The system stays in supervisor mode (green trace, top) as intended.
So I think everything in this part of the circuit is working well. This evening if I get time I will wire up the RAM and the transceivers, and hopefully be able to bootstrap a minimal kernel. The strategy for that is probably to have the boot ROM run some system tests and then stream the kernel code from the ACIA into the private RAM, then disable the ROM.