A place where I can ramble about my projects.

MiniDragon Homebrew CPU Early Progress

March 27th 2020, 2:05:46 pm

Progress has continued on my MiniDragon Homebrew CPU at a fairly linear pace. I'm well on my way to a very early bring up. Lots of things have been cemented and I am narrowing in on the final physical layout for lots of parts! Since my last blog post a month ago I've made a ton of progress on both the hardware and software side of things, validated a bunch of assumptions and tested a giant chunk of the existing design individually. I have yet to do a full integration test but I am getting very close to having enough of the CPU built in order to start that process!

Physical Assembly and Layout

The current plan for the physical towers of components. The bold text represent finished sections.

At the end of February several major components were out to fab and I had no design for the CPU itself outside of the simulator. In order to get an accurate part count I started putting together a high level block diagram for the whole thing. Dispite a lot of limitations and bugs, I decided to do this in KiCad. The advantage is huge: I have a block diagram where each component can be opened up and inspected, all the way down to an individual transistor. So MiniDragon is about as fully documented as is possible. I have yet to finish all of the diagrams but they are already complete enough for me to have built six components of the CPU as well as create a microcode programming generator program. If you are curious, the block diagram is up on github, along with everything else in this blog entry!

I've continued assembling circuits as they come back from fab. Progress has shifted from initial board bring-up on the first board of a design to bulk assembly of boards. I've been building components as fast as I can in order to get enough boards to build out the various high-level components of the CPU itself. Since the last blog post I've assembled nine Rev. 2 D/T flip-flops, almost a dozen simple logic gates, a few 1-to-2 decoders and 2-to-4 decoders and and a handful of the 4-bit register circuits. On the completed components I count 24 logic boards, flip-flops and demultiplexer circuts and another 24 breakout boards used to bring connections out to the edge of the 1'x1' component boards.

Assembling the D/T flip-flops to be used in the microcode counter board.

As for the components themselves, I have assembled the B and D registers (seen below on the right), the instruction register, the flags circuitry, the microcode counter (seen below on the left) and the data bus (below in the center). The pieces of each of these that interface with each other have been connected as well. Each board has been verified in isolation to ensure that it performs as specified. However, without the beginnings of an instruction decoder any test to verify that the components play well with each other will be meaningless so I've held off for now. I'm sure I'll find stuff during integration and bring-up but that's how every project goes. I'm extremely excited to be very close to this, however!

Current layout of completed components.

The block diagrams also include the wiring for control signals coming out of the instruction decoder. My current design revolves around a bunch of 32-bit ROM boards which are programmed using jumpers and collected through a pull-down open-collector bus. That means that I have plenty of room to represent the 29 control signals as reprogrammable microcodes per-instruction. It also means that I now have a physical location in ROM for each of the control signals. With that I've been able to write a utility that takes the instruction classes in the current simulator and spits out a microcode programming guide for each instruction, telling me how many microcode entries each instruction needs as well as where to put the jumpers in order to make the instructions work correctly. This also means that any modifications I make to the instruction set in the simulator can be quickly reflected in hardware. This will surely come in handy as I continue to iterate on the instruction set.

Software and Opcodes

The instruction set itself has changed little since the last blog post but the changes I have made unlock new processing power. I made some minor adjustments to the ADDPC/SUBPC instructions, renaming them to ADDPCI/SUBPCI (the I standing for immediate, to bring them in line with the other immediate-based instructions). I also added a new 4-bit sign extended immediate register to complement the 6-bit one that currently exists. This allowed me to optimize the ADDPCI/SUBPCI instructions in terms of clock cycles per execution as well as remove the need for 29 ROM boards! This doesn't seem like much, but the overall speedup to the standard library was around 7% and a few of the worst algorithms were sped up by over 20%. Also, the ROM boards themselves are massive and thus expensive, so reducing my need by such a large number of boards is huge!

Measurements taken in the simulator before and after the ADDPCI/SUBPCI change.

Renaming the ADDPC/SUBPC instructions to ADDPCI and SUBPCI opened up the ADDPC namespace for a new instruction that can adjust the PC register by a signed offset stored in the A register. I originally added this instruction to rewind string pointers for strcmp/strcat/strlen/strcpy functions in the standard library. However, after finishing the implementation I realized that it also unlocks indirect memory addressing. This means doing object-oriented operations as well as array lookups and jump tables become much, much faster. It was always theoretically possible to increment/decrement the PC in a loop, but this takes operations that could potentially be thousands of clock ticks down to a single instruction and makes it feasable to use in practice. Much like introducing SKIPIF gave me turing completeness and introducing PUSHIP/POPIP gave me subroutines, this single instruction gives me yet another degree of power to write complex algorithms!

On the software side of things, I've been hard at work fleshing out the standard library for MiniDragon. I've been coding up a host of useful basics that one might expect in a stdlib, such as atoi/itoa, strlen/strcpy/strstr/strcmp, cmp/add/negate/multiply/divide and processor initialization routines all of which are up on github. This has been an enormous amount of fun! I love nothing more than writing a standard library from scratch on a new CPU architecture that doesn't even exist physically yet! It has also been extremely valuable. The changes I've made to instructions that I detailed above came directly out of this exercise. In order to make the standard library as useful and comprehensive as possible I've been making sure that all the functions are fully tested and side-effect free from the perspective of the caller. That meant, in the case of the string functions, adding the ADDPC instruction in order to facilitate this! It has also been a huge relief to see that it is indeed possible to do real-world, useful things with this CPU and that it is not just a physical build of a toy instruction set.

Finally, in the process of writing the standard library I've made a lot of progress refining the toolset that comes with MiniDragon. I've made a boatload of improvements to the assembler, fixed several small bugs in the simulator and added additional utilities such as a function visualizer and the aforementioned microcode ROM programming generator. The processor test suite now includes validation for many classes of errors that the assembler should generate and the assembler is much more helpful in attempting to communicate why something isn't valid. This has been especially valuable in tracking down when JRI instructions reference labels outside of the 31 byte jump boundary. The simulator frontend now allows step-over-function debugging as well as run until return debugging in tandem with the existing single-step. And finally, the assembler supports much more powerful constant definitions, including the addition of the ".char" and ".str" directives for data embedding as well as support for using character literals as parameters to instructions. This has allowed me to keep the standard library fairly readable (as readable as a low-level stack-based CPU can be) while also using fewer instructions for referencing constants.

Screenshot visualization of strlen, showing stack tracing for PC and SPC registers.

What's Left?

Of course, I'm nowhere near done! I've had a few expected setbacks and for everything I finish two more things magically show up on my TODO list. The AND/OR gates that I put together to make the microcode counter board ended up not working in-circuit so I had to submit redesigns of them to fabrication before assembling the microcode counter board. Some of my seemingly simpler components were revealed to be more complicated once I block diagrammed them which meant that I had to submit more fabrication requests. And, of course, assembling the 4-bit register boards is still a very long process. I have gotten the time down from about 3 hours to an hour and twenty minutes. I have also refined my soldering technique which has resulted in far fewer parts coming up wrong during bring-up, reducing the need for time-intensive debugging sessions. However, I still have 14 more boards to assemble, so at the current rate of assembly that's about 20 hours of soldering!

Stack of register boards awaiting assembly and bring-up.

If assembling enough 4-bit register boards to complete the PC, IP and A registers wasn't enough, I also have hours upon hours of additional things to tackle before I'm on the home stretch. The microcode programming boards and the control signals termination and distributor board are all with oshpark right now. When they arrive, I'll need to bring them up and ensure they work as expected before I order a ton more ROM boards. I have another 30 bus output boards coming from fab which will be used for everything from the general purpose registers to the immediate register and the ALU. I also need to start the design of both the ALU and the external memory interface circuitry which will talk to the external RAM, bootROM and external hardware. I also need to decide on that external hardware in order to work on IO routines in the standard library. Right now I am leaning towards a simple LCD for debug messages and an 8-bit serial chip to provide standard in/standard out. It might never happen, but I'd love to pair this thing to a VT-100 like some old mainframe. And finally, I need to continue working on the standard library. I have yet to code memcpy, memset or memcmp. These will be similar to the existing string functionality. I also need to create 16-bit and 32-bit versions of the math and conversion libraries, and also handle 8-bit, 16-bit and 32-bit signed variants of the math library. And finally, once I standardize on the IO itself, I'll need to create access routines and start working on a basic shell to place in the bootROM.

As I get closer to the physical realm one thing is becoming clear: I need to finish this. If I don't, and get to 80-90% done before calling success, I am going to miss the other 80-90% of the process. I am not interested in software-only theoretical CPUs, I want a real life stack of hardware that executes software I wrote for it, built from the ground up. It is going to be a lot of hand-soldering and a lot of patience, but it is going to be VERY worth it!

Transistor CPU Part Fabrication

February 26th 2020, 4:25:26 pm

I've been working on the Transistor CPU for a few months now, which I've dubbed the MiniDragon. Since the last blog post I've changed a few minor things and nailed down a lot more of the CPU. I have a much more complete simulator, an assembler and disassembler and the beginning of a software library for an upcoming boot ROM. The CPU now has subroutine and stack instructions as well as an absolute jump, making it possible to write a reasonable library of built-in functions. Through writing a multiply subroutine I learned a lot about my chosen instruction set and have made several changes to the CPU as a result. I'm a lot more confident in the instructions since I've implemented actual software with them. That software, as well as an up-to-date description of the CPU itself, is available on GitHub.

Fleshing out the simulator allowed me to get a much better handle on the CPU itself. However, I only spent a small amount of time working on the software side of things. The majority of my time over the last few months was spent learning KiCad, digitizing schematics that I've tested on breadboards, laying boards out and sending them out for fabrication. I've been using Oshpark for fabrication since it can take pcbnew files directly and is cheap enough for small runs of boards. The boards are also my favorite color: purple! I started out with some simple logic gates that I'd breadboarded over Christmas: NOT, NAND and NOR. These aren't super interesting and they only consist of a few discrete components. However, they let me learn the ropes of KiCad before I undertook some more complicated design and routing projects like a 4-bit register.

Probably the hardest part of the last few months was being patient. As soon as I finished board layouts I wanted to throw them over the fence and get them fabricated. However, since I've never done PCB layout and fabrication before I was sure that I would make a ton of mistakes. So, I forced myself to send out small batches and observe everything that I did wrong before sending additional boards to fabrication. This paid off heavily, as I made a few mistakes that I had time to correct before it cost me unusable boards or ugly rework. It also allowed me to pace myself with board assembly, since I underestimated how much time it would take to assemble through-hole circuits.

A few specific things that I learned during this process stand out. First is to always ensure that you silkscreen the circuit title AND revision to the board. If you change component values or layout significantly, you will want to know what revision the board is in order to assemble it properly. You might be 100% sure that you will always recognize your boards, but manufacturing can take a few weeks and by the time it comes in you won't remember. I messed up my first board and forgot to silk screen it at all, so I ended up affixing labels to the circuits.

Rev 1 NOT gates with missing skilk screen.

Second is to make sure to space headers on the board apart by a multiple of the pin spacing. I chose to use 2.54mm (0.1") pitch pin headers for all of my circuits. Especially when you are dealing with 1x1 headers, it can be next to impossible to correctly insert the pin and solder it while making sure that it is plumb and square against the top of the board. However, if you have a row of multiple sets of pin headers, spacing them apart by a multiple of the pin pitch (2.54mm in my case) means that you can use a long pinsocket as a temporary holder in order to line up all the pins at once. I happened to do this on accident on my first board and was super glad that I learned this before sending out the next batch in which I had not lined up the pins at all. I use a 40-pin pinsocket as my assembly template and plug in the pin headers to it before soldering them all in one go.

Third is to use the rendered top copper and bottom copper layer photos on Oshpark. I had one board where a trace was routed far too close to a through-hole connection and another which had spurious traces. Neither of these were caught by pcbnew's constraint checker and I missed them looking at the PCB in pcbnew itself. Seeing your circuit in a new light is a super good time to check for errors. I definitely get a bit blind to errors in my layouts due to staring at them for so long while I put them together. So, a different look at the same boards has let me catch issues before sending to fabrication.

Finally, your breadboard designs might not work when laid out on an actual PCB. I arrived at values for my clock edge detection circuit when breadboarding which did not actually work when I assembled my first revision T/D flip-flop. I ended up having to do a really gross rework to verify an updated design. Luckily, I held off on sending a 4-bit register to fabrication which was based off this flip-flop. Only once I got the second revision of the flip-flop back from fabrication and verified that it worked did I send the 4-bit registers out. As a result, they worked fine! Given their size, it would have been a costly mistake had I not waited and verified. A secondary advantage to reworking the circuit was that I got to reduce the resistor part count from 4 distinct values to only 3 on clocked circuits. This makes assembly a lot easier and means that I can keep fewer parts on hand.

Nasty rework done to fix a hardware bug in the Rev. 1 T/D flip-flop.

I have a fairly decent library of logic gates and CPU components fabricated and verified on the bench at this point. I have my staples, like NOT, NAND, NOR, XOR, XNOR and a T/D flip-flop (behavior can be changed with a jumper). I am waiting for fabrication to finish on some AND and OR gates. Some of the CPU microcode logic requires them and I don't want to waste physical space and propagation delay chaining NAND/NOR circuits to NOT circuits. I have a few more useful circuits such as a 1-to-2 decoder and 2-to-4 decoder. Naturally, these can be chained to make a 3-to-8 or 4-to-16 decoder which I will be using in the instruction decoder and microcode lookup circuits. I also have a few parts that I'll use for the actual CPU designed and tested. I settled on a pull-up, open collector bus architecture due to its ease of coupling multiple driving circuits. To support that I have a few 8-bit bus backbone circuits that provide current bus value indicator LEDs and pull-up resistors as well as a ton of 8-pin headers. I have an 8-bit bus writing circuit that has an 8-pin input header and an enable control signal and an 8-bit output header designed to plug directly into a bus. Finally, I have a power on reset and clock circuit that provides automatic and manual reset as well as automatic (adjustable speed) and manual clock pulses.

Lots of completed and verified circuits!

Most of the above circuits are fairly boring in their implementation. The logic gates are similar in design to any RTL circuit you can find online. The bus circuit is just a bunch of pull-ups and a few buffered transistors driving LEDs. The bus writing circuit just converts 8 signals from 0V/5V signals to open-collector outputs. The clock circuit, however, is a bit more interesting. At its core it consists of two components: power detection and a clock generator. The clock generator is essentially an astable multivibrator buffered through some signal-shaping inverting transistors, then finally lead through a 2-bit selection switch and an output amplifier. This lets me choose either the automatic clock or manual clock pulses that are input by a switch that's debounced using a capacitor. The power detection circuit uses a zener with a reverse breakdown voltage of 4.7V, hooked up in reverse. This drives a transistor that switches on to indicate that power is good enough to use which drives an RC circuit to generate a reset pulse. The output of that is buffered, and also goes to the clock's amplifier circuit. This allows the reset logic to disable the clock while the reset line is held high or when the voltage is too low. This lets the CPU auto-reset its registers on power-up, ensuring stable boot every time power is turned on. Finally, a manual reset button is wired in as a logical OR to the reset pulse circuit. That way, if I decide to reset the CPU I can press the button and reset will be asserted while clock is disabled.

Oscilloscope showing power (purple), reset pulse (blue) and system clock (yellow).

I still have a ton of stuff to lay out and get to fabrication. I have the parts on hand to build the flags circuitry and one of the 8-bit registers (either the A register or the D register). I'm waiting on additional parts to come in to build the microcode counter circuitry. I've standardized on a 1x1 foot ABS plastic base for various logical components of the CPU but haven't permanently attached any components yet. I am also waiting on some simple bus/power/control line breakout boards that will let me run various connections to the edge of the panels for easier and more modular assembly. I need to design some jumpered 8-bit ROM boards so that I can program the microcode instructions in, and I need to start laying out the actual instructions one at a time. I also need to assemble 18 more 4-bit register boards to have enough registers for the whole CPU. Then, once all of that is brought up and verified, it will be time to start work designing, laying out and fabricating the ALU itself!

Transistor CPU Project

January 4th 2020, 4:20:15 pm

For about a decade and a half, I've wanted to design and build my own CPU from some sort of discrete components. This has become fairly standard in the hobby world and is completely obsolete with the existence of FPGAs, tons of cheap and available processors and even some microcontrollers costing as little as three cents USD. Nonetheless, I wanted to design a CPU myself mostly as an opportunity to learn and give myself a large project as a challenge.

A few weeks ago I was feeling super under the weather so I came home from work to rest up and started binge watching Ben Eater's Channel on YouTube. Side note, I love his channel. He does such an amazing job breaking things down to easy, well paced chunks that so many people can understand. The channel is like junk food to me and I love picking random things to watch. Anyway, six or seven videos into his 8-bit CPU build I started asking myself why I hadn't ever gotten around to designing and building my own CPU. So, I grabbed a set of resistors and some 2N2222 transistors I had laying around and just started playing with BJT logic gate circuits I could find on Google.

Simple circuits that I threw on paper after testing.

I didn't want to just take somebody's word for it online, so when I built the circuits I took a lot of measurements using my oscilloscope to verify the design. For each simple circuit I build I measured propagation time when the input went from low to high and from high to low as well as the amperage pulled by the circuit when running at 5V in various scenarios. Getting the worst-case propagation delay for each circuit allows me to figure out what the maximum clock speed of any CPU I build will be. I worked my way up from a simple not gate with a single transistor as well as a buffer, to nand, nor, and and or gates, and finally an SR latch. Once I had those parts built and verified, I would sketch up schematics for them with various notes on their propagation delay and current requirements. Ignore the obvious schematic errors below, I haven't done pen and paper logic design in years and completely forgot that I was drawing xor gates instead of nor gates.

Additional circuits that I tested and measured.

With an SR latch you can build all other types of latches and flip-flops. A CPU needs registers, and I want to build the core of the CPU entirely from discrete transistors and resistors, so I needed to build and test a D flip-flop. This meant adding an enable line and tying that enable to an edge detection circuit and verifying that I could "clock in" a 1 or a 0. This worked as expected, except for playing around with the resistor and capacitor values for the edge detection circuit. It still doesn't work quite right depending on the speed of the clock, and its affected by the circuit that drives it so I think I'll have to buffer it in a future redesign. Later, I added a second un-clocked enable and an asynchronous reset input, both of which will be necessary to use this as a single bit in an upcoming register. The enable will act as a chip select, allowing CPU control logic to dictate whether a particular register should store a value present on its input at the next clock pulse or retain the existing value. The asynchronous reset will allow a power on reset circuit to reset all registers to zero when the CPU is powered on for the first time.

D flip-flop with a clock and data input, a buffered output with a 10K load and an indicator LED.

If you have a D flip-flop and you have access to the inverted output, you can feed that back into the data input in order to make a T flip-flop. This type of circuit is great for chaining together to make counter circuits or clock dividers. I verified that the theory also worked on my D flip-flop circuit. I have several more circuits that I have to build and verify before I could theoretically put together a CPU of any sort. I need xor gates. I also need some way of selecting one of multiple inputs to drive a bus. Both of these could be handled by the circuits I've already built at the cost of additional propagation delay as well as higher part count. However, I want to keep the part count low so I need to build simpler circuits. Currently I have plans to lay out several busses for the CPU core and I've decided to go with an open collector design instead of tri-state output. This is because of the increased complexity involved in producing a tri-state output (multiple transistors, diodes and an inversion required) versus an open-collector (a single pull-down transistor on an active-high bus).

In order to make the CPU core more modular and thus easier to build, I've decided to go with a microcoded architecture. This will let me prototype the CPU using an EEPROM to hold the microcodes and very quickly swap things out if I don't like how it works. The final CPU will use combinatorial logic to decode instructions and a diode matrix board per opcode to store the control signals at each step in the CPU's execution. I'll use a series of D flip-flops as a counter to control which microcode to select given a decoded instruction. This design also allows me to reduce parts in several critical areas of the CPU since I can reuse expensive parts such as an adder circuit to drive both the ALU and the program counter. This comes at the cost of slower instruction throughput as only part of each instruction will be executed every clock cycle. I could have made the trade-off to have more complicated logic circuitry, but when I'm looking at hand-soldering each transistor I would prefer to keep things simple and slow.

With most of the basic theory out of the way and verified in-circuit, I got to work thinking about an instruction set. I took a lot of inspiration from the PDP-8, another transistor computer, as well as Ben Eater's simple 8-bit computer. Ben Eater's computer is more of a learning CPU since it only has 16 bytes of memory available. While it is turing complete, it is extremely limited. I want to keep my CPU simple so that its humanly possible to design, wire up, debug and code for. However, I do want to be able to write "useful" software for it. I'd like it to be capable of interfacing with external devices, possibly through serial or keyboard and VGA. I'd like to be able to code simple games or productivity software for it. And finally, I'd like it to be self hosting which means making it powerful enough to code an assembler that runs in a boot ROM. This necessitates a Von Neumann architecture. It also necessitates having access to a decent amount of memory and external hardware registers.

I settled on a hybrid 8-bit CPU design which allows for software access to 16 bits of RAM/ROM/external hardware registers. Staying true to its inspiration, I have a simple 8-bit, accumulator-based CPU and software can only interact with memory or this single register. However, several more support registers that aren't directly software-accessible will be 16-bit to enable full access to program and data memory and external hardware. I wanted to keep instruction decoding simple, so all instructions are 8-bit as well with no variable instruction width support. Most instructions deal with loading/storing or manipulating the accumulator in some manner, with a few instructions able to interact with special registers. Instead of conditional jumps, I'm going with a skip next instruction opcode which will allow any supported instruction to be made conditional. I don't currently have absolute jumps, call or return support or a stack right now but I have plenty of space reserved in the opcode space to add these in a future revision. Several of the registers are write-only or cannot be directly read or written from software which means this CPU cannot be multi-threaded. I think that's okay though, given that the CPU is probably going to run on a clock in the KHz range and would struggle with even the simplest of multi-threaded code.

Snapshot of a Google Sheets document outlining my current instruction support.

The full list of busses and their design is as follows:

  • 16-bit data bus. This is the primary bus used for moving data between registers and memory. It is 16 bits in order to allow the instruction pointer register to interact with the ALU.
  • 16-bit address bus. This is the bus that feeds the address circuitry for main RAM. It is separate from the data bus to remove the need for a dedicated memory address register.
  • 16-bit ALU source bus. This bus feeds one input to the ALU.

The full list of registers and their capabilities are as follows:

  • 16-bit instruction pointer register (IP), holding the address of the current instruction in memory. Its value can be placed on the address bus or ALU input bus and it can read from the data bus. Software cannot directly set this, but a jump relative to immediate instruction allows it to be indirectly updated.
  • 8-bit instruction register (IR), holding the current instruction that was fetched from memory. It is write-only and feeds microcode decoding logic, but can read from the data bus. Software cannot directly set this, but memory is modifiable so self-modifying code is possible.
  • 8-bit accumulator register (A), holding the current accumulated result. It can read from and write to the data bus, and it can output to the ALU input bus. Many instructions available to software can directly manipulate this register.
  • 8-bit ALU temporary register (B), holding a temporary value from the bus. It can read from and write to the data bus. Its output is also hardcoded to the second input of the ALU. Software has no capability to modify this register and it is used by various microcodes to accomplish virtually all CPU operations.
  • 8-bit memory page register (P) and 8-bit memory cell register (C), together holding a 16-bit address. The P and C registers can individually read from the data bus, and the combined PC value can be output to the address bus or the ALU input bus. Software can write to the P and C registers from the A register and can use the combined PC register contents to load from and store to memory, but it cannot directly read from either register.
  • 2-bit flags register, containing a carry flag (CF) and a zero flag (ZF). Software can directly set or clear the carry flag using a pair of instructions, and both carry and zero flags are set appropriately when carrying out any ALU-based operation which sources from and stores to the A register. It is not directly readable by software but there exist skip instructions that allow software to conditionally execute a particular instruction if either CF or ZF is set or cleared.

Aside from registers and busses, a few more pieces of hardware will exist to make the CPU core:

  • An ALU, which performs operations against the B register and either the A register (sign-extended from 8 bits to 16 bits), the IP register or the PC virtual register. All operations except for add operate only on the low 8 bits of the ALU bus. Add works on all 16 bits of the ALU bus and a sign-extended version of the B register. This allows software to request that the PC be incremented or decremented and it allows the CPU to use the ALU to both increment the IR as well as perform both conditional and unconditional jumps.
  • A zero generator which outputs all zeros to the data bus. This is for pre-loading the B register for certain ALU operations. We also need a -1 value but given that we are using an open collector bus design, we can simply turn off all outputs and the bus will read all 1's which is equivalent to a -1 in two's compliment.
  • Some sort of ROM and some sort of RAM. Given that its infeasable to build an SRAM circuit of any usable size out of discrete transistors and core memory is far past obsolte and difficult to obtain, I'll probably use a standard EEPROM and SRAM chip for this.
  • Combinatorial decoding logic, feeding diode ROM select boards and sourcing from the IR. This is the heart of the control circuitry which will generate the control signals which feed the various register enable input and bus output inputs.
  • Flags register combinatorial logic, feeding the data bus with either a 0 or 1 value given particular opcodes in the IR and current values in the CF and ZF registers. This allows us to preload a 1 or a 0 into the B register and implement a conditional skip.

Given that its going to be rather expensive to prototype in terms of space, time and actual components, I went ahead and wrote a microcode simulator for the CPU design. This was super useful when I was laying out the supported instructions because I was able to test out the actual capabilities of such a CPU by writing miniature programs. Using this simulator I realized that I could do away with several opcodes such as shift right, and could cleverly manipulate the IP register using the ALU to implement standard instruction advancing, relative jumps and conditional execution. During the development of the simulator I ended up also writing a simple assembler and disassembler in Python which will be super useful for writing code that runs on the real hardware before I get the on-target assembler off the ground. If you're interested in playing with it, I threw it up on my website. It also serves as the master documentation for microcodes since it fully simulates the various busses and registers.

I still have a lot of work to do on virtually all of the CPU pieces before I have anything resembling a real CPU. However, given the layout and simplicity of the busses and control signals, I should be able to piece-wise assemble and test the CPU part by part. The next big thing I need to do is test circuitry that will allow me to assert on a bus so that I can start building the bus itself and then send out some boards to be fabricated. I think I'll start with register read/write and a bus and build out the various pieces from there. The most complicated part is going to be the ALU but even that can be built function-by-function until I have a fully functioning ALU circuit. And finally, once I get this particular version of the CPU up and running I'll jump in and see if I cant get absolute jump, call and return instructions and a real stack implemented. Stay tuned for updates to this project!

Reverse Engineering a Dot Matrix Plasma Display

May 2nd 2012, 11:26:00 pm

Awhile back, I bought a lot of arcade parts from somebody on Craigslist with the intention of getting some parts and marquees for my house. I wanted to try my hand at reselling as well to see if I liked it. Most of the garage has been sorted, sold or thrown away at this point and in the back corner I found a box labeled "Betson Replacement Screen." I pulled it open and found a brand new dot matrix display. Remembering a post by a friend on his progress reverse engineering a LED display from the 80s, I decided that I had to get a picture on it.

The first thing I did when I got it upstairs was flip it over and google the model number, "APD-128G064A-1". My initial excitement was dashed when I realized that the copious links all pointed to the same 2-page PDF document that was almost entirely devoid of information (datasheet here). It did go over the theory of use at a very high level. Also, thankfully, it discussed voltage requirements. With that bit of information I decided that my best course of action would be to get power to it and see if it did anything, followed by attempting to trace the circuit and figure out what pins did what. Since the datasheet called out the method by which the screen could be updated, I just needed to get a good idea of which pin was for each of the six signals listed. Then I could experiment with a microcontroller and confirm my guesses.

There was a nice test fixture (possibly for probing converted voltages?) that mentioned Vcc, so I stupidly hooked 12V to that and ground to the gnd pin. Nothing happened, and I shut off the power supply and re-read the data sheet. I realized that it called out two different voltages, one for DC-DC conversion to run the plasma display, and one for the logic. With that bit, I re-examined the ignored 4-lead cable attachment and realized that it was probably the power connector. Vcc as labeled was traced to a pin, ground to two more, and I guessed that the last would be the 12-36V DC-DC input. I re-soldered to these instead of the test points and turned the power on. I was immediately greeted with a zapping noise and several of the dots lit up temporarily. Before I could congratulate myself, the power supply tripped. No matter, I knew I had the power lines connected right.

With a renewed excitement, I soldered wires to the used pins of what appeared to be the logic connector and began tracing the circuit out. Every chip on the circuit was a 7400 series chip with the exception of the shift registers. All of them had data sheets online. I managed to probe out several of the connections, concentrating on the circuits that fed the clock and data in pins on the shift registers. After a good hour or so of probing, I had the following crudely sketched schematic and was 99% sure as to the use of four of the six inputs. The other two were either row clock or row data, but I could figure that out by trying both combinations.

Backside of the display.

Schematic bits.

I hurried to Fry's to pick up an Arduino board and they had what they called "Arduino Compatible" OSEPP Uno boards. I got it home and after a bit of googling learned that you had to manually select a different board type. I burned the blinking light test binary on and it worked, so I was off throwing together a quick test application. Armed with a new, beefier power supply, I tried my code. The first thing I attempted was to display a test pattern to the screen. It failed outright. I figured that since the display claimed that the row needed manual resetting that I wasn't clocking the row line in properly. After a bit of time experimenting, I had a program that took the random garbage that showed up on the screen when it booted and stepped it down one line per second. I now knew which line was row clock and which was row data. After a few ore hours of throwing code together, I came up with this program. I powered on the display and below is what I was greeted with:

I had officially achieved liftoff! There was a pesky problem with wobbly pixels in some areas, and the screen refreshed so slowly that it flickered like hell. Also, there was garbage at the bottom of the screen that I couldn't account for. Now that I'd prototyped it, it was time to get rid of the Arduino libraries (they are very slow) and go to raw register accesses. Also, I wanted to create some sort of protocol by which I could update the display using the USB to serial connection provided on the Arduino. I briefly tried creating 3 color and 4 color grayscale images by displaying images 1/2 of the time, and then 2/3 and 1/3 of the time. The screen stopped lighting all the pixels when refreshed too quickly and when I slowed it down well enough to get a clean picture, it flickered, so that was out. Also, since there was only 2 KB of SRAM, I had to place half the image in EEPROM which meant that access times for the two frames were not in sync. I finally settled on a simple serial protocol by which the EEPROM could be rewritten, a 1KB buffer in SRAM could be rewritten, or text could be rendered onto the screen using the EEPROM image as a 128 character 8x8 font.

The program took me a few nights to complete, thanks to some setbacks with the Arduino programming environment. However, I got the column data clocking out via SPI instead of bit banging which drastically improved the refresh speed. I also converted to raw port accesses which helped. The images now displayed crisply and I could dump data to the screen using a test program in Visual Studio. I put my friends to work helping me format an 8x8 font bitmap and drawing black and white images to display, and the results are below:

Text rendering using font burned to EEPROM

A cat!

Milhouse is not a picture.

The current source code to the Arduino application is available here. The visual Studio application that I developed into a console application is available here. They're probably not much use to anyone who doesn't happen to have one of these screens available but I figured they would be educational to the curious. The serial protocol can currently accept a 128x64 image for display or burning into the EEPROM. It can accept text to be rendered. It can clear or invert the screen. It can force text to be drawn normal or inverted. It can also move the cursor. I plan to add several more functions to the serial API such as scrolling and scaling, blitting and other high level graphics operations one might expect from a nice display driver. I would take the easy route and just upload full processed images but unfortunately I can't push the serial past 57600 baud or the interrupt can't keep up with the image refresh.

That isn't all, however. I plan to pick up an old laptop from a friend and put Linux on it. From there, I'll need to port the console application and add a web server. I want to put up a simple PHP script that allows poeple to upload images or text to the screen from the internet. Possibly, I also want it to take twitter updates or texts on a google voice number. Basically, I want this to be an interactive toy that guests who come to my parties can interact with using their phones. Stay tuned for more development!

Wanted: Competent Video Game Sellers

November 12th 2011, 10:37:00 am

So you want to sell some video games on craigslist? Maybe you have a few games you don't play anymore. Maybe you are a reseller. Maybe you are cleaning out your parents garage and find something you think is rare. Do all of us a favor when posting and follow these simple instructions so that you don't waste everyone's fucking time with the written equivalent of a crayon drawing of your dick.

First things first. Tell us what you are actually fucking selling! You'd think this is business 101 but there's probably a reason you are selling this on craigslist instead of opening your own game store. If you want to attract anybody at all except for the lowest of low-ballers you will need to specify exactly what you have and what condition it is in! I am not even going to bother replying to you when you leave a vague, poorly worded one sentence post about your Atari and a few games. "Sweetening" the deal by mentioning that you have rarities and not mentioning what they are only makes us suspect that you have no fucking clue what you are doing. It helps to list what accessories come with a system as well as what condition it and the games are in. Also nice to know is whether manuals, cases, boxes or anything else interesting comes with your sale. By the way, brand new means just that: BRAND NEW. If something is opened but not played, it is LIKE NEW. If something is played, it is USED. Don't bullshit me with brand new when it is not factory sealed.

Please actually do some fucking research before claiming rarity on games or systems! I understand that not everyone is a collector but do realize that when you scream on about your super rare Atari 2600 with 40 games, what you probably have is nothing more than $10-$20 worth of garbage that nobody will want to take off your hands. I've noticed a direct correlation between people who think they have amazing sales and notices in the sale about ignoring low-ballers. There's a fucking reason people are low-balling the price you set, buddy! It's because its TOO FUCKING HIGH. Nobody is going to spend $20 to buy your worn copy of Tetris for game boy. It doesn't matter if the system is 20 years old; it isn't fucking rare unless people are looking for it and can't find it!

Also, pictures! Craigslist lets you post pictures for a goddamned reason. Its hard enough to trust a random anonymous stranger on the internet as it is. Provide photographic backup to your description! Don't want to post a picture? You probably have no fucking idea what you are selling anyway. Don't tell me to email you for pictures. If you were too fucking lazy to snap one goddamn grainy cellphone picture, that doesn't lend much to the belief that you will respond at all let alone with pictures that will help me make a decision on your sale! By the way, STOCK PICTURES DO NOT FUCKING COUNT. I know what an Atari Jaguar is supposed to look like. I want to know what YOUR Atari Jaguar looks like.

The condition of your games and systems ACTUALLY DOES MATTER! Believe it or not, there are some people that buy games who want their games to look good. I know for a fact that your copy of Chrono Trigger plays like new. Cartridges don't go bad! But if its covered in permanent marker and barbecue stains complete with a torn label I am not going to pay full price for it, no matter how rare you read that it is. Look at it this way: any of these old games that you are selling can be emulated near perfectly on a computer FOR FREE, so what do your games have that makes them better? I am buying something to own, to show off and to cherish. You wouldn't expect me to pay Kelley Blue Book on a car that has dents and peeling paint, even if it runs just fine! So don't fucking pull that horse shit with video games. This also goes for manuals, cases, boxes and the like. If you expect to fetch a decent price on a video game that isn't rare, have all the pieces that it comes with! If you don't, realize that it is WORTH LESS.

Also, you'd think this would be a no-brainer with something that needs to function, but test your fucking games! If you can't test them for some reason, tell us that you couldn't test them! Expect to get a lower offer unless you are willing to help us test them on the spot during the sale! Do understand that if you do not mention testing the games, it is assumed that THEY WORK. Take the car analogy again: if a car is listed and the seller makes no mention of problems it is a valid assumption to believe that everything works fine!

Now, lets touch on pricing. As with ANYTHING on the market, video games are only worth what somebody is willing to pay for them! This is another business 101 concept that I wish more people would figure out. Just because you saw it go for $300 once on eBay does NOT mean that your used copy of Final Fantasy VII is worth that much. You probably made a mistake in reading the condition of the sale! Was it a sealed copy? Was it a rare misprint? Was it signed by the developer? Chances are your copy is not worth what the most expensive games go for on eBay. Also, nothing infuriates me more than seeing somebody justify ridiculous prices by pointing out that eBay sells it for that much. If you want to sell it for eBay prices, then go sell it on eBay. I know you are a cheap-ass who doesn't want to pay fees, but I am also a cheap-ass who doesn't want to pay eBay prices! The point of exploiting a local market is that things tend to be cheaper! I am not going to go out of my way to drive to your house all the way across town to pick up something that I could have shipped to me for the same price!

With all that in mind, I look forward to purchasing from you! After all, I love video games and I love getting excited about the games you are selling if only you'd allow me to. A well written advertisement for stuff that I want will get me to jump on the sale immediately. That way you can take my money and I can take your games and we can both go home happy. After all, the whole point of doing business is so that both parties walk away happy. Otherwise, what's the fucking point?

Newer Entries

Older Entries