A node.js-powered 8-bit CPU - part four
This post is part four of a series, the other posts available are
In part two we constructed a circuit using shift registers whereby we could write a byte of data and then read it back in. In part three we booted a 6309 CPU and ran it with a hard-coded operation on the data bus. In this final post, we will put the two together so that we can write arbitrary data onto the data bus and then read back any results.
First, we need to introduce one more 7400-series IC, the 74HC14 hex inverter.
These are really simple chips, all they do is take input on a pin, and then output the opposite onto another pin.
The “A”s are input and “Y”s are output. So, inputting a
0 into 1A/Pin1
results in a
1 being output from 1Y/Pin2. No need for any clocks, just
power, and we have 6 converters available.
Hooking It All Up
Ok, let’s connect everything up. Here’s what we need to do:
Connect the two bread boards together, with the shift register board above the CPU board. The bread boards have tongue and groove joints for a snug fit.
Wire up +5V and ground rails between the two so that they both have power from a single source.
Wire up the D0-D7 data bus from the 6309 to the data bus on the shift register board - it is easiest to hook them in just below the green LEDs.
The GPIO pins from the Raspberry Pi are connected as follows:
GPIO17/Pin11 goes to Q on the 6309.
GPIO18/Pin12 goes to E on the 6309.
GPIO21/Pin13 goes to STCP on the 74HC595.
GPIO22/Pin15 goes to DS on the 74HC595.
GPIO23/Pin16 goes to Q7 on the 74HC165.
GPIO24/Pin18 goes to RESET on the 6309.
There are 4 more connections we need to make, which is where the 74HC14 comes in.
RW On The 6309
The RW pin on the 6309 denotes whether from the 6309’s point of view the bus is being written to or read from. When high, the 6309 is reading, and when low, it is writing.
We can use this feature to automatically switch the OE (“output enable”) pin on the 74HC595 on or off, so that when the 6309 is reading OE is enabled, and when it is writing OE is disabled, leaving the data bus clear for the 6309s data. In this way the bus can be shared between devices.
However, we cannot connect them directly, as the logic is the wrong way around
- we want OE to be low when RW is high and vice versa. So, we use the 74HC14, and by connecting them through pins 1A and 1Y on the 74HC14, we get the logic we want.
PL On The 74HC165
The final connection we need to make is a way to trigger the 74HC165 to read from the data bus, so that we can get data back into the Raspberry Pi.
The solution for this is to wire the 74HC165’s PL pin to clock E on the 6309. This isn’t immediately obvious, but is determined by careful reading of the 6309’s datasheet. The quality of the image below isn’t great, but it shows when data from the 6309 is valid.
The part we are interested in is the “Data” line, and the timing when the data is readable is the time between the Q clock going low and the E clock going low.
So, for my design, I have hooked up the PL pin to the inverse (again, going through the 74HC14 to invert the logic) of the Q clock, using pins 2A and 2Y. This way, whenever Q goes low, the 74HC165 reads whatever is on the data bus at that time, and if this happens to be at a point when we want to grab data from the 6309, it should be valid at that point in time and we then just need to shift the data out of the 74HC165 before the next Q clock when the data will be replaced.
The finished boards should look something like this:
The 74HC14 is at the top left, with the orange and white jumper wires used for the inverted connections.
At this point we are done with the hardware, and can move to software. First we need to come up with a program, and for this I have chosen a very simple one
- add two numbers together and return the result. The code is below.
od we can look at what the instruction opcodes need
From this we can determine:
0xccis the opcode for
LDDwith an immediate value.
0xc3is the opcode for
ADDDwith an immediate value.
0xfdis the opcode for
STDto store at an extended address.
An immediate value is one that is used as-is for the data, rather than being an address containing the data. An extended address is where a full address is specified, rather than an offset. Different addressing modes result in different opcodes, as the instruction needs to do different things.
Now that we have our opcodes, we need to find out how many cycles each of them take to complete. As we are hard coding every clock cycle in this set up, it is critical that we read and write data at exactly the right time. The best reference I’ve found for the 6809/6309 is here and it gives the number of bytes and clock cycles for every instruction.
At this point we have all the information required, and it is a Simple Matter Of Programming. My script to add two arbitrary numbers using the 6309 going via the shift registers for input and output is below. The comments inline hopefully explain exactly what is happening.
We can run the program as follows.
The output appears to be the correct answer. We now have a fully functioning co-processor attached to our Raspberry Pi ;)
Of course, there are some limitations. As we are dealing with 16-bit numbers, we can overflow:
However, adding support for that and executing arbitrary instructions is left as an exercise for the reader!
Hopefully this was interesting. It has certainly been very useful for me to learn exactly what is going on at the hardware level, and I’d strongly encourage anyone involved in software development to do likewise - it gives you a new appreciation for the operations involved in running your code.
- 16 Jul 2015 » Reducing RAM usage in pkgin
- 03 Mar 2015 » pkgsrc-2014Q4: LTS, signed packages, and more
- 06 Oct 2014 » Building packages at scale
- 04 Dec 2013 » A node.js-powered 8-bit CPU - part four
- 03 Dec 2013 » A node.js-powered 8-bit CPU - part three
- 02 Dec 2013 » A node.js-powered 8-bit CPU - part two
- 01 Dec 2013 » A node.js-powered 8-bit CPU - part one
- 21 Nov 2013 » MDB support for Go
- 30 Jul 2013 » What's new in pkgsrc-2013Q2
- 24 Jul 2013 » Distributed chrooted pkgsrc bulk builds
- 07 Jun 2013 » pkgsrc on SmartOS - creating new packages
- 15 Apr 2013 » What's new in pkgsrc-2013Q1
- 19 Mar 2013 » Installing SVR4 packages on SmartOS
- 27 Feb 2013 » SmartOS is Not GNU/Linux
- 18 Feb 2013 » SmartOS development preview dataset
- 17 Jan 2013 » pkgsrc on SmartOS - fixing broken builds
- 15 Jan 2013 » pkgsrc on SmartOS - zone creation and basic builds
- 10 Jan 2013 » Multi-architecture package support in SmartOS
- 09 Jan 2013 » Solaris portability - cfmakeraw()
- 08 Jan 2013 » Solaris portability - flock()
- 06 Jan 2013 » pkgsrc-2012Q4 illumos packages now available
- 23 Nov 2012 » SmartOS and the global zone
- 24 Oct 2012 » Setting up Samba on SmartOS
- 10 Oct 2012 » pkgsrc-2012Q3 packages for illumos
- 23 Aug 2012 » Creating local SmartOS packages
- 10 Jul 2012 » 7,000 binary packages for OSX Lion
- 09 Jul 2012 » 9,000 packages for SmartOS and illumos
- 07 May 2012 » Goodbye Oracle, Hello Joyent!
- 13 Apr 2012 » SmartOS global zone tweaks
- 12 Apr 2012 » Automated VirtualBox SmartOS installs
- 30 Mar 2012 » iptables script for Debian / Ubuntu
- 20 Feb 2012 » New site design
- 11 Jan 2012 » Set up anonymous FTP upload on Oracle Linux
- 09 Jan 2012 » Kickstart Oracle Linux in VirtualBox
- 09 Jan 2012 » Kickstart Oracle Linux from Ubuntu
- 22 Dec 2011 » Last day at MySQL
- 15 Dec 2011 » Installing OpenBSD with softraid
- 21 Sep 2011 » Create VirtualBox VM from the command line
- 14 Sep 2011 » Creating chroots for fun and MySQL testing
- 30 Jun 2011 » Graphing memory usage during an MTR run
- 29 Jun 2011 » Fix input box keybindings in Firefox
- 24 Jun 2011 » How to lose weight
- 23 Jun 2011 » How to fix stdio buffering
- 13 Jun 2011 » Serving multiple DNS search domains in IOS DHCP
- 13 Jun 2011 » Fix Firefox URL double click behaviour
- 20 Apr 2011 » SSH via HTTP proxy in OSX
- 09 Nov 2010 » How to build MySQL releases
- 29 Apr 2010 » 'apt-get' and 5,000 packages for Solaris10/x86
- 16 Sep 2009 » ZFS and NFS vs OSX
- 12 Sep 2009 » pkgsrc on Solaris
- 09 Dec 2008 » Jumpstart from OSX
- 31 Dec 2007 » Set up local caching DNS server on OSX 10.4