Demon Debugger – alternative to a conventional ICE

December 1, 2020

This system is more of a concept than a single device, but that just means it’s very extensible. The concept is this: in order to explore, reverse engineer, or repair a system you can run a very small bit of software on the target system’s CPU and interface it to a computer to command it to read and write various IO lines or chunks of memory. How you go about doing this almost doesn’t matter, and we have put together a variety of different ways that may work for various platforms.

the most basic architecture

The project consists of three parts, the host PC software (interfacing to an arduino’s usb serial port for communication), the arduino as an i2c-to-serial converter, and the target system running a tiny kernel and wired to the arduino. Each of these is optional and can be replaced by other stuff. Let’s examine the simplest implementation: Sega’s Star Trek arcade machine, a Z80. For this implementation we use the demon.py software (either via demoncmd.py or demonapi.py, or your own script) to talk to the arduino, we use the arduino interface software to talk to the arcade board, and we use some already existing input and output pins determined by checking out the MAME driver and schematic for star trek.

; Moniker - Z80 Version
; by Frank Palazzolo
; For Sega Star Trek
;
; SCL  - OUT F9, bit7, (0x80) coin counter 1, pin 5, U11 - R1
; DOUT - OUT F9, bit6, (0x40) coin counter 2, pin 9, U11 - R3
; DIN  - IN  F8, bit3, (0x08) DIP, SW1, pin9, U2-pin 6
;
; Note: We cannot use opcode 0x32 on this platform, or it will trigger
;       the security chip

At the beginning of the z80 assembly code we can see the choices that were made. In the Z80 processor the code starts at 0x0000 so this program will be burned to a rom chip that is placed in the socket of the memory chip already mapped there. The Z80 also has a seperate IO space from memory space, it is accessed with different opcodes but generally behaves like memory reads and writes. We have decided to hook up to two pins of the coin counter for outputs and one pin of a dip switch as an input. It is important that we find 5v logic lines that are (for the outputs) latched to allow for this rather slow implementation of I2C. Speaking of I2C, why are we using three wires? well, getting bidirectional communication on arcade PCBs is a bit difficult, especially since the wires we’re hooking up to were never meant for communication. The 2n7000 FET basically multiplexes the signals together sort of how these level shifter boards do. One thing that’s also noted about this platform is a ‘gotcha’ put in by the manufacturer that means the security chip is triggered by a 0x32.

START:  DI                  ; Disable interrupts - we don't handle them
        LD      A,81h
        LD      HL,0xE000
        LD      (HL),A      ; blank the screen
        JP      INIT        ; go to initialization code

This is also a small amount of application specific code. For this platform we can want to disable the screen before moving on.

; Set the SCL pin high
; D is the global coin counter buffer
; Destroys A
SETSCL:
        LD      A,D
        OR      0x80
        LD      D,A
        OUT     (CCPORT),A
        CALL    I2CDELAY
        RET
    
; Set the SCL pin low
; D is the global coin counter buffer
; Destroys A
CLRSCL:
        LD      A,D
        AND     0x7F
        LD      D,A
        OUT     (CCPORT),A
        RET
; Set the DOUT pin low
; D is the global coin counter buffer
; Destroys A 
SETSDA:
        LD      A,D
        AND     0xBF
        LD      D,A
        OUT     (CCPORT),A
        CALL    I2CDELAY
        RET
; Set the DOUT pin high
; D is the global coin counter buffer
; Destroys A  
CLRSDA:
        LD      A,D
        OR      0x40
        LD      D,A
        OUT     (CCPORT),A
        CALL    I2CDELAY
        RET
; Read the DIN pin 
; returns bit in carry flag    
READSDA:
        IN      A,(DSPORT)  ;0x08
        SRL     A           ;0x04
        SRL     A           ;0x02
        SRL     A           ;0x01
        SRL     A           ;carry flag
        RET

This is the remainder of the application specific code. Because the target system is the I2C master and I2C is a synchronous bus it doesn’t matter how slow or variable the speed commands are sent out, everything just works. That means if it takes another 10 cycles to read a bit than it does to write one then everything is still ok. It doesn’t even matter what the speed of the cpu is on the target system because the arduino is so much faster in this case. The functions above will execute reads and writes to the bits we defined at the top for our hookups. All the electrical necessities for shifting bits, decoding addresses, latching outputs, that’s all already done by the designers of this board, we’re just trusting that they work and using them. But what happens if they don’t work, or if there aren’t easily accessible pins?

I give you, the Bit90! This is a rare colecovision clone that has built in basic roms and a keyboard. In fixing and reverse engineering it Frank and I developed the next generation of demon debugger: the entirely memory mapped interface. For this we gutted a colecovision cartridge and installed a memory chip containing our code, and two digital logic chips sitting where there used to be more memory.

        .org    0x8000	    ; cartridge start
    		
    	.db	0xaa	    ; cartridge signature
    	.db	0x55
    	
    	.dw     0x0000
    	.dw     0x0000
    	.dw     0x0000
    	.dw     0x0000
    	.dw     START
    	JP      0x0008
    	JP      0x0010
    	JP      0x0018
    	JP      0x0020
    	JP      0x0028
    	JP      0x0030
    	JP      0x0038
    	JP      0x0066
    	
    	.ascii  "BY: EVAN&FRANK/DEMON DEBUGGER/2019"
    	
START:  DI                  ; Disable interrupts - we don't handle them
        JP      INIT        ; go to initialization code

In this code we had some new challenges. The colecovision uses a BIOS, meaning that unlike other cartridge based consoles it runs code off the main board even if there is no cart inserted. This also means we cannot place the code at 0x0000 if we just use the cart slot. The cart is mapped to 0x8000 as you can see above and requires a header to be recognized as valid.

; Set the SCL pin high
; D is the global buffer
; Destroys A
SETSCL:
        LD      A,D
        OR      0x01
        LD      D,A
        PUSH    HL
        LD      H,0xc0
        LD      L,A
        LD      A,(HL)
        POP     HL
        CALL    I2CDELAY
        RET
    
; Set the SCL pin low
; D is the global buffer
; Destroys A
CLRSCL:
        LD      A,D
        AND     0xfe
        LD      D,A
        PUSH    HL
        LD      H,0xc0
        LD      L,A
        LD      A,(HL)
        POP     HL
        CALL    I2CDELAY
        RET
; Set the DOUT pin low
; D is the global buffer
; Destroys A 
SETSDA:
        LD      A,D
        AND     0xfd
        LD      D,A
        PUSH    HL
        LD      H,0xc0
        LD      L,A
        LD      A,(HL)
        POP     HL
        CALL    I2CDELAY
        RET
; Set the DOUT pin high
; D is the global buffer
; Destroys A  
CLRSDA:
        LD      A,D
        OR      0x02
        LD      D,A
        PUSH    HL
        LD      H,0xc0
        LD      L,A
        LD      A,(HL)
        POP     HL
        CALL    I2CDELAY
        RET

The act of writing data out is interesting because we are using a rom socket so we can only trigger the chip select line in this socket by reading from the memory region at 0xc000. The output lines we have are the address bus so by reading from various sections of the 0xc000 range we can trigger those address lines to go high or low and latch out a one or a zero to the 74HC74 chip. This act of writing by performing reads is incredibly inefficient without a lot more decoding, but it you have unused rom sockets it is an easy way to get output pins from memory mapped rom sockets.

DATAIN	.equ	0xa000		; where to read data in from
; Read the DIN pin 
; returns bit in carry flag    
READSDA:
        LD      HL,DATAIN
        LD      A,(HL)      ;perform a read into bit0
        SRL     A           ;carry flag
        RET

Reading is a lot more straightforward. I used a 74ls244 to put 8 TTL input pins on the bus to be read any time you read from the entire 0xa000 range (that’s where this rom chip used to be mapped). For this application I think we use D0 but this method can just five you 8 pins of input if you don’t want to bother with any more address decoding. Some 74xx138 chips could be hooked to the address lines to give you the entire memory space as discrete I/O but that is way overkill for this project, all we need is one bit.

Another success! after learning about how the bank switching inside the Bit90 worked we were able to dump the BIOS and BASIC roms by triggering bank switches and reading from the same section of the Z80 memory map.

The last iteration I’m going to talk about in detail is a setup that fits entirely in a single 2716 socket. The decoding logic is a bit more complex than it needs to be because it’s set up for either a Z80 or a 6502 which need the code to start at different locations, but the setup didn’t end up that complex.

First, this is my somewhat complicated additional demon debugger hardware that is jumper selectable. I had some extra PCB space so I added the 2n7000 that muxes the single direction lines into actual I2c, but in order to keep compatible with existing demon debugger hardware I made it selectable. To the left of the dotted line is the suggested demon debugger hardware on the arduino side. To the right is my new hardware that is incorporated into the target board. The first thing I added was a pull up on the Din (or SDA) line. we had an issue once where the internal pullup failed and we were chasing a bug for quite a while before we found that out. External pull ups are cheap and easy so that could help a setup that uses a vanilla arduino or one with the FET on it. I also added a 2n7000 of my own. In the configuration I show it with two jumpers connecting the outer most pins on that header the rightmost FET is disabled, its gate is tied to ground so it is not having an effect on the Din line and the Dout line is wired to the gate of the FET on the arduino side. If you pull those two jumpers and instead fit a single jumper between the middle two pins you allow the device on the left side of the dotted line to just be a vanilla arduino with no additional hardware. You may also be able to use a buspirate or FTDI chip with no software on it as an I2C slave at that point, but the python code would have to be tweaked to accommodate that. If you were using a demon debugger bit of hardware on the left side you could also connect the top and bottom pins to ground the gate of the FET on the left in case it’s acting up for some reason.

Using OE instead of CE or both is because we are actually interfacing to a socket that used to have a 2364 installed, this should work for most setups though

This is the output enable logic for the devil board as we called it. Basically there’s a 2716 or equivalent socket on the target system and we have a 2732 that we use to maps into that space. I used a 2732 so we can have a jumper on the A11 line to get us two programs on one chip. The devil board sniffs four address lines, can inject data on D0, and man-in-the-middles the Output Enable line to redirect some of the memory calls away from the ROM and to our latch circuits. The 74LS74 and 74LS244 are the same as we had in the Coleco cart, but they are now mapped as follows:

; "2716" Memory Map
; X000-X3FF - ROM
; X400-X5FF - I/O (Special region)
; X600-X7FF - ROM
; SCL connected to A0
; DOUT connected to A1
; DIN connected to D0
; "Special Region"
; Read from X4X0 - clear SCL, clear DOUT
; Read from X4X1 -   set SCL, clear DOUT
; Read from X4X2 - clear SCL,   set DOUT
; Read from X4X3 -   set SCL,   set DOUT
; All reads return DIN as bit 0

The input and output all occur in the 0x400 to 0x5FF section of the 2716 as selected by the 74LS139. The ‘139 basically breaks the 0x800 space into chunks of 0x000-0x1FF, 0x200-0x3FF, 0x400-0x5FF, and 0x600-0x7FF. Three of those are routed back to the ROM, but one is stolen and enables our registers when the system thinks it’s accessing ROM. Since we’re setting SCL and Dout at once we have four different sections of memory we can read from, each sets the outputs in a different combination. Every output set also triggers an input on Din because unlike last time the input register is triggered at the same time as the output.

        .org    0x2000	    ; cartridge start
        
        .byte   0x55	    ; cartridge header
        .word   0x0218	    ; next menu item (first one)
        .word   TITLE	    ; title pointer
        .word   START	    ; start pointer
        
        ret		    ; rst8
        nop
        nop
        ret		    ; rst16
        nop
        nop
        
	ret		    ; rst24
	nop
        nop
        
	ret		    ; rst32
	nop
        nop
        
	ret		    ; rst40
	nop
        nop
        
	ret		    ; rst48
	nop
        nop
        
TITLE:	.ascii	"DEMON DEBUGGER"
	.byte	0x00

In this implementation we installed it in the cartridge of a Bally Astrocade console so we needed a cart header here as well, the astrocade not only has a BIOS like the Colecovision, but built in games (and our demon debugger shows up as a selectable ‘game’. Like the Coleco/Bit90 implementation we set both output bits at once so the functions have to grab what the other bit is supposed to be from RAM and make sure both bits are asserted correctly when the function to assert just one is called.

That’s all the hardware I helped develop, and honestly it’s at a pretty good stage. Some implementations like Gorf or the Astrocade require you to hit a hardware watchdog or the machine will keep resetting, and any new applications will have their own quirks. You can always use something like this PET RAM/ROM board to get some code right into the memory map of any 6502 based machine (still have to locate some I/O or add a version of our devil board to map some in). There’s also a special implementation for the Intellivision based on the LTO Flash cart and its built in USB serial port (so just need the PC side software there). All of the above are various options for the target system interface hardware and some replacements for the intermediate arduino I2C to serial converter. Now it’s time to talk a bit about the PC-side software.

So far I’ve been talking about the platform dependent changes that need to be made, but this is all the common stuff. In addition to those functions to set and read the I/O lines each kernel includes functions that can read and write memory, read and write ports, and start executing code form a given location. That all remains the same for a given architecture (so far the Z80 and 6502). The PC side software is written in python for windows, but should be able to be ported just about anywhere. There is a python API exposed for demon debugger so you can write python code to interface to the target board as well as do some other things. This has so far been used for automating data collection from a rigol scope on the network to improve the Gorf mame emulation colors. This stuff is located in the DEV branch on the github repo so be sure to go there to check out the latest on how to automate the reverse engineering of vintage hardware.

The last thing to mention is that there is an arduino emulator to check out your hardware. using a second arduino uno you can emulate a Z80 target for demon debugger and practice reading and writing memory and I/O before you hook up to a real system and attempt to debug that. If you’ve got a tiny bit of RAM, and a place to install your own code then nearly any system can be hooked up to a PC and the hardware on it can be investigated to see what’s hooked where, or to determine experimentally what is broken on the board.

Ascii Keyboard project

November 30, 2020

When we put together the ZRT-80 it was complete luck that there was an unassembled kit for an ascii keyboard to just use for that project. Plenty of people aren’t so lucky and have to resort to extremely expensive vintage offerings or converters that use pretty outdated keyboards as the base anyhow. I can’t exactly throw stones because I’m still using an older keyboard every day and the matrix I chose for this project is not really great quality, but at least it was free. Well, at least it was free to me. The archer 227-1020 keyboard is not exactly cheap these days if you want to do an exact clone of this project. EDIT: this is a Coleco Adam keyboard. In fact, to interface with my matrix you need a nearly unobtainable 2.5mm spacing flat flex socket that’s exactly 21 positions wide. Almost no one makes those connectors in that spacing any more and only one that I could find that wide.

Even if you don’t want to spend an unreasonable amount of money on a hard to use and poor feeling keyboard, this project can help with other options. We live in the golden age of custom keyboards! standardized switch shapes and footprints, custom printed and molded keycaps, diy frames and 3d printed cases, you could do everything that people do to make custom USB keyboards and replace the controller with mine here and you have an extremely high quality parallel keyboard for any old ’70s or ’80s system that needs it. If you’re doing your own matrix and can lay it out however you want then you might look at the 2376 encoder chip which (with minimal extra parts) can serve as a one-chip solution parallel keyboard.

Once I had obtained the connector for the key matrix I had to decide on a strategy for talking to the keyboard and the computer/terminal. I needed 21 pins for the matrix, 8 data bits, and a strobe line. I read somewhere that slower computers have a busy line so they can keep the keyboard from sending more data until the computer is ready so we can also add that. I’m up to 31 pins and I don’t even have any LED indicators or aux switch inputs/outputs. At that big of a pin count my microcontroller options are annoyingly expensive so we’re back to the old atmega328 and associated chips to reduce the pin count. Above you can see 2x 74hc138 chips wired together as a 4-to-16 matrix scanner, a 74hc595 as a SPI to parallel output chip for driving the bus to the computer, a 74hc597 as a SPI to parallel input for reading the matrix, and a 74hc86 xor gate for selectable inversions of the strobe and busy lines. I’m pretty proud of this setup but it needed a little rework after first contact with the matrix.

Here is my brilliant attempt to scan the key matrix. These ‘138 chips output a high logic level on all the outputs except the one that’s selected. These days chips tend to go into high-z mode on the lines that aren’t being used but in some applications driving outputs like this would reduce the need for pull up resistors and cut down on the part count of the design. I used one of the positive enable lines and one of the negative enable lines to add a fourth address line to these two chips, making it act like one big one. This meant that I just needed one extra pin on the arduino and I could double my scanning output. This worked, but if you look up at the matrix layout above we have a problem. When you press and hold one key while pressing another it is possible to interfere with the scanning and give the voltage somewhere else to escape. On this matrix the modifier keys, control and shift, are in the matrix, but not on any shared rows. This means they can be scanned by the matrix and if you do it right they will not interfere with any other keys. I did it wrong. Holding shift and A for example connects two of the outputs of the ‘138s together, inevitably connecting a high output to a low output whenever one of those rows is scanned. I then had to reverse course.

Here is my revised (cut down) strobe circuit, now only scanning the 8 columns. This also shows my use of XOR gates for selectable inversion of the strobe and busy lines. With this configuration you can run the keyboard with the busy line unconnected and the pull up will make it think that the connected computer is always ready for more data. If the computer needs inverse polarity of either or both of those lines just short those jumpers and you can invert the signals in hardware. If you want a really general purpose keyboard I guess you can break those out to toggle switches, but usually something like this would be paired with a specific computer or terminal and they can be set once (also possible to do in software, but I find it annoying to have to recompile some software every time I want to change a little setting like that).

Here is the new 597 chained input section. This can read up to 16 lines of the matrix all latched in at once and using hardware SPI too! The 595 adds 8 output pins and only costs one more pin on the arduino as they are the same bus. The chainable output on the 595 is left floating because we don’t need to drive another chip (although we could) and the 597s are tied together with theirs. This would have been a perfect time to use SIP resistor packages but I didn’t have any of the right size convenient. I can use the unused input lines to detect if there’s a problem with the hardware or the reading routine for the chips. The top 3 lines are always wired to 5v through their pull up resistors, the unused chainable input is wired to ground. That means if I read anything other than high logic signals on the lines that are supposedly unconnected I have an issue, and if I read three or more bytes and the additional bytes are anything other than low logic signals I also have an issue.

The hardware was the easy part, the software is where things are more interesting. Driving the ‘138 scanner is trivial, just put the address on the bus. Converting from a byte to individual lines though is nice and compact: We iterate over the entire array of output pins, writing the result of bit shifting and checking if the resulting number is even or odd, basically if the least significant bit is 0 or 1. The bit shifting just puts that bit in the position to drive the output of that modulus check.

  //set address line
  address += 1;
  if (address > 7)
    address = 0;
  for (int i = 0; i < sizeof(addr_pin) / sizeof(addr_pin[0]); ++i)
    digitalWrite(addr_pin[i], (address >> i) % 2);
  delay(1);

Then we have to read from the ‘597s. We take that data into a 2-byte wide variable and similarly check if the bit at a given position is a one or a zero. In this case a zero means the key is being detected as down. we then update a current working copy of all the key statuses (if they are pressed or released)

  SPI.beginTransaction(set597);
  digitalWrite (ss, LOW);
  digitalWrite (ss, HIGH); //Latch inputs
  //read entire row of keys
  temp = SPI.transfer(0);
  temp += (SPI.transfer(0) << 8);
  SPI.endTransaction();
  for (int i = 0; i < 16; i++) //iterate through each bit of the 16 lines we just read
  {
    if (!((temp >> i) % 2))
    {
      key_status[i][address] = 1;
    }
    else
    {
      key_status[i][address] = 0;
    }
  }

The next part is a bit hard to follow. We iterate through the whole matrix (8×16) checking the key statuses versus what they were last time we scanned them. If they move from a 0 to a 1 the key must have been just pressed, if it went from a 1 to a 0 it must have been released. On key down, if the key is shift or control and nothing else is complicating things we move into either control or shift mode. In these modes the keyboard keys are fully remapped to send different codes, all definable in tables. On key release if you are releasing the shift or control keys we cancel that mode unless some lock has been set. This keyboard has a lock key so I decided to make shift lock and control lock each work as if you are holding down those keys all the time. The lock status is indicated by the state of two LEDs that can be wired to the top of a future enclosure. If a key goes down and you are in either shift or control mode then we check to see if it’s the lock key and either set or un-set the lock mode. The last thing we do after checking each key is to update the other table of key statuses so we can detect the next time it goes up or down properly.

  for (int i = 0; i < 8; i++)
  {
    for (int j = 0; j < 16; j++)
    {
      if (prev_key_status[j][i] && !key_status[j][i]) //key went up
      {
        if (!controled && !shifted)
        {
          //do nothing
        }
        if (i == 0 && j == 13 && !shift_locked)//hardcoded shift
          shifted = 0; //only stop shifting if the lock isn't set
        if (i == 0 && j == 12 && !control_locked)//hardcoded control
          controled = 0; //only stop controling if the lock isn't set
      }
      if (!prev_key_status[j][i] && key_status[j][i]) //key went down
      {
        if (controled)
        {
          if (i == 0 && j == 14 && !control_locked)//hardcoded lock
          {
            control_locked = 1; //set control lock
            digitalWrite (ctrl_lock, control_locked);
            Serial.println("set control lock");
          }
          else if (i == 0 && j == 14 && control_locked)//hardcoded lock
          {
            control_locked = 0; //un-set control lock
            digitalWrite (ctrl_lock, control_locked);
            Serial.println("un-set control lock");
          }
          else
          {
            HandleKey(j, i, 0, 1);//controlled key
          }
        }
        else if (shifted)
        {
          if (i == 0 && j == 14 && !shift_locked)//hardcoded lock
          {
            shift_locked = 1; //set shift lock
            digitalWrite (shift_lock, shift_locked);
            Serial.println("set shift lock");
          }
          else if (i == 0 && j == 14 && shift_locked)//hardcoded lock
          {
            shift_locked = 0; //un-set shift lock
            digitalWrite (shift_lock, shift_locked);
            Serial.println("un-set shift lock");
          }
          else
          {
            HandleKey(j, i, 1, 0);//shifted key
          }
        }
        else if (!controled && !shifted)
        {
          // modifier keys handled state triggered now, not edge triggered
          HandleKey(j, i, 0, 0);//normal key
        }
      }
      prev_key_status[j][i] = key_status[j][i];
    }
  }

I still had some edge cases based on how the matrix was scanned so I had to add some checks outside of the key edge detection logic, I added some state dependent logic. I may be able to go entirely to state based logic but for now this works. Depending on what order I scan that far left column it is possible to detect two keys going down in the same scan window and act on them in whichever order I want, but that’s probably too far in the weeds for a matrix that scans so fast.

  //this handles edge cases where you unlock while not holding down the modifier key
  if (!key_status[13][0] && !shift_locked)
    shifted = 0;
  if (!key_status[12][0] && !control_locked)
    controled = 0;
  //this handles edge cases where you unlock while holding down the other modifier key
  if (key_status[12][0] && !key_status[13][0] && !shift_locked) //holding control and not holding shift and not shift locked
    controled = 1;
  if (key_status[13][0] && !key_status[12][0] && !control_locked) //holding shift and not holding control and not control locked
    shifted = 1;

The HandleKey function basically replaced my debug statements, taking all the data I used to print out and handling it sanely.

void HandleKey(int j, int i, boolean shift, boolean control)
{
  if (!shift && !control)
  {
    if (key_scancode[j][i] > 0x7f)
      HandleMacro(key_scancode[j][i]);
    else
      SendKey(key_scancode[j][i]);
  }
  if (shift)
  {
    if (shifted_scancode[j][i] > 0x7f)
      HandleMacro(shifted_scancode[j][i]);
    else
      SendKey(shifted_scancode[j][i]);
  }
  if (control)
  {
    if (controled_scancode[j][i] > 0x7f)
      HandleMacro(controled_scancode[j][i]);
    else
      SendKey(controled_scancode[j][i]);
  }
}

The next function is something I’m pretty happy with. Macros can be any character from 0x80 to 0xFF, I started counting backward from 0xFF because it’s easy to see all the undefined 0xFF key behavior in the scancode tables. This means that in my current implementation you cannot define extended ascii characters except through single character macro sequences which is a bit cumbersome to read but I don’t expect to come up often, if at all. I don’t actually know how various terminals and computers will respond to getting 8-bit extended ascii sequences over a parallel keyboard port, the 2376 controller chip won’t generate them so maybe they’re just passed on to the operating system or program? maybe they’re filtered out? maybe they just ignore the high bit entirely.

These macros can be useful for talking to a specific terminal and automating escape or control sequences. They can also be useful for talking through the terminal transparently and talking to the computer operating system or program to execute a specific function that could take multiple keys.

void HandleMacro(int macro)
{
  switch (macro) {
    case 0xff:
      Serial.println("do nothing");
      break;
    case 0xfe:
      Serial.println("up"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      //SendKey(0x31); //1 might be unneeded, defaults to 1
      SendKey(0x41); //A
      break;
    case 0xfd:
      Serial.println("down"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      //SendKey(0x31); //1 might be unneeded, defaults to 1
      SendKey(0x42); //B
      break;
    case 0xfc:
      Serial.println("left"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      //SendKey(0x31); //1 might be unneeded, defaults to 1
      SendKey(0x44); //D
      break;
    case 0xfb:
      Serial.println("right"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      //SendKey(0x31); //1 might be unneeded, defaults to 1
      SendKey(0x43); //C
      break;
    case 0xfa:
      Serial.println("wtf? no. stop."); //you are mucking with control and shift at the same time, stop it!
      break;
    case 0xf9:
      Serial.println("Erase in Display"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x32); //2
      SendKey(0x4a); //J
      break;
    case 0xf8:
      Serial.println("home"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x48); //H
      break;
    case 0xf7:
      Serial.println("F1"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x31); //1
      SendKey(0x50); //P
      break;
    case 0xf6:
      Serial.println("F2"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x31); //1
      SendKey(0x51); //Q
      break;
    case 0xf5:
      Serial.println("F3"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x31); //1
      SendKey(0x52); //R
      break;
    case 0xf4:
      Serial.println("F4"); //from wikipedia ANSI_escape_code
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x31); //1
      SendKey(0x53); //S
      break;
    case 0xf3:
      Serial.println("normal charset"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x67); //g
      break;
    case 0xf2:
      Serial.println("alt charset"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x66); //f
      break;
    case 0xf1:
      Serial.println("clear display"); //from zrt-80 manual
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x45); //E
      break;
    case 0xf0:
      Serial.println("clear"); //spare
      SendKey(0x1b); //ESC
      SendKey(0x5b); //[
      SendKey(0x32); //2
      SendKey(0x4a); //J
      break;
    default:
      Serial.println("undefined macro");
      break;
  }
}

The last part is how I deviate a little from the 2376 I’m modeling my timing on. That controller holds a state for as long as you are holding down that key. With my multi-key sequence macros I can’t do that. I took the information on the world’s fastest typist (216WPM), converted that to characters per minute (1080) and then to the maximum amount of time the whole transaction needs to take place (55.5ms). That helped me set the timing of the keyboard pulses so as to mimic someone typing faster than the world’s fastest typist hopefully to allow for the whole macro to be sent out before someone tries to hit the next key (although for very long macros that time would have to be further reduced to get that effect).

void SendKey(byte code)
{
  //set bits
  SPI.beginTransaction(set595);
  digitalWrite (ss, LOW);
  //set output bits
  SPI.transfer(code);
  digitalWrite (ss, HIGH);
  SPI.endTransaction();
  Serial.println(code, HEX);
  //settle 4.4ms + 88cycles @ 50khz = 6.16ms
  delayMicroseconds(6160);
  //set strobe
  digitalWrite (strobe, HIGH);
  //settle 0.5us + 30ms (way exceed human typing speed)
  delay(30);
  //look for busy signal
  while (!digitalRead(busy)) { //blocks on busy signal being low
    Serial.println("blocking for busy line");
  }
  //un-set strobe
  digitalWrite (strobe, LOW);
  //settle for more than 0.5us
  delayMicroseconds(1);
}

The keys I chose for the macro sequences came from the manual for the ZRT-80 (which this will probably hook up to), the manual for the ADM-3a (a pretty standard terminal), and the wikipedia entry on escape sequences. I will probably want to tune the macros to the application, but for now these will do. To edit the codes that each key sends just edit the three tables near the top of the code, 0x00 indicates no key is present in the matrix (or to send the null byte), 0xff indicates that a function has not been assigned to that key in that mode, anything 0x7f or less just gets sent right out, anything above that gets handled by the macro function to send one or more keys in sequence. I wired the output exactly like a parallel port that printers hook up to so if I were to get a dot matrix printer I may be able to make this act like a teletype in loopback mode. Now I just have to model up and print an enclosure. Either that or I can just buy a metal keyboard case and cut a big hole for the keys to stick out.

c64DTV power on reset circuit

November 27, 2020

I wanted to get this project all documented at once, but that’s a pretty tall order with how many moving parts it has. I also have issues with being able to track down all the information I want to include in a blog entry months after I did the project. I’m going to start with the latest aspect of this project I’ve finished and work backward through parts that I can document.

This is the issue I have. With my ps/2 keyboard when I turn on the c64DTV I get a “V” on the screen. As I type I get some characters, but not all, and a bunch of “V”s included until some point where it snaps out of it and is ok. The easiest option for this is to press the reset button after the device is powered on and then the keyboard operates properly. I recessed my reset button a little too far to be comfortable so now I want to have an automatic option. The standard option to get around this slight flaw in the c64DTV implementation is a keyboard twister. The problem I have is that even though it fixed the initialization function the keypresses were much slower, there was lag, it got confused if you type too fast. I don’t need the german translation functions or macros offered by that device so I designed my own solution.

I started by looking up “power on reset circuit” and came across the one for the apple 2. The problem is in order to simulate pushing the reset button I wanted to wait some amount of time, then press the button for a different amount of time, then release it for the duration of the power being applied. This circuit simply generates a high pulse for a given amount of time. In order to generate a pulse after some time I need something more complicated.

This circuit has two of those pulse generators that have slightly different periods. I combined them with a 2n7000 to get an open drain output and only have the reset pulse enabled when the output of side A is low and B is high. I did that by grounding the 2n7000 through the A half of the 556 so the pulse on the B side is not causing a reset until the pulse on the A side is done.

prototype circuit installed in the c64dtv
compact module, removable if needed
Fixed!

Upgrading a cloned logic analyzer

November 26, 2020

In my last post the logic analyzer I built so many years ago was actually useful. I did have some problems though. Due to it not being in a case or protected at all it wound up not having all 8 functioning channels. For this project it wasn’t a big deal, but for the future I want to fix that. While I’m fixing that I might as well make an interesting upgrade: double the channels! The first hack was centered around cloning an existing device to use its software, that is no longer desirable. Pulseview is now plenty good for using on a regular basis and I have no need of any proprietary software. That being said, I still (had to?) write a cloned eeprom to this board to make it identify as what I wanted.

The one I chose is the Braintechnology USB-LPS, this device is auto-detected in Pulseview with 16 channels and therefore can do what I want. I tossed out the shield that I had made so many years ago and decided to use the onboard eeprom spot since I no longer want to be able to switch between USB identifiers.

The chip I used happens to be a 24LC16B, since I’m not trying to get around the Saleae copy protection I don’t have to worry about 16bit addressing. The formatting I determined from this site ended up being C0 (tells the board to use the eeprom’s following VID/PID/DID) D0 16 98 04 84 23 00. I don’t know what DID I should use but it doesn’t seem to matter. Note that the addressing is backward-endian so every other byte is swapped. These bytes need to be placed at address 0x00 and it will detect in windows properly.

I had assumed that the software would just sort itself out because of the USB Vid/Pid setup. That was not the case for me. I got the device show up as unidentified until I installed the Braintechnology USB-LPS driver. That went well but still resulted in it not showing up in Pulseview. I got the error “fx2lafw: Failed to open potential device with VID:PID 16d0:0498: LIBUSB_ERROR_NOT_SUPPORTED”. To fix it I had to run Zadig and force the device to use WinUSB driver. I find myself having to use that program a lot lately, still not really sure what the usb drivers it switches out are for.

The next step is input protection. For the last one I cloned the input stage of the Saleae directly, but I don’t think that is as important this time. Based on the above table I decided to go with 100R inline resistors and 74HC245 buffer chips (I don’t need the bidirectionality, but the layout of the 244 is just annoying). I’m socketing them so I can try out HC, HCT, and other families (or none). This time around I’m not limiting myself to the same footprint as the breakout since I need 16 channels plus a couple grounds. I think I’m going to omit the use of a power header like the USBee, I never used them before and usb ports can’t source that much power anyway.

Since my original design I replaced the cable that had cheap chinese micro grabbers hardwired to bits of ethernet cable with a higher quality one. The new one has 9 pins, 8 for data and one for ground, it is color coded in the resistor color order and terminates in female 0.1″ crimps so I can use brand name Tektronix micro grabbers or plug into board pins directly. The newest replacement uses the color scheme that Pulseview assigns to each of the 16 channels. 0-7 are black through purple and 8-15 are grey through green. This means that each of the standard rainbow colors are used and even reused, but I think it can be obvious the order it goes in. That is except for needing to have ground on different cables; the colors get even more confusing because black is used as a data color twice by Pulseview.

still need to crimp the second 8 conductor cable

VCP200 reverse engineering

November 26, 2020

Some years ago I got a few VCP200 Speech recognition chips. Even though they are very limited in what they can do I was motivated by the simple interface to try to find a use for them. I eventually built the circuit available in the April 1991 edition of Radio Electronics to make use of the chip (with minor modifications). After finding some soldering faults and understanding more of what the amplification and clipping sections were doing I actually got it to work! My next plan for the three chips I have now is to draw up a design for the circuit to drive them using either through hole or surface mount components and have some slightly more robust modules than the one I hand soldered together.

But this post is not about that. When I watched the EEVblog episode on this chip it interested me greatly that it was not an ASIC, but probably a microcontroller with mask ROM inside. Having done a lot of work with MAME and dumping whatever I can get my hands on this seemed like an interesting challenge. After asking around it seemed that it may be possible to electrically dump the contents of the chip using a diagnostic mode Motorola included. Now comes the challenge of building the circuit to get those modes working and eventually figure out if the data I want comes out somewhere undocumented.

That’s the problem, the documentation says that this chip, the MC6804J2 has four modes of operation, and none of the documentation for any of them indicates that it can be used to read out the internal program or data memory. Based on some previous reverse engineering efforts on similar Motorola parts we started by looking at Non-User Mode, but that ended up being a dead-end (for now). For starters the documentation says that ports A, B, and C are used to interface to external instructions, but on the J2 chip we use there’s only port B and half of port A available at all! In addition the non-user mode interface is described in no documents I can find for this chip, but we may be able to eventually infer what the interface is based on other similar Motorola chips with more surviving documentation.

Based on the schematics for these test circuits I built something similar. What I made used the reset circuit and PA6/PA7 drive circuit from the functional test (which is self-check from above), but changed the crystal to an oscillator and made a whole bunch of pin configuration jumper settable. Before we move on to my design, take a look at the circuit for the ROM verify mode. This chip has a hardware CRC circuit inside and the ROMs, when properly authored, will be able to pass a CRC check using this circuit. That means that at very least the ROM data is coming out on one or more of those pins, XORed or otherwise garbled, and that at least one pin is clocking all those bits into this circuit which can verify that the CRC passes or fails.

As you can see I have adopted my usual use of massive piles of 0.1″ header and jumpers to configure things. Here I have pin headers that can be connected to a logic analyzer (port B only) as well as jumpers to enable pull ups on any available port pin. For the non-port pins: /IRQ is floating, as is EXTAL, XTAL goes to the output of my 1.000MHZ oscillator can, MDS and TIMER are both pulled up with 1K resistors. The reset circuit pins are pulled up with 4.7K resistors as are all of port B. Port A pins 6 and 7 are pulled up with 10K resistors, and pins 4 and 5 are pulled up with 1K resistors. Now for the taking of the data.

For this we used sigrok (the pulseview GUI). I had previously built a clone logic analyzer, but really the only important part about it is that it’s compatible with this open source software stack that’s pretty easy to use. For this dump we connected to pins PB0 (D7), PB2 (D6), PB3 (D4), PB5 (D2), PB6 (D1), and PB7 (D0). The other pins didn’t seem to do anything interesting and I was having an issue with some channels of the logic analyzer (soon to be overhauled). You can see that PB7 is the master bit clock, and that makes sense because in the rom verify crc circuit it is clocking the latches. PB6 is not used in that circuit but seems to be a pulse happening once every 12 clock cycles, our frame clock. PB5 seems to be the address of memory being represented in those 12 bits of data (each address is sent twice before moving on). PB3 and PB2 look random, probably the CRC values. PB0 looks like our serial program data! Here is the explanation for the above captured data:

one frame clock every 12 bits
one cycle of data every 24 bits
we get the address twice
we get 0xff for the first data byte (not real)
we get the real data for the second data byte
also, the data is delayed by 24 bits (1 address cycle)
Example as seen in the capture pic:
1 00000000000 D1, PB7 frame clock
1 00000111010 D2, PB5 reverse is (010 1110 0000) (0x2e0 address)
1 10000000011 D7, PB0 reverse is (11 00000000 1) (0x00 data from 0x2df)
1 00000000000 (frame clock)
1 10000111010 reverse is (010 1110 0001) (0x2e1 address)
1 11111111111 reverse is (11 11111111 1) (0xff data not real)
1 00000000000 (frame clock)
1 10000111010 reverse is (010 1110 0001) (0x2e1 address)
1 11010011011 reverse is (11 01100101 1) (0x65 data from 0x2e0)
1 00000000000 (frame clock)
1 01000111010 reverse is (010 1110 0010) (0x2e2 address)
1 11111111111 reverse is (11 11111111 1) (0xff data not real)

The python code that does all this parsing is based around the raw binary logic data that Pulseview can export. This keeps the filesizes nice and small while still being pretty easy to parse. That python code was able to generate a binary file representing the entirety of the program memory on the chip.

Now that we have a big block of everything in program memory it needs to be disassembled. You can do this by hand, but that’s infinitely tedious, and since there was no disassembler for the 6804 readily available we had to write one. The first pass was my super easy method of decoding every byte. I just checked one table to see what instruction decoded to and printed the contents of that table entry. Then I checked another table to see if it was at more than one byte, if so I grabbed another byte and printed it verbatim. Then I checked that table again to see if it was three bytes long, if so I grabbed the last byte and printed it, otherwise I moved on. There’s no re-checking or anything so if you get off by one byte somewhere you may never get lined up again and you’re reading data as opcodes and everything is all screwy. Frank had an improvement to my disassembler that made it a little smarter in handling each op code and formatting them in a way that matches standard disassembly syntax better.

Inside the ROM we see key areas described in the datasheet memory map. The User Restart Vector and the User /IRQ Vector are the same because in the VCP200 datasheet the /IRQ line of the microcontroller is tied to Vcc. The self-test ROM Reset Vector goes to AF6 which is interesting since the region starts at AE0. It turns out that AE0 is used for a subroutine that gets called later. The self-test /IRQ line goes to some sort of subroutine in the middle of the self test region so that makes sense, whenever that line is triggered then the program can verify it happened based on where it is in the code. The bottom of the self-test ROM actually contains some ascii data saying “MC6804J2 – ATX” which must mean that this is the self-test code specific to that processor variant, confirming what we suspected (except now we know it’s probably the HMOS variant). In the preliminary reverse engineering of the program memory it looks like the VCP200 code looks at three bytes of the data ROM that we don’t have. It also doesn’t look like the self-test code look at it at all. I am unclear what part of this processor checks the data ROM for integrity, if any.

It’s possible the data ROM is somewhere in the data we’ve already captured, it’s also possible it’s accessible in non-user mode, or through some part of the functional test program that we’ve started reverse engineering. If we emulate the processor, then send it known streams of pulses representing speech data and see what the real chip does versus the emulated chip we may even be able to reverse engineer those three bytes it reads out of the data ROM. It should be a tedious, but simple task of designing test sequences that cause the program to read those data bytes and analyzing what it does after to determine what those bytes were. That is beyond my current level of ambition though.

Vtech Whiz Kid reverse engineering

November 21, 2020

In an effort to free up space and get some low hanging fruit projects done fast I took on this one. This is a small learning computer from the 1980s. It uses a custom segment LCD for display, an optical barcode card reader and cartridges for data, a regular speaker for sound, and a membrane keyboard for input. I decided to go fairly in depth reverse engineering the hardware even though the software (and even the architecture of the processor) is still a bit of a mystery.

The cart is a pretty good place to start. I generated an entire schematic of this but the highlights are that it’s a 4-bit interface and the jumpers can pick from different pinouts of ROM chips. J7, J8, J9, and J10 pick which 4 bits are sent to the card edge, since the most common ROM chips are 8 bits and this machine is apparently 4 bits then they only need half of the ROM. The other half of the mask ROM is probably a different program and would be sold with the jumpers in the other configuration for that expansion pack. J11 and J12 swap which pin is A11 and which is an unused chip select pin, this allows a TMS4732 like is present right now or a more standard 2732 to be used, see my ROM pinouts for the slight differences. The other interesting thing is the use of a PNP NPN pair of transistors to cut power to the chip unless the chip select is asserted. This is clearly to cut down on power consumption of a battery powered toy, but thinking back on it now, why don’t ROM chips do this internally to save power? There’s also an unpopulated one for if a 2732 is fitted.

The next part I reverse engineered was the barcode reader. It uses an LM358 to amplify and digitize the signal from the optical sensor so the data can be read from the cards. There is only one track so you have to pull the card at just the right rate, but I was never able to do it consistently. It’s possible the part values have drifted far enough that the pot onboard couldn’t bring the signal back in spec, or maybe I’m just bad at it.

The screen is quite primitive, it’s an LCD segment display with some segments to create animations (like the game and watch series) but also some starbursts for character display. I managed to get a picture of the whole thing lit up due to some sort of bug when I had the keyboard disconnected. This should help if anyone in MAME can get a virtual set of segments drawn up. Higher quality versions of the back transparency and the rest of these images (as well as the cards and rom dump) are hosted at the internet archive.

Next I generated a schematic for the entire mainboard. This took a while but was not that difficult, it is only a one sided pcb with a lot of jumpers. The main takeaway is that the processor (which has to have internal ROM for the default game) also has the ability to drive the LCD directly. It has a parallel bus for reading the keyboard and the ROM from the cart, and the power button on the key matrix is a soft power switch at the hardware level.

My pin numbering scheme is not that inscrutable, is it?

The last thing of importance is the main processor itself. It is labeled T7826 with a date code of the 36th week of 1985. It can drive the LCD directly, it seems to have a 4-bit data bus and at least 11 bits of address space. The oscillator is either internal or an RC oscillator on pin 73. It uses a QFP92 package. From my research this is likely a Toshiba tlcs-42, 46a, or 47 series 4-bit microprocessor but I’ve never seen a datasheet for one in a package grater than 80 pins. I went one step further though and sent it in for decapping, having learned just about all I needed to know from the working machine. There hasn’t been a conclusion yet regarding the ROM bits or anything else, but maybe someone out there can verify my pinout findings or the processor architecture from the die.

Simple tool for reverse engineering key matrices

November 11, 2020

I decided to put together a tool for hooking up an arbitrary key matrix to an arduino and decode it by scanning it and reading the buttons pushed out the serial port. The gimmick I have added beyond most key decoders is I don’t care which are rows and which are columns. It was also pointed out to me that based on the information that we get over the serial port you can tell if the matrix has embedded diodes or not.

#define Start_pin          26
#define Stop_pin           49

void setup() {
  //start serial connection
  Serial.begin(115200);
  //set all pins as inputs to be read
  for (int i = Start_pin; i < (Stop_pin + 1); i++)
  {
    pinMode(i, INPUT_PULLUP);
  }
}

void loop() {
  //iterate through pins, outputting on one at a time
  for (int i = Start_pin; i < (Stop_pin + 1); i++)
  {
    pinMode(i, OUTPUT);
    digitalWrite(i, 0);
    //delay(10);  //settling time?
    //iterate throughall pins reading them as inputs
    for (int j = Start_pin; j < (Stop_pin + 1); j++)
    {
      //if the output pin is not the same pin being read and there is a connection
      if (!digitalRead(j) && i != j)
      {
        //print connection
        Serial.print("found connection: ");
        Serial.print(i);
        Serial.print(" and ");
        Serial.println(j);
      }
    }
    //set pin back to input before moving on to the next pin
    pinMode(i, INPUT_PULLUP);

  }
}

That’s all my code. I built it for use on an arduino mega so you get as many pins as possible, but it should work on anything. I set one pin at a time as an output and read all the rest in. This means that with a 21 pin matrix it could be a 20×1, 19×2, 18×3, … 12×9, or 11×10 matrix and I will still be able to decode that without knowing what it was beforehand. Since I scan both directions you will get a message like this with no diode across the switch:

found connection: 28 and 31
found connection: 31 and 28
found connection: 28 and 31
found connection: 31 and 28
found connection: 28 and 31
found connection: 31 and 28

Or this if there is a diode:

found connection: 28 and 31
found connection: 28 and 31
found connection: 28 and 31

One possible enhancement would be to use analog pins where possible and set a threshold for inputs to detect a crappy matrix that ends up as high resistance, but if that’s the case you have other problems (and I don’t have that many analog pins to use).

Ithica IA-1010 rev 1.3 2716 mod and enhancement

November 8, 2020

After the success of getting our Poly-88 up and running I wanted to clean up some hacks and simplify things. One of those hacks was to replace the 2708 monitor ROM with a stack of sockets bodge wired together in such a way to adapt a 2716 to the pinout on the board. After reading in the manual that there are instructions for converting the board to use a 2716 I wanted to do that so the card sits flatter in the card cage. This will explain how to make just the switch to the 2716 without increasing the monitor ROM size (power and chip select lines), how to expand the monitor ROM (and make it switchable), and how to quickly enable and disable the ROM entirely.

That’s the instructions for swapping the 2708 for a 2716. I notice a few things right away that I knew would be there, like the different power supplies and an enable line. I also notice something mucking with A10 (the highest address available on a 2716 and not available at all on a 2708. That tells me that this mod will map the entirety of the 2716 into memory and we will lose that 0x400 worth of RAM that we had on the other side of the monitor ROM. Initially this was not what I wanted, but I decided that I would like to make this switchable and I think I know how to do it.

In the instructions above it says to cut IC23’s connection to A10 and ground it. The trace for that is on the top of the board so I pulled the socket and cut the trace. I will make it switchable in a bit, but the instructions are not clear where A10 actually goes on this board. Looking at another line in those instructions you can see we’re supposed to attach IC25 pin 11 to one of the 2716 pins, I verified that this used to be connected to IC23’s pin 12. It seems that the 2708 -> 2716 size mapping increase is caused by these two things. I just need to be able to pick between grounding A10 on the 2716 while tying the board’s A10 line to IC23, and tying the board’s A10 line to the 2716 (IC36) while grounding IC23’s pin. I chose a 5 pin header and two jumpers to accomplish this. The pinout goes GND-IC23-IC25-IC36-GND and by moving two jumpers I can swap the size of the monitor ROM this board recognizes. I also cannot connect both IC23 and IC36 to IC25 simultaneously because the jumpers share that physical pin.

I went with right angle header because I knew I wanted low profile, I didn’t want wires or headers sticking outside of the card edges, and I had them handy. You also see a three pin header hooked to a different breakout that used to hold a wire link jumper. I did this so I could remove the monitor ROM entirely if I wanted. Look:

This section says that for using the monitor ROM you connect J and H, but if you don’t need it connect J and K. That’s fine and all, but look at the top. Revision 2 of the board had the benefit of being laid out so I could put pin headers and use shorting bars to quickly jump between configurations. This is an earlier revision so I don’t get that nicety. Now I have it.

vias used as pass through holes, no electrical contact is made

Here you can see all my yellow wires for this mod. You can also see one lone black wire connecting ground to an unused pin on the front panel header that allows me to power the LEDs I used on my bargraph substitute for a front panel. You can also see some red wires that were installed before I got the board. I assume that’s to fix compatibility with the expandoram or something but I didn’t like how they were done soldering directly to chips. I understand that the board cuts I made (you can see some in this picture) are permanent changes to the traces on the board and are not as easily undone as desoldering from chip legs, but I don’t think anyone will need to put this back to using a 2708. Having a programmer that can burn 2708 chips is odd enough these days, but if you do have some chips to flash I think you would want to install them into a board that you really don’t want to cut up (for historical reasons). 2716 equivalent EEPROM chips are also super convenient so you don’t have to cook them for every change. I also find that the chips will need replacing more often than the board will need rewiring, so swapability is important.

Cheap and cheerful DRAM tester

October 30, 2020

While working on my Commodore PET 4032 I had a strange problem where it started chirping like it normally does in the boot up sequence and then crashed with some extra characters on the screen. There it sat, and when I rebooted it would make some terrible sorrowful beeping not at all like it was supposed to make and it hung. The issue was very quickly found with the Commodore PET RAM/ROM board. I installed it, switched to using all possible resources from that board and it booted well. Systematically turning things off I found the issue to be in the first 16k of RAM. According to the (almost my board) schematics that meant every odd numbered ram chip.

Given that each chip is supplying one of the bits to the entire 16k bank I didn’t know which chip it was. I removed the one socketed low RAM chip to prove to myself that I had the right bank and sure enough the computer started up fine with the RAM bank replaced with the red board, but failed to boot otherwise. It is important to note that if I had removed a chip from the wrong bank the commodore would also have booted, but it would only show 16k of RAM because one of the bits from the high bank would be missing. I did things in the wrong order and while I waited for some parts for my DRAM tester I removed and socketed all the low ram chips. This did not go smoothly. On my board the chips that had been soldered down had their legs clipped so they do not insert well into machine pin sockets, but interestingly enough they work fine in double wipe sockets. I had just ensured that I would be replacing the entire bank with new ram chips and saving the working 4116s for repairs elsewhere.

Now on to the actual tester. My full BoM is an arduino pro mini, a 5v-to-12v step up converter, a 5v-to-negative5v converter, some LEDs, sockets, jumpers, and wire. With the two voltage modules selected and tweaked in (they come with potentiometers, be careful as the output seems related to the input so it’s scaling not regulating). I followed (mostly) the schematic laid out here for a 41256 ram tester. It also has a jumper that allows you to test 4164 chips (which I need) so I decided to start there. The code I wrote is pulled from him as well, but I made a few modifications for my needs. I changed the LEDs to be active high because I had already soldered them that way before I looked at the code. I moved the LEDs down one and added a second mode bit so I can now test for 4116 chips as well. I also bumped the baud rate up because I usually run 115,200 on my stuff. That’s it, the code seems to wok really well and scaled down to 4116s without complaining one bit. The hardware configuration was a bit trickier.

I had first found this nice site explaining pinouts of various 16 pin DRAM chips and how to substitute newer ones for older ones. It seems that the 41256 and the 4164 are identical except pin 1 is not used on the maller module and is another address line on the larger one. That is why the creator of the code I borrowed opted to do both, they’re very compatible. The 4116 is a little different. In addition to dropping another address line it adds two new voltages on the pins that are not used. We need to inject negative 5v on pin 1 (instead of an address line on the 256 and nothing on the 64) and put 12v on pin 8 which is where the other two draw their 5v from. The 4116 still uses 5v but it gets it from pin 9 which is where that last address line just disappeared from. This means we need a jumper at pin 1 to go between negative 5v and a data line, a jumper at pin 8 to go between 12v and 5v, and a jumper at pin 9 to go between a data line and 5v. That last one I went back and forth on. I could just output a high signal from the arduino and power the device under test from that logic signal directly, but looking at the datasheets for 4116 chips and knowing how power hungry old hardware can be I didn’t want to chance it.

I used colored 0.1″ header to remind me which lines were voltage and which were data. That will help in the future if I want to look at this device again without having to reference my documentation (which you are reading right now). Coincidentally I got a free power jack because my 5v to 12v converter has a micro usb port on it so I can run this without needing a bench supply or barrel jack.

Working once again (used but clipped 4116 chips seen to the left)

I elected not to cut or severely bend any pins on my 4164 chips because I didn’t need to. If I ever have spare 4116 chips and need to reclaim some 4164s from this PET I can do that with in the future.

Poly-88 S100 system raised from the dead

October 25, 2020

Back at Makerfaire Detroit I did a booth with my buddy Frank. We showed off a bunch of vintage computers we had fixed over the prior year and had them set up for use. Because of this a member of our hackerspace offered me some spare vintage stuff he had which either didn’t work or were just undesirable parts for some reason. Of course I accepted and we still haven’t gone through and gotten all of it working.

The pile

Here’s the pile of hardware. There are a couple things to notice. There’s two 8″ floppy drives there and an enclosure for said drives. Under those orange binders are two more drives in a cardboard box (probably the intact ones, two have wires gnawed by mice that I still have to fix). Those orange binders though, they’re interesting. They seem to be the original binders that came with a Poly88 S100 bus computer system, notable for being one of (possibly THE?) smallest S100 chassis that was available at the time. Let’s take a look in that last box…

Looks like we have TWO of these adorable little Poly88 chassis, some cards, and another mystery PCB. Ignoring the S100 stuff for now, what is that mystery PCB? It’s got:

  • 6x 24 pin dip spots, with 5 populated sockets
  • 3x 40 pin dip sockets
  • a bunch of 0.1″ header of indeterminate purpose
  • a screw terminal block screwed to a cable that connects to a supplied power supply board

The silk screen says: Copyright 1983 William White and Digital Research Computers. There are some other hints as to what it is by the labels at the many dip switches, but I’ll jump right to what we discovered. It is a single board computer/low cost serial terminal called the ZRT-80. It can take up to four 6116 equivalent ram chips, two 2732 rom chips, and either a 2716 or 2732 font rom (the larger rom allowing for two character sets that are swappable with an escape code). It runs on a Z80 with an 8250 serial port controller and a Hitachi 6845SP CRTC. I don’t know why that specific variant of the CRTC is needed, but that’s what the scanned version of the schematics with the handwritten note says.

Working

Once you have the key word “ZRT-80” you can find some stuff online and I have mirrored the relevant contents on my github. When you search that silk screen text now you’ll find an ebay listing for one of these boards. I thought I’d give someone the heads-up based on the fact that searching those words do not immediately reveal what the board is (until this article is published…). But that board, that the seller doesn’t seem to know what it is, is listed for $480. So no, no one should buy that. You can build something from scratch using only new or surplus chips and datasheet example circuits and it’d end up cheaper than that (and the ZRT-80 rom is out there so go nuts running that on your custom terminals). On this board I believe after I got all the missing parts to put it back together there were some bad solder joints in the oscillator section near that inductor on the right as well as a bad logic chip (74LS166) that was found by replacing the one that seemed too hot. That got it working alright

‘new’ in package

One of the things needed to make this terminal work was an ascii keyboard. This has a controller that scans a key matrix and presents data to the z80 on a parallel bus. These used to be the ubiquitous way of interfacing to early computers because of how little hardware they took to be read by processors with a proper bus architecture, but these days they can get a bit expensive. People have made adapters that will use a microcontroller to read a ps/2 keyboard and present the data as an ascii keyboard would do, but it’s nice to have a real one. Coincidentally Frank had a kit he got many years ago when radio shack was putting these on clearance. It has two missing parts, the stabilizer bar for the keyboard and the plastic bracket that that bar attaches to. Without it the spacebar will only work if pushed in the center and otherwise will bind and probably break the key that I can’t easily buy another one of.

This is a picture of the mechanism from the documentation that came with the keyboard. This is not exactly right as it doesn’t quite match what the underside of the spacebar we have looks like, but I still need to get around to making one of these using the measurements from the board.

Before I assembled it I scanned the board and all the docs which are now up on the internet archive. I believe the unpopulated spots on the pcb to the left are for a switching power supply in case you couldn’t supply the required negative 12 volts required by the ASCII keyboard standard (and this arcane controller chip). The spots sticking above the asterisk is for an external numpad which I think I will be attaching. It just gloms on to the matrix in parallel so the controller IC doesn’t even know it’s there and just works. ASCII keyboards are also cool because you can tell what the shift and control keys do very easily. All shift does is it masks off bit 6, making the keypress sent 32DEC or 0x20 lower in the table. Additionally the control key masks off bits 6 and 7 pushing the data sent down even further in the ascii table. This is where things like control-g for terminal bell come from, because if you held down the control key and pressed the ‘g’ key it would send 0x07 which is supposed to make the machine at the other end beep

This is the power supply that came with the ZRT-80. It has a connector in the middle of the cable and that pigtail is just screwed into the power supply terminals on the terminal. I assume this was not purchased for this terminal and was salvage to the person that had this thing before me, but it works so I gingerly don’t touch it.

One last thing we did (I asked for it) was that Frank massaged a font found here that looks like that famous ’80s font called ‘byte’ into the second half of a rom image to allow us to switch to this cool looking style. In reality it doesn’t read well even at 80 characters wide (instead of 96) on an apple monitor 3 (which has a nice crisp picture and lovely long persistence phosphor). It was made with some hex editing and visualized with this python script. The generated roms (with both fonts in both locations) are also on github for anyone that has one of these and wants to use it (I promised some people at VCFMW that they’d go up online).

The eventual goal for this is to locate it all in a nice wedge shaped enclosure and tote it around as a serial terminal with external composite monitor.

I realize this is going to be a very long post, but it’s gonna be one post because it’s all relevant to the /r/retrobattlestations contest so here we go into the next part: the s100 machine. In this pile of parts we got two Poly-88 chassis. The cool thing about these tiny S100 machines is you can expand them by getting another one and chaining them together side by side. We are only using one chassis right now because either due to bad contacts or something else the second one doesn’t work reliably yet (and we don’t have enough boards to warrant more than 5 slots anyhow). These are very cute, photogenic machines and ours is missing all the original cards and the distinctive orange metal cases but we can make do in the future with some orange acrylic bent into a U shape with vent holes and spots for any switches and indicators that would be useful to us.

It doesn’t seem to be shown in this schematic but I think some of the diodes in the unit we have were doubled up for current carrying capacity and one shorted. Replacing that and cleaning up some other solder joints got us a reliable chassis. We have the optional fan in both which is highly recommended and aside form some reset button cleaning and contact cleaner in the slots I think we’re pretty much good to go here.

I’m gonna talk about the next two cards at once (because I only took the one picture). We have a Solid State Music IO-4 card which is nice because we get serial and parallel ports that we can play with. The manual is really interesting because it shows just how configurable these serial ports are and gives different interfaces to mimic different cards that an off-the-shelf S100 system might be expecting. It’s not surprising that SSM makes flexible cards, One that Glitch reproduces is also nice, the IO-2. The second card is an SD systems Expandoram that gives me a full 64K of DRAM and only had some shorted tantalum caps on the power bus if I remember correctly. That’s right, DRAM chips older than me are all socketed and working just fine. The real reason for this is that I haven’t been able to set up a really good ram test yet to find out what’s wrong so for now we live in ignorance.

The CPU card is interesting. It’s an Ithica Audio IA-1010 card which has a Z80 and an onboard 2708 that can be placed at any 4k location starting at 8000, 9000, A000, B000, C000, D000, E000, or F000. It also has a front panel header that is IMSAI compatible, but includes a couple of extra inputs. The ROM that it came with was a modified version of the PERCOM ROM monitor found in docs for a Percom CI-812 Cassette/Terminal card. It was loaded at C000 and for some reason had the ram relocated to start at 00FF (matched a penciled in note in the manual we got with these parts). Frank hand-edited dump to put it the ram back where it was supposed to be, then relocated the entire thing to F000 so we had the maximum amount of contiguous ram available.

This is a cable I built for us since we are running without a front panel so I figured we could use any help we could get. The pushbutton is for reset, the middle toggle switch is a run/stop switch (there must be some routine still refreshing the DRAM otherwise bad things would happen, this isn’t an RCA 1802 geez), and the far toggle switch changes if the monitor rom is where we jump to on boot or if we jump to 0x0000. The method described in the manual for this is a NOP sled which forces NOP instructions into the processor upon a reset until the program counter hits the target address, then it lets go.

Now we have a serial terminal, a working S100 machine with serial ports and a rom monitor. What do we do with it? We load z80 BASIC of course! What this took was slight modifications to Grant Searle’s Z80 BASIC and simply loading the resulting intel formated hex file via the rom monitor, a usb-serial converter, and putty. I believe the serial chips we have in our IO-4 are compatible with the one he used for his BASIC and we just loaded that program into memory at 0x0000 using the monitor program. Once the program was loaded we just switch the jump switch, reset the machine and we’re at BASIC. On boot it can either be told how much memory we have or we can let it investigate by reading and writing until it hits memory that won’t change.

Once we had BASIC it was time to load some BASIC programs. This took a bit of fiddling because BASIC had some more overhead tokenizing commands before storing them in memory, so a simple python script was used to drip feed the characters by waiting for the echo from the terminal before continuing. Even then though we got some unexpected errors. This particular one is because that line is creating a very long string to display the positions of ships on the screen. By default the BASIC we used has a very small amount of string space, so by executing the command ‘CLEAR 1000’ we were able to expand that enough for this program to run.

You may notice some string artifacts, so something may not be perfect but it plays well enough for me to die quite quickly.

Here’s one of the first cards I bought to continue playing with the S100 system, It’s the 8080 CPU board rev2 (or the JAIR board for short). It has two serial ports, two parallel ports, 64k of SRAM, 64K of ROM, an 8080 CPU, and an SD card slot. It’s very versatile and I haven’t even scraped the surface of what it can do yet. I got it for the SD card slot because you may have noticed that I got 4 8″ floppy drives, but no floppy controller. With this board we should be able to load CP/M which will give quite a lot of freedom in software that can be run on a system like this. This card can switch in RAM in 8k chunks, ROM in 8k chunks, the serial and parallel ports, the SD card, and the CPU all separately. It can also drive Altair style or IMSAI style front panels. It was designed to make machines in museums work with more reliable hardware inside so they can still look authentic outside. The ability to switch all these things in and out also makes it useful in diagnosing systems that have bad boards and slowly replacing them with other boards as you fix things. I had populated it with the wrong logic families at first so it didn’t quite work, but once I replaced everything to match the silkscreen it worked both with RAM and ROM. The other functions will come later, but for now I call that a win.

This is an ADDS Kluge card, it includes S100 decoding so you can use it for prototyping whatever you want. I bought this unpopulated at VCFMW and uploaded scans of it so it can be recreated if anyone wants to take the time. With the addition of some WD1773-PH Western Digital Floppy Disk Formatter/Controller chips I might yet get that floppy controller. Just have to design it in a way that’s compatible with some existing codebase.

The other cards I have haven’t been used or even tested yet, but with all this we may be able to get two working machines now that I have a second card that can act as a CPU. So after all this work tracking down parts, fixing solder joints and faulty chips, replacing suicidal tantalum capacitors (seriously, I will never use a tantalum in any design, shorting is a completely inexcusable failure mode for a capacitor) we finally resurrected this into a working computer. Granted, right now we have to bootstrap it with typing in a BASIC interpreter or letting putty do that for us. We could load basic into the rom on the JAIR card and bank in 8K of the rom and the rest of the ram, but there’s other plans for that card. There’s also no storage yet, but listing programs to a terminal emulator and copying them into a file is reasonable for now.

Oh yeah. After all that, it was never a computer to begin with. These were apparently the leftover parts from some people that grew up with S100 systems and kept upgrading them. These parts had not, until now, constituted a complete system. So, we sorta resurrected a machine. Sorta Frankenstein-ed one together out of mismatched parts.