COLOR 64 AT 9600, THE SECOND LOOK

Last time we said the Color 64 7.40 mod, the 9600 baud edition of Color 64 7.37, was coming soon. It still is. But a serial driver is the kind of code that works fine in a demo and then fails at three in the morning, halfway through somebody's download. Before we hand it to other sysops we wanted a second look. So we re-read the driver and the BASIC patches as if someone else had written them, listed everything that looked wrong, fixed it, and then ran every test we have, all night, on a plain 64 and on a SuperCPU, in PAL and NTSC, and finally on the exact machine our own board runs on.
This post is the list. It's for the sysops and the technically curious; if you just want the board, nothing below changes what you'll see when you call.
What the review found
The fallback to 2400 was dead. 7.40 is supposed to use the original KERNAL routines if it doesn't find a UP9600 interface at boot, so a board without the interface keeps running at 2400 like it always did. Two things broke that. The driver's interrupt handler took every NMI whether the interface was there or not, so the KERNAL's own serial interrupts never arrived. And the BASIC line that starts the driver ended in IF PEEK(679) THEN ..., and in Commodore BASIC a false IF skips the whole rest of the line, including the POKE after it. The handler now passes the interrupt straight to the KERNAL when the interface isn't present, and the BASIC lines don't depend on an IF. In VICE, the patched BBS now behaves exactly like the original without the interface. (VICE's emulation of the 64's bit-banged RS-232 garbles the first byte at 2400 for the original too, so a real 2400 modem is the final test of that path.)
The "receiving" flag was never set. Before the KERNAL talks to a disk drive it waits for the modem to go quiet, by watching two busy bits: one for sending, one for receiving. The driver set the sending bit and not the receiving one. So a byte still being clocked into the shift register could collide with the start of a disk access, and on the 64's serial bus that's how you corrupt a file. The driver now sets the receive bit from the start bit until the byte is in.
A stale start bit could restart the clock. The chip that receives data raises a flag when a start bit arrives. If that flag was still set from an earlier byte when the driver was enabled, the receive clock started with nothing on the line, and the first byte in was garbage. The flag is now cleared before the interrupt is armed.
The clock had to stop in time. When a byte has been shifted in, the driver has to stop the receive clock before it starts shifting in the next bit, and it has about one bit time to do it, around 100 cycles at 9600 baud. The VIC chip steals the bus for about 40 cycles every eighth scan line while the screen is on, so the budget is tighter than it looks. The handler now stops the clock 29 cycles after it is entered, before it even saves the registers, and starts it 57 cycles after a start bit, so a bad line can't push either one over.
The clock pin had to stay an output. The receive clock comes out of a user port pin that the driver drives. The KERNAL's own RS-232 code can reset that pin's direction. The driver now sets it when it is enabled and re-asserts it sixty times a second.
A caller could hang themselves up. 7.40 takes carrier from the modem's CONNECT and NO CARRIER messages. The driver looked for those words anywhere in the incoming stream, so a caller typing "no carrier" in a message would have been logged off. A modem puts its result codes at the start of a line, so now that is the only place the driver looks.
Smaller things. The receive buffer index wasn't cleared at start-up; a control register write was clobbering the CIA's time-of-day bit; the send timer wasn't force-loaded after its value changed; the boot banner didn't match the other screens. All fixed.
What the tests found
The review fixes were the easy part. The night of testing found two more, and both were worse.
The clock stood still during disk access. A BBS lives by its clock: time limits, time stamps, the hang-up timer, "time left" on every menu. The 64's clock ticks on a CIA timer interrupt, and 7.40 had moved that tick to CIA 1's timer B because timer A had become the send clock. What we'd missed is that the KERNAL's disk routines borrow timer B for every single byte they move. While a drive was busy the clock simply didn't run. We measured it: in 2.3 seconds of disk reads, the clock counted 8 sixtieths of a second instead of 138.
The fix was to stop using CIA timers for the clock at all. The VIC chip can raise an interrupt at a chosen scan line, once per frame, and nothing else in the 64 borrows it. The driver now ticks the clock from the raster interrupt at line 250. A frame isn't quite a sixtieth of a second, so it adds a tick every 344 frames on an NTSC machine, which comes out exact, and every 5 frames on PAL, which runs a quarter of a percent fast; we'll trim that. The idle rate now matches the stock clock exactly, and it keeps counting through disk access.
An interrupt storm. This one only showed up in the full-duplex test on a plain 64 at 1 MHz: sending a thousand characters while two hundred arrive. The machine stopped mid-test and came back at the READY prompt as if someone had hit RESTORE. The VICE monitor showed the stack overflowed and a timer B interrupt firing again and again.
Here's what happened. When a byte finishes arriving, the handler stops timer B and re-arms the start-bit interrupt. It did that with a mask write that also enabled timer B's own interrupt. If the next byte's start bit arrived in the few cycles in between, a nested interrupt restarted timer B, and from then on timer B raised an interrupt every bit, forever. The original driver had the same race, with a smaller window; our reordering of the handler (to stop the clock earlier, see above) widened it enough to hit. The handler now re-arms only the start-bit interrupt, immediately after stopping the timer, and never touches timer B's interrupt bit.
Two small improvements went in while we were there. If the 256-byte receive buffer fills and a byte is lost, the driver now sets the overrun bit in the KERNAL's RS-232 status, the way the KERNAL does, so a transfer program can see it. And the bit reversal that UP9600 needs (the CIA shifts bits in the opposite order from RS-232) now uses a lookup table: 45 cycles instead of about 100, which is time taken straight out of the interrupt handler's budget.
Carrier detect: we tried the wire
Color 64 was written for modems that drive the carrier detect line. 7.40 uses result codes instead, so it works with WiFi modems whether or not their DCD line is wired. We tried switching the board back to the hardware line under VICE's network modem. The BBS never saw carrier. Result codes stay the default; we'll try the wire again on a real modem.
What was tested
Every test that follows passes.
- The driver suite, on a plain 64 and on a SuperCPU at 20 MHz, in both PAL and NTSC: echo through both output paths, every byte value, long blasts out and in, back-to-back bursts, full duplex, a thousand repeats of one character, and the clock holding 60 ticks a second while sending and after disk access.
- A scripted call to the running BBS from a pretend Hayes modem, on both machines: waiting screen, CONNECT, login prompt, and the BBS recycling after NO CARRIER.
- A real call through our modem bridge to the production machine: VICE with CMD's SuperCPU ROM, a RAMLink holding the programs and a CMD HD holding the data, booted the way our board boots. It answers, shows the login screen in three seconds, hangs up ten seconds after the caller does, drops DTR, raises it again, and answers a second call.
Our board is running the new driver now.
What's left
A real UP9600 cable on real silicon. VICE hands received bytes straight to the shift register and doesn't model the clock pulses on the user port, so the receive timing is proven on paper and in the emulator, not yet in a chip. Then Xmodem and Punter at 9600 end to end, the PAL trim, and the release package: the patched programs, the driver, and the install steps.
The mod is still coming. We'd rather it came right.
Color 64 is copyright Greg Pfountz. UP9600 is Daniel Dallmann's design. The 7.40 mod is from Comm-Adore, comm-adore.com.