Quelling A Weeks Long Headache With Creative 65C22 VIA Use
As soon as the SBDS was built, I started porting Modular Monitor over. This should have been a pretty easy task- Modular Monitor was specifically designed to be easy to port. I did everything you’re supposed to: encapsulating I/O, using agnostic calling conventions, clearly marking out magic constants. All I had to do was change some constants and write UART specific I/O routines. What could go wrong?
“What could go wrong” is a phrase normally said just before everything goes wrong.
Despite my best efforts, porting Modular Monitor to the SBDS proved to be unusually tricky. Part of the problem is that MM is just big. There’s a lot going on under there, with plenty of opportunities for strange bugs to pop up. I did what I could, but the best I could do was printing half a string to the terminal. No amount of testing, refactoring, swearing, crying, or begging could improve things.
I had to rebuild Modular Monitor from the ground up. One module at a time. At least I got that bit right. Along the way I found yet another embarrassing bug, redid some things that kept bothering me, and proved once again that I do tend to get the important things right the first time around. Just not all of them.
Part of the Modular Monitor Series:
- Modular Monitor: Serial Terminals, Hardware Bugs, and You
- Modular Monitor: a Flexible ROM Monitor for the 6502
- Modular Monitor: Dynamic Dispatch and Code Cleanup
- Modular Monitor: Interactive Debugging With The Non Maskable Interrupt
- Modular Monitor: Porting To The Single Board Development System
- Modular Monitor: Operating System Call Table
Modular Monitor SBDS Port
The good thing is that despite having to rewrite MM, I can still use most of the underlying routines with only small modifications. I still have to do lots of little tweaks to get everything in line. On the whole, I suppose I have to rewrite maybe half of the code. Better than a full refactor; worse than the expected workload.
Because I have to throw so much out anyways, I’m willing to completely redo some of the underlying conventions. I didn’t consider some of the downstream effects of certain design choices. The 6502 makes you work hard to get even basic stuff working. Experience has taught me better ways to make the 6502 do what I want it to do.
Much of this project is simply going to be retracing my steps. I won’t bother going over every little detail- read the previous articles for that.
Clean Up And Refactor
Throwing everything out is a good excuse to fix some deeply embedded problems with Modular Monitor that would otherwise require a huge rewrite. I have to rewrite everything anyways; might as well do it right the second time.
- BINTOHEX now returns the most significant character in A, least significant in X. This is because the 6502 does not support any of the good addressing modes for the index registers. If you wanted to buffer a string using BINTOHEX you had to backup A, swap in X, store A, restore A, and then store A again. Now you can simply store A, then swap X, then store A.
- While the jump table for commands is cool, I found it to be cumbersome in practice. Given how few commands I currently have, I chose to go back to a branch list. Easier to debug too, which is more important right now.
- A bunch of experimental commands were removed to reduce the code size. I’ll add them back in on a need-to-use basis.
- Input is currently polled, because I’m not sure how to handle 65C22 VIA interrupts just yet.
I have far more ambitious plans for Modular Monitor, but you have to walk before you run. Stripping things back gives an appreciation for the low-level infrastructure. When it goes down, everything built on top goes along.
UART Woes
Pretty much the entire reason this article exists is the TL16C550C UART. I thought it would be better than the UM245R I had been using. After all, the UART can connect to pretty much anything. A UM245R can only really connect to a PC.
At first, things were going well. I had largely rebuilt the basic serial terminal test which seemed to work. Then it didn’t. Then it did. Then I got all kinds of strange bugs where certain things just broke the UART- or sometimes fixed them. Clearly something’s wrong with the UART. Unfortunately the UART is where almost all of my I/O flows through. No UART, no debugging.
So the solution here is to fall all the way back to the UM245R. I still don’t like it, but what other choices do I really have? I’ve been chipping away at this problem for weeks without much to show. The UM245R is known to work. Patching it into the SBDS required using the 65C22 VIA, along with some dodgy wiring.
Jumpers and breadboards are the electrical engineer’s equivalent of duct tape and string.
It took a while to get the UM245R via VIA setup working, but once I got it going it kept going. My only complaint with this setup is that it’s incredibly fragile. Bumping the USB cable is enough to rip the UM245R off the breadboard. I can’t fix it if it breaks again. At least this is only a temporary setup.
Here’s the interface code, in all it’s beautiful ugliness:
;Temporary UM245R driver using 65C22 VIA as bridge
;Data through PORTA
;Read/write flags on PORTB bits 7 & 6
;CA2 is read, CB2 is write
USB_INIT:
STZ VIA::DDRB ;PORTB is always input
LDA #%11001110 ;Set up the initial R/W state
STA VIA::PCR ;Neither control line should be active yet
RTS
USB_RD:
BIT VIA::PORTB
BMI @EXIT ;Check flag first
STZ VIA::DDRA ;PORTA = all inputs
LDA VIA::PCR ;Enable the read line
EOR #%00000010 ;Toggle
STA VIA::PCR ;Read is now active
LDX VIA::PORTA ;Get data
EOR #%00000010 ;Toggle
STA VIA::PCR ;Read is now inactive
TXA ;Return in A
SEC ;Success!
RTS
@EXIT:
CLC ;No success
RTS
USB_WR:
BIT VIA::PORTB ;Check flag first
BVS @EXIT
STA VIA::PORTA ;Prepare byte to send
LDA #$FF
STA VIA::DDRA ;Port A is now all outputs
LDA VIA::PCR
EOR #%00100000 ;Toggle
STA VIA::PCR ;Write is now active
EOR #%00100000 ;Toggle
STA VIA::PCR ;Write is now inactive
SEC ;Success!
RTS
@EXIT:
CLC ;No success
RTS
I suspect there are more than a few ways to make this interface better. I have no interest in doing so, because there is no real use for this driver beyond this particular test. It works, which is the only thing I care about.
As a blueprint for other devices hooked to the 65C22 VIA, it’s more valuable. I have a couple of devices that need to hook into the VIA; they will likely use this driver as their base. Interrupts weren’t used for this particular test, but it shouldn’t be too hard to add them in.
POKE-ing Around
Something I have really come to appreciate, is that even a basic monitor is a vital debugging tool. You need the ability to read/write arbitrary locations with no restrictions. My logic analyzer and oscilloscope proved less than adequate. Useful under the right circumstances, yes, but not sufficient on their own.
My first order of business was to check if the UART was actually working properly. After all, plenty of hard work failed to get it doing things. In order to do this, I had to first make an unusual setup where I have two terminal windows open at the same time.
Thankfully this Just Worked the first time. It does require two USB-to-serial adapters though.
On the left is the UART connection in half-duplex. Red chars are sent, but not echoed back through the UART. On the right is the UM245R in full duplex mode. Most of the traffic is going through the UM245R right now.
As it turned out, I had to fix up some problems before even getting this far. My double terminal setup ended up being less useful for debugging than I expected.
The big bug I discovered was entirely my own fault. I found it not by ruthlessly plugging away at the new terminal link, but by doing a random verification on my ROM programmer. Long story short, I was declaring certain important values as ROM constants rather than assembler labels. Once I got that out of the way everything started working as expected.
Experimentation with a partially ported MM proved the UART was working as intended. All the many tests I concocted proved that. While that means I did get it right the first time, it also means I spent a couple of months failing to uncover a pretty mild bug. I was expecting some kind of hardware failure. But no, it was a simple typo.
Two syntax errors led to weeks of ineffectual debugging. It’s the tiniest things that have the biggest impact, eh?
Finishing Up
Software is hard. There’s no two ways about it. Without a functional monitor, you can’t really do much. I didn’t want to go to the effort of setting up a completely different channel to talk to the SBDS. Just look at that setup; a single misplaced elbow would completely destroy it. But, in the end I persevered. Once I had something to compare the UART to, I was able to single out what was broken. Sometimes it really is helpful to have a second opinion.
Besides being an opportunity to gripe about a bug that had me going round for weeks, this project also proves something I’ve been working towards. One thing I want Modular Monitor to do, is to redirect it’s I/O through the various new hardware on the SBDS. I/O encapsulation meant I only had to swap out a couple of subroutines to send through the VIA, even thought the UM245R is nothing like the UART. I already have a remarkably simple plan to do this at runtime.
Plugging the UM245R into the VIA is also a very convenient way to get to grips with VIA idiosyncrasies. The 65xx series is full of unpleasant restrictions you just have to learn to work around/with. Since you don’t typically use the VIA as an output unto itself, this was also a valuable experience in using the VIA as a bridge to other hardware.
I only got the basic READ and WRITE functions working, warts and all. Over the next month or so I’ll slowly be adding more features back. After I get back to the pre-SBDS functionality, I’ll probably stop development there. Modular Monitor is only supposed to be a stepping stone, and I have my eyes on a proper operating system.
The end of 2023 has been quite dramatic. My laptop suffered a hard drive failure that took almost a month to sort out. It failed just before I almost finished this article. Nothing big got lost, but while it was out I couldn’t do much of anything. You don’t realize just how incredibly boring things can be without projects to work on.
I have maybe a dozen 6502-centric projects I really want to get going. I want to get at least one out before year’s end. A working port of MM means I can finally start moving forward again.
Have a question? Comment? Insight? Post below!