Saturday, August 11, 2012

Fun with Old Computers






One of the benefits of getting older is that you can afford the state of the art tech of your childhood.  In my case, I suddenly got the desire to tinker with the 8-bit goodness that is the Tandy Color Computer line that was sold in Radio Shack in the 80's.  I had a Color Computer 2 with standard BASIC.  It had 16k of memory, and used a cassette interface for storage.  It was the first computer I ever owned, and the second one I learned to program.  The first machine I ever "programmed" was an Apple IIe clone in my elementary school.  Of course, programming at the time typically meant copying BASIC programs from magazines like Family Computing.

This go around, I was able to pick up at 128k Color Computer 3 (as well as an original and a coco 2 for good measure!) off Ebay.  This model has composite video output as well as analog RGB out (think CGA!).  Unfortunately, old analog RGB monitors are surprisingly expensive, and so it's composite for now.  I have a Cunning Plan however.  There seem to be some inexpensive conversion devices out there (like this), and if I get sufficiently interested I might pick one up.

My first goal was simply to play a few old games.  In particular, I wanted to play my favorite game at the time, Dungeons of Daggorath.  Unfortunately, my original cartridge was long gone.  While many of these games are still under copyright, you can often find ROM images around the net.  Fortunately for me, this particular game has a very generous developer (Douglas Morgan) who has licensed the game to anyone who wants it.  He even offers the source code!

So now I have the ROM image, and my next challenge is how to get the data into the computer.  Fortunately, I have some raw materials to work with - an old 8k EEPROM, and an ancient cartridge for Personal Finance.  I carefully removed the old ROM from the cartridge, and soldered a socket connecter in the same place.  Unfortunately, the ROMs used at the time were a 24-pin 2364 variant, and the "modern" device I had was a 28-pin 27C64 with a completely different pinout.  With a little searching, I was able to find a pin mapping, and I was able to build an ugly but serviceable wiring harness.

The next step was how to program the EEPROM.  I had an Arduino UNO handy; problem was, it didn't have enough pins to directly address all of the ROM pins.  A little discrete logic to build a 18 bit shift register and I was in business.  I decided to directly wire the data lines so that I could read the chip (8 pins), used clock and serial out (2 more pins) and still needed 3 more output pins for CE, OE, and WE.  I ended up putting OE and WE pins on the shift register, and leaving CE direct.  One output pin left over!
 
I wrote a simple Arduino sketch to initialize and to read bytes from the serial line and follow the relevant chip timing to push the bits out.  I them wrote a simple Processing sketch to download a binary ROM image to the UNO.  The Arduino sketch is below if you're interested in doing something similar, though it's pretty simple code.










 After that, it was just a matter of popping in the chip, plugging in the "cartridge," and turning it on:


















The Arduino sketch:

const int addrOut = 11;
const int clock = 12;
const int chip_enable = 13;
const int ready = 0; // ANALOG
int value;

void setup() {
  for (int i=2; i <= 13; i++) {
    pinMode(i, OUTPUT);
  }
 
  randomSeed(millis());
 
  digitalWrite(clock, LOW);
  digitalWrite(chip_enable, HIGH); // invert logic
 
  Serial.begin(9600);
}

unsigned int loadAddr(unsigned int addr, boolean WE, boolean OE) {
  unsigned int d = addr;
 
  d &= 0x1fff;
  if (WE)
    d |= 0x8000;
  if (OE)
    d |= 0x4000;
 
  shiftOut(addrOut, clock, MSBFIRST, (d & 0xff00) >> 8);
  shiftOut(addrOut, clock, MSBFIRST, (d & 0x00ff));
}

void readEeprom() {
  unsigned int addr = 0;
  byte value;
  int pcount;
 
  for (int i=2; i < 10; i++) {
    pinMode(i, INPUT);
  }
 
  pcount = 0;
  while (addr <= 0x1fff) {
    value = 0;
    if (pcount == 0) {
      Serial.print("\nAddress: ");
      Serial.print(addr, 16);
      Serial.print(" : ");
    }
    loadAddr(addr, true, false);
    digitalWrite(chip_enable, LOW);
    for (int i=0; i < 8; i++) {
      value |= (digitalRead(i+2) << i);
    }
    
    Serial.print(value, 16);
    addr++;
    pcount = (pcount+1) % 16;
    digitalWrite(chip_enable, HIGH);
  }
}
void chipClear() {
  loadAddr(0, true, true);
  digitalWrite(chip_enable, LOW);
  loadAddr(0, false, true);
  delay(10);
  loadAddr(0, true, true);
  digitalWrite(chip_enable, HIGH);
}

void writeByte(unsigned int addr, byte b) {
  for (int i=2; i < 10; i++) {
    pinMode(i, OUTPUT);
  }
  loadAddr(addr, false, true);
  digitalWrite(chip_enable, LOW);
  for (int i=0; i < 8; i++) {
    if (b & (1 << i))
      digitalWrite(i+2, HIGH);
    else
      digitalWrite(i+2, LOW);
  }
  digitalWrite(chip_enable, HIGH);
  loadAddr(addr, true, true);
}

void loop() {
  while ((value = Serial.read()) == -1);
  if (value == 'w') {
    for (int i=0; i < 8*1024; i++) {
      while (Serial.available() == 0);
      value = Serial.read();
      writeByte(i, value);
      Serial.write(value);
    }
  } else {
    Serial.println("got something else!");
  }
}


No comments:

Post a Comment