Retrochallenge WW Wrapup

Well, the project is done, I’ve put down my soldering iron, I’ve stopped writing code, and I’m in the middle of editing a demo video (to be posted later, when I’m a little less tired).

I’m mostly happy with how things turned out this time around. I was successful with my project, I’ve met my goal of having usable tape storage. I should probably put the word usable in quotes, though, like this: “usable”. Because, to be honest, it is somewhat flakey. I have about a 70% success rate with saving programs that can be read back later. I think my main problem is just how much noise my circuit injects into the signal. I really did not use best engineering practices: I have no ground plane, my cables are unshielded, my leads are too long, I’m just begging for noise. So, really, I’d say 70% success is not so bad, all things considered.

I learned a lot, especially about how the Apple II did things. I pored through Woz’s code and really tried to understand what it was doing. I enjoyed the process.

But I confess I don’t know how much I’m actually going to use what I’ve built. The unreliability is a problem (as it was for the Apple I, incidentally. At least I’m not alone!). I think my next project will involve writing 6502 SPI code so I can use an SD card for mass storage. It should be perfectly reliable, and provide essentially limitless storage.

Continue reading Retrochallenge WW Wrapup

PRESS PLAY ON TAPE

Good news! I tracked down my checksum bug last night, and just in the nick of time, everything is working. I can both LOAD and SAVE from EhBASIC, and my little SBC is happy. Finally, a Retrochallenge that I’ve completed successfully!

For the technically minded who are interested in the gory details, I’ll explain exactly what was going wrong. BASIC programs are stored in tape files that contain two records.

BASIC tape record

Each record starts with a 770Hz tone as a header, followed by a short sync bit, then the record data, and finally a checksum. The checksum is just a running-XOR of the data.

The first record is just two bytes, it specifies the length of the data in the second record (little endian, which is, as we all know, the only true endianness).

The second record is the BASIC program data. The bug I was seeing on Sunday was that the checksum failed, but only on the DATA record, never on the LENGTH record. I could not for the life of me figure out what was happening. I went so far as to capture the tape data on my oscilloscope and walk over it bit by bit, by hand, and calculate a checksum. The checksum on the tape was correct, it should have read correctly. And of course the checksum of the LENGTH record was matching correctly.

Then I had a breakthrough, one of those things that should have been painfully obvious but was not. I discovered it by walking through the data that was loaded into memory off the tape. EhBASIC puts the program at $0301, so I dumped page 3 to see what was in it. It all looked correct, until I noticed that the checksum itself was being loaded as data. I had a simple, very stupid off-by-one error in my arithmetic, so I was slurping in the checksum as part of the program data by mistake. It was then reading garbage (random tape hiss noise) from just after the record and treating that as the checksum, which of course never matched. Aha!

I fixed my arithmetic, burned a new EPROM, and now, at last, I can SAVE and LOAD just like it’s 1977 all over again.

Tomorrow I’ll post a quick video, and a final Retrochallenge wrap-up.

Still Flogging Away

OK, it turns out this is harder than I expected. I’m fighting a very strange bug in my LOAD code. It’s complaining of a checksum mismatch, but there doesn’t actually seem to be one. And, weirder, my program loads anyway, despite the fact that the checksum failure branches away from the code that is supposed to reset the BASIC program and variable space.

In short, it’s failing where it’s supposed to succeed, and it’s succeeding where it’s supposed to fail. Wait, what?!

Whatever the bug is, I still intend to squash it before the Retrochallenge deadline on Thursday. Plenty of time! (he said, over-confidently)

Down to the Wire

Golly, I do like waiting until the last minute, don’t I?

No, I haven’t given up. I’m just finishing up the last few bits. I meant to get everything done yesterday, but got distracted by Real Life Matters. Oh well! But I have plenty of time today.

The very last thing I need to do is patch together my READ and WRITE routines into LOAD and SAVE BASIC commands. Since my little computer uses Enhanced 6502 BASIC, this will not be difficult. EhBASIC provides empty hooks for your own LOAD and SAVE code, so it’s very easy to patch into.

By this time tomorrow, I hope to have a finished project ready to demo.

Getting Closer

Good progress tonight! I think the code to WRITE data to tape is fully complete. I’m not particularly impressed with my own 6502 assembly, but by golly it works.

Note that there is no checksum yet. I’m calling this “Optional” for now. I may regret that later.

The next step is the opposite process: READING THE DATA! I’ll have more on that when I’m a little less tired.

Retrochallenge: The First Piece of Code

Alright, it’s not much to look at, but it’s a start.

This code generates ten seconds of 770Hz square wave, which is the tape header. It is both less compact and less elegant than the code that Woz wrote for the Apple 1 and Apple II, but for that I blame my inexperience (and the fact that Woz was a kind of 6502 savant who had no need for such niceties as assemblers). I hope that it is at least fairly easy to read and understand.

Next up is writing a sync bit, followed by shifting in data from memory and writing it to tape one bit at a time.

How It Works

I’ve been pretty quiet over the past week. This is primarily because my day job has kept me busier than I’d like, compounded by the fact that I’ve come down with a cold. But I shall persevere and forge on ahead. The hardware is done, and I’m currently working on the software. But, how exactly does it work? That’s what I hope to show in this post.

Recording data on a cassette tape is actually very simple, at least the way the Apple II does it. The output signal from the computer to the cassette recorder is just a square wave, varying between TTL low and high logic values (about 0V and +5V). The Apple II drives the signal from a 74LS74, and the SYM-I drives it from a 74LS145. Just about anything could drive it, the input impedance to the cassette is about 4kΩ, it requires very little current.

The width of the cycles in the square wave determine the values being stored. To write a “0”, one full cycle of a 2,000 Hz signal is recorded (low for 250 µs, then high for 250 µs). To write a “1”, one full cycle of a 1,000 Hz signal is recorded (low for 500 µs, high for 500 µs). Here’s a diagram to show what I mean.

Apple II Cassette IO

Of course there’s more than just bits on the tape. A full file on tape starts with a header (10 seconds of a 770 Hz square wave), a “Sync” bit indicating the start of data (a single 2500 Hz cycle, 400 µs wide), then the data itself. The last byte of the data is a checksum byte. See, not very complex!

So, now that we know what’s on the tape, how do we write code to read it and write it? Here’s the algorithm, in a slightly condensed form.

To write a single bit of data to the tape, we need to flip the output state from whatever it currently is, to the opposite (say, from high to low). Then, using a calibrated delay loop, wait until either 250 µs has passed if writing a “0”, or 500 µs if writing a “1”. We do this one more time, this time flipping from low back to high. At the end of the second trip through the delay loop, the bit has been fully written.

Cassette Output

Reading data that’s already been written is similar. The state of an input line coming in from the cassette is monitored in a polling loop (remember, it’s going through a comparator that turns the messy, degraded cassette signal into a nice clean square wave). Each time through the polling loop, a counter is incremented, and we keep looping until we see that the input has changed twice. When that happens, we compare our counter with a known value to see whether we just read a “0” or a “1”

Cassette Input

It’s not a difficult problem, but getting the delay loops just right is important. I hope I’ll have some more concrete results to show by next week. We’re closing in on the 15th, so I only have half a month to get everything working. But I’m not worried… yet!

Comparator Success

I put together the cassette input circuit tonight on a teeny-tiny little perfboard. It’s not really much to look at, but happily, it works!

cassette_input

Its sole purpose in life is to take the audio from a cassette and turning it into a useful digital signal. Here’s a capture from my scope, showing the cassette input on the yellow trace, and the digital output on the blue trace. The input is showing 200mV per division, so it’s a peak-to-peak of about 300mV. The output trace is showing 2V per division and it’s peaking at about 3.9V, but that’s because I forgot a pull-up resistor. I’ll add that tomorrow!

ones_and_zeroes_annotated

I’ve annotated the screenshot to show how the data is encoded on the tape. This is actually from a copy of APPLE 1 INTEGER BASIC (the original, the one that Woz wrote!) that I downloaded as a digital audio file from Brutal Deluxe Software and then recorded to cassette myself. The Apple II used the same format. A “0” is encoded as one cycle of a 2 kHz signal (about 500µs wide), and a “1” is encoded as one cycle of a 1 kHz signal (about 1000µs wide). This seems like a fine format to use for my own project, so that’s what I’ll do.

I have a few minor tweaks to do to the hardware before I mount it into the project box next to my 6502 SBC, but I should have all the hardware done tomorrow. Then it’s onto the hard part: the software.