Close

Entry 9: Adding a Peripheral

A project log for Aiie! - an embedded Apple //e emulator

A Teensy 4.1 running as an Apple //e

jorj-bauerJorj Bauer 02/20/2017 at 19:491 Comment

This whole thing was a matter of "I can build that with what I've got". And I've got a *lot* of stuff, which explains why there's a currently unused nRF24L01 soldered in there. What else do I have lying around that could become part of this project? Let's see... hey! Hows'bout this guy?

A number of years ago, I bought this thermal printer (probably from SparkFun) as one of those "that's interesting and I don't know what to use it for now, but it'll probably disappear off the market in 12 seconds and I'll regret not having bought one" kinds of purchases. My son had a friend over one weekend, and I briefly used it as part of a scavenger hunt: they found an RFID card, that they had to swipe across the timey-wimey box that my then 9-ish-year-old son used as a time machine. It had an Arduino Uno in it with an RFID reader; he had a key card that he needed to "start" the time machine, before he played with the buttons and dials and made noise and lights with analog dials that went up and down. When they found the clue that gave them a blank RFID card, he knew exactly what to do with it - he swiped it on the timey-wimey machine, which had a mysterious black box connected to it, and BBZZZZZT out came receipt tape with their next clue printed on it!

And since that magnificent performance, this guy has sat in a drawer. With the timey-wimey machine that he stopped playing with. Not coincidentally, they're both in the "partial projects" drawer that I occasionally go to in order to scavenge parts.

Which brings me to the actual story. Can I glom this guy on to my Apple //e? Back in the day, I had a printer, of course; the Epson FX-80. (Apparently you can still buy ribbons for them on Amazon. Holy. Crap.) What if I made this a virtual FX-80, to go with my Virtual Apple?

Thinking of the physical hardware: first you'd need to add a printer card. That's easy enough - add another object that inherits from the Slot class, which requires a few methods:

  virtual void Reset();

Boringly called when we want to re-initialize the card to its boot-up state. We can deal with that later.

  virtual void loadROM(uint8_t *toWhere);

Used to load its ROM image in to the MMU's memory on boot or reset. So what ROM do we load? I *didn't* grab the ROM from my printer card. (I don't even remember for certain what it was. I think it was probably a Grappler+?) Which leads me on the hunt for a printer rom. Fortunately for us, the Apple 2 Documentation Project exists! Buried in there are ROM images for Apple's original (1977) Parallel Card. I think it's the one named "Parallel Mode". The ROM image is all of 256 bytes.

Let that sink in a minute: Woz managed to pack the parallel port interface that became the base standard for printing on the Apple II in 256 bytes. I double dog dare you to write anything actually useful in 256 bytes today. Even the Hackaday challenge was for 1k!

Back to the task at hand:

  virtual uint8_t readSwitches(uint8_t s);
  virtual void writeSwitches(uint8_t s, uint8_t v);

These handle the reads and writes to the mapped memory registers ("switches") that this I/O device uses. For a first pass, I'm going to ignore reading; any read will just return 0xFF. And for writing, it looks like the parallel card sends commands to us at memory address 0xC100 (slot 1, switch 00).

Right! We have a ROM. We have a ParallelCard in a Slot. Time for a virtual printer - fx80.cpp. This is fairly complicated, and does a lot of "take that input, switch in to or out of command modes based on the input, and do the right thing with it". And the easiest thing to have it do is print graphics.

Why is that easier than text? Simple: I want the text to look like the FX-80 text. And for that, I'm gonna need to hand-code their font, one dot at a time. Which is entirely possible, thankfully, because they printed the whole damn thing in a manual. (Aside: Epson, themselves, have the manuals online! What an amazing thing in this day and age.)

Anyway: some manual bitmap coding later, and we have at least partial results in fx80-font.h. (As of this writing: all the upper-case letters are done, along with the numbers and some symbols. Most character definitions are still blank though.) It's enough for testing. Given the way the graphics printing works on the FX80 - where you dump 8-bit data for each column of pins, across all 960 pixels for the page - it makes sense to buffer one line of pixels and then figure out how to print them. Hence rowOfBits[ (FX80_MAXWIDTH/8)*9] -- because I lied, and it's actually 9 dots tall instead of 8 -- and printing a letter is a matter of transferring pixels from fx80-font.h to the right position in that row of bits. Okay, good enough for now.

Next up - how do I test any of this? I'm going to want an OpenCV equivalent of the printer, before I start dumping garbage to my very (in)expensive thermal printer paper. Easy enough - whip up another openCV window in opencv-printer.cpp, which has an interface (PhysicalPrinter) that expects addLine(pixels); moveDownPixels(count); and update().

Wire it all together, fire up PrintShop, and *bam*:

Holy shit, it works.

All I need now is a glass of wine, some time to translate those codes to the thermal printer, and ...

Happy Birthday, emulated FX-80!

Discussions

juddy wrote 02/21/2017 at 02:56 point

Talk about inspiring!

  Are you sure? yes | no