Building The SAP-3 Rev 2.0 PDF
Building The SAP-3 Rev 2.0 PDF
Dubbeld
Hoogvliet, The Netherlands
rolf-electronics, used on Reddit and Github
https://github.com/rolf-electronics/The-8-bit-SAP-3
-1-
The almost end result. Missing the interrupt controller and the 320*240 LCD screen.
-2-
Table of Content
-3-
Chapter 1, The Start
Ben Eater has made a very good series on YouTube on building an SAP-1 computer, were SAP
stands for Simple as Possible. The idea is to build a computer from first principles with integrated
circuits as available in the 1980’s. And I am going to deviating from that in building the SAP-3.
The basis of Ben Eaters design is extensively described by Albert Paul Malvino in his book Digital
Computer Electronics. A similar build can be found on
http://buildyourcomputer.org/aGuideToComputerScience.pdf. Also the book, An introduction to
microprocessors from D.K.Kaushik describes the SAP-1. This books as well as the book from Albert
Paul Malvino describes not only the SAP-1 but also the more capable SAP-2 and SAP-3.
The differences between SAP-1, 2 and 3 will be discussed in the next chapter.
The purpose of this document is to describe my design and built of the SAP-3 computer starting from
the SAP-1. This also means that nothing will be repeated or explained from the SAP-1 computer since
that’s all done by Ben Eater on YouTube.
It is a must to fully understand all aspects of SAP-1 before proceeding to SAP-2 or 3. Without the
basic knowledge of SAP-1 it’s not recommended to build SAP-2 or SAP-3. It can of course be done,
but troubleshooting will be far more difficult if not impossible and the learning experience is far less.
I also strongly recommend any builder to read and implement the lessons learned from
u/lordmonoxide on the Reddit sub channel of Ben Eater.
Last but not least, this is how I build it. Other solutions or directions to build SAP-3 are always
possible.
Finally is this a project for people without any experience in electronics, well most likely it is not going
to be easy for several reasons. Building an SAP-1 with all video’s and schematics as provided by Ben
Eater and all the info which you can find on the Reddit sub channel, it has shown that it’s challenging
for absolute beginners. This I base on the type of questions being posted. (not meant to be un-nice)
Upgrading to SAP-2 or 3 means that you have to start designing yourself, which is something totally
different than building from schematics made by others. There is not much detailed info to find on the
internet, at least not complete builds. You also might be faced with the situation that you can find some
sort of design, but cannot find the right components anymore. This would also force you to make
design modifications. On top of that when you find something on the internet what’s the guarantee that
the schematics are correct. How would you know how it’s supposed to work, if what you found and
build doesn’t work. For instance I posted my solution for an upgraded RAM, finding out later that there
was a mistake in the drawing. I built it properly since it worked. I made a first idea with some scribbled
notes and then started building. Than someone asked how did you build it? So I made a “proper”
schematic, unfortunately it had a mistake. (correction is included in this document)
Additionally as long as everything works what you found and built you’re ok. But once it doesn’t you’re
in the field of troubleshooting, which is a lot more difficult than just building. On top of that
troubleshooting without some equipment is really no fun. But what’s the chance that someone without
any electronics background has a descent oscilloscope, a multi-meter, and a logic analyser.
-4-
Chapter 2, SAP-2
In the book from AP.Malvino there is no summary of the characteristics of SAP-2. So I went through
the complete chapter and came up with the following:
Variable machine cycle,
Includes Jump instructions, Ben Eater’s SAP-1 computer already has this,
16 bit program counter, PC,
16 bit memory address register, MAR,
16 bit W-Bus,
2K ROM from 0000[h] to 07ff[h], 8 bit wide (to initialize the computer etc.),
62K RAM from 0800[h] to FFFF[h], 8 bit wide,
Memory Data Register MDR,
Instruction register 8 bit,
Temporary 8 bit register connected to the 8 bit ALU,
8 bit B and C register,
Two input ports. One HEX display, one serial,
Two output ports. One Hex keyboard entry, one serial,
ALU with arithmetic and logical operations, control by means of control bits,
Two flags zero and sign. Ben Eaters SAP-1 already has a zero and carry flag,
44 Instructions,
Maximum of 18 T states, which is mainly for the CALL instruction
In my build I did not implement the CALL instruction as described for SAP-2. The use of the Stack
Pointer mechanism in SAP-3 is far more efficient and more flexible to save a return address.
-5-
Chapter 3, SAP-3
In the book from AP.Malvino there is no summary of the characteristics of SAP-3. So I went through
the complete chapter and came up with the following:
Four additional registers D, E, H, L
16 bit Stack Pointer
More flags, carry and parity
33 extra instructions
Functionality to use register pair DE and HL
In general it can be concluded that the step from SAP-1 to SAP-2 is a lot larger than from SAP-2 to
SAP-3
Chapter 4, Add-ons
Additional functionality I built include:
Use of a device to program the RAM and ROM on-board. Especially for programming the
RAM another solution has to be found. With 32K RAM and 8 bits one has to toggle a quarter
of a million DIP switch settings to enter a program. Even programming one page, 256 bytes
would already be 256*8 ~2000 DIP switch settings. Debugging and altering is even worse.
An interrupt controller alike the 8085
An 2*16 LCD screen
An 320*240 LCD screen
Fixed crystal controlled clock speed possibility to allow for time critical applications.
-6-
Chapter 5, Choices I made in building SAP-2
The following choices are made by me in building the SAP-2. Beside the choices I made there is also
more detail on each of the subjects on why and how I did certain things.
And yes, on my build I have a small misalignment on the bottom breadboards and my top right
breadboard is mounted the wrong way around.
- Power distribution
I doubled up the power wiring, see below picture. This became necessary since the amount of IC’s for
the SAP-3 is far more than what is required for SAP-1. Without this the IC’s most distant away from
the feeding point of the board had a Vcc of 4.2 Volt. With the doubled up wiring Vcc went up to 4.8 to
4.9 Volt.
-7-
Every power rail has a 0.1 uF capacitor. On the middle of the board left and right I place two 1000 uF
capacitors and on the feeding point a 220 uF capacitor.
This is how I made the power distribution over the board. Power is fed from one point and distributed
over two strings, one for the boards on the left and one for boards on the right side. One shall not
connect the left side of the board with the right side, except the shown coupler. Reason for this is as
follows. By making a link on the bottom breadboards between the left and the right power rail a run
around loop is created for spurious signals.
Now a lot of posts can be found that power is the most important thing in building SAP computers, I
have I note on that remark. Now I agree that decoupling capacitors on each power rail is a good idea
as well as some larger capacitors spread over the board. That is even standard practise and adviced
by the manufacturers of these IC’s. And I supply 5.1 volt at the feeding point of the board in order to
have 4.8 to 4.9 volt on the IC’s on the bottom of the backplane. So I also try to stick to the rules.
But I did some testing with the circuits whereby I gradually reduced the power supply voltage, many
IC’s kept on working as low as 3.0 volt, some even could go lower. The LED’s went blank but on
turning the power up again the IC’s were still in the same state. So true, power is important but the
manufacturers have done a pretty good job in making IC’s which are not that sensitive to drops in the
power supply voltage. And yes that is not advised according the datasheet. But who uses these IC’s at
their maximum possible switching range. I think with these breadboard computers nobody.
Power distribution
-8-
- Wiring Technique.
There are many examples of how to make descent and clean wiring on the breadboard. A good
example is given at:
https://www.reddit.com/r/beneater/comments/g9c2do/how_to_make_your_build_clean/
However when changing from a 4 bit addresses to essentially a 16 bit address mode a more compact
wiring design is a must. Also I will put two registers on one breadboards, so a more compact wiring
technique is required. Due to all this one has to leave the path of flat wiring, it just is too much wires
which all need there place. Raising the wires off the board is a possible solution. Below shown are two
example pictures of, off the board wiring. But whatever you do neat proper wiring pays off when you
need to troubleshoot. Also a good choice of wire colours can help not making mistakes or make
troubleshooting easier.
-9-
Also don’t strip off a too
short part of the wire
insulation. Note the
difference of the
stripped wire between
above and below wire
end on below picture. A
longer stripped off wire
makes a more secure
connection.
- 10 -
- TTL Logic Family
The SAP-1 computer from Ben Eater is built with 74LS logic, I built the kit from Ben Eater using these
74LS logic IC’S. Since the amount of IC’s would be roughly doubled for the SAP-3, I wanted to reduce
power consumption. Initially I changed to 74HCT logic, which wasn’t a good idea, but I had many on
stock. The better choice for new builds is to go for 74HC logic, for the following reason.
The left most column is typical for 74HC, the column next to it for 74HCT. The 74HCT family has a
very high noise tolerance for deviations from high level voltage states. Note that Vt is typically the
voltage at which the state is changed. 74HC has a better balance for high and low level noise
tolerance and is therefore the better choice.
After having the SAP-2 completely build I could not get it to operate stable, spurious signals made it
very unreliable, the cause of this and the solutions are described in Chapter 9, Troubles with my SAP-
2 design and Solutions
Beside the solutions as described in chapter 9, I also changed from 74HCT to 74HC logic. After all
these modifications the computer worked like a charm.
A clearer picture than the one above can be found by googling: TTL logic levels.
- 11 -
- Variable machine cycle, Ring counter versus Step Counter (*)
The original SAP-1 design uses a ring counter for the machine states and logic gates for the control
logic. A ring counter is different from a step counter that on a 4 bit ring counter only 1 bit is high at the
time.
A step counter is more efficient since you can have more steps with the same amount of bits. The
advantage of a ring counter is that the signal can directly be used to control the computer. With a step
counter a decoding step is required to show in which state the computer is in. Ben Eater in his design
built in the 74LS138 demultiplexer to indicate in which state the computer is. Ben Eater built the
computer with 5 states, T0 to T4. This can be expanded to T7, by altering the reset which is connected
to pin 10 of the 74LS138 to pin 7.
When the control logic is not made from logic gates as described in the book by A.P.malvino but
instead an EEPROM is used it’s more logic to use a step counter since these can directly be
connected to the address lines of the EEPROM.
Picture of the step counter I built. The IC is a 74HC161. I did not built the decoder to reduce IC’s so
my steps count 0000 0001 0010 0011 0100 etc., indicated by the 4 green LED’s. Picture
indicates T-state = T7
Additionally I included a step counter reset, this has the following advantage. Most instructions require
between 6 and 8 T states, the big exception is the CALL statement which uses 15 T states. Not
implementing a step counter reset would mean that almost every instruction would carry out on
average 8 no operation steps. This is very ineffective and slows down the computer considerable.
- 12 -
- Page Register and Program Counter (*)
The SAP-1 and the computer from Ben uses a 4 bit program counter allowing 16 bytes to be
addressed. Although nice for educational purpose 16 bytes severely limits the possibilities of the
machine.
SAP-2 and SAP-3 use a 16 bit program counter allowing 64K (65.536) bytes to be addressed
In my design I use a PR and PC standing for Page Register and Program Counter. Each page has
256 program counter steps and in total there are 256 pages. This is called paging or a segment
register. The program counter indicates an address relative to the page as indicated by the page
register. Both the PR and PC are 8 bits wide.
Now this sounds the same as having 1 program counter with 16 bits opposed to two counters with 8
bits, but there is a difference. Since I have an 8 bit bus I cannot send all 16 bits at ones. So when
addressing memory I cannot send all 16 bytes at once, first the PR must be send and in the next state
the PC value. So first the page is addressed and then the relative value from the program counter.
This gives the possibility of relative addressing, meaning when I stay on the same page there is no
reason to send the PR to the MAR. I have not implemented this yet, but it would double up the speed
for instruction requiring access to the memory. For the PR and PC 74HC161 IC’s are used.
- 13 -
- 16 bit memory address register, MAR
This is obvious a 16 bit addressing range from the PC, or PR+PC, requires a 16 bit MAR. In my design
I made a split between MAR-H and MAR-L. MAR-H being the higher byte of the address and MAR-L
the lower byte. Thus PR addresses MAR-H and PC addresses MAR-L. Both MAR-H and MAR-L use a
74HC377 IC. On below picture the MAR-H and MAR-L are shown.
Furthermore there are DIP-switches for manually entering an address in program mode. These are
only foreseen for the MAR-L address. My address mapping is such that the RAM section starts from
address 0x8000. The address multiplexers are wired in such a manner that only for page 0x80
addresses can be entered manually. I found it totally irrelevant to make also DIP switches for MAR-H, I
never had any intention to toggle in that many program steps. Entering one page, 256 bytes, with DIP
switches is really sufficient. The Green LED’s show the MAR-L status. The pushbutton on the right is
the PROG/RUN switch with the two LED’s, red for program mode and green for run mode.
Since the addition of the Arduino for programming the RAM the PROG/RUN switch and the DIP
switches for manually entering a program are hardly used anymore. One could decide to skip the
manual programming mode completely saving space on the breadboards, IC’s and wiring, in my case
the complete lower breadboard of below picture.
Picture on the top right shows the Memory Address Register, MAR, and the DIP switches for
manually entering an address location.
- 14 -
- 8 bit W-bus (*)
When I started SAP-1 very quickly I made a plywood board to mount all the breadboards. I anticipated
on more breadboards for SAP-2 and SAP-3, but only when I started to look into it in detail I found that
in SAP-2 and 3 the bus is 16 bit. As stated earlier once mounted on the backplane the breadboards
cannot be removed anymore without seriously damaging them. So I had an 8 bit bus, as can be seen
on previous picture. Ok through away the board and start over again.
Hell NO, I am Dutch and I’m not going to through away 20 breadboards !
And why should you have a 16 bit bus, all registers, the memory and ALU are 8 bits. Only the PC and
MAR are 16 bit. And in using relative addressing there would hardly be any difference.
For the rest the bus is standard and made exactly as per Ben Eater design.
Data flow
With an 8 bit wide RAM/ROM only 8 bits can be stored and that’s also the format of all registers. So by
using the lower 8 bits of the bus data can be transferred. Only if the registers and ALU were 16 bits an
MDR would be required to make the translation from 16 to 8 bit. But making registers and memory 16
bit is no longer an 8 bit computer.
Address data
For a JMP instruction with 2 bytes for the address an 8 bit RAM/ROM and a 16 bit bus and PC which
can only be loaded in one time with the complete address also an MDR would be required. But since I
have the PR and PC separate both for register out and register load signals, an MDR is not required in
my design.
- 15 -
- ROM and RAM memory (*)
My memory address is mapped as follows:
0x0000 to 0x7FFF, is for ROM,
0x8000 to 0XFFFF, is for RAM.
The memory data is 8 bit wide. With IC’s which were readily available, I build 8K ROM and 32K RAM
with respectively an 28C64 and a 62256.
Also here a DIP switch can be seen for manually entering data, once I built the Arduino interface to the
memory this has hardly been used anymore.
Unlike the 74LS189 as used by Ben Eater the 62256 has common I/O. If the data LED’s should
indicate the data output for the selected address, than the 62256 must have its output enabled. This
however would conflicts with the data signal from the multiplexer. Leading to a situation were 2 outputs
drive a common line, which is an unwanted situation. This is the reason for the extra created bus,
indicated on the drawings as “second bus”. A 74HC245 separates the output from the 65256 from the
output from the multiplexer.
If data needs to be written into the RAM, either manually or by means of RI, the following happens:
the OE1 signal is set to low, now the data from the multiplexer is on the second bus,
the OE of the 65256 is made high, setting the I/O to input,
the WE of the 65256 is pulsed to store data on the secondary bus in RAM.
- 16 -
Memory control (later to be made in Ki CAD)
( there is a mistake in above drawing, OE RAM/ROM and the signals connected to pin 8 of the NAND
should be reversed )
- Registers
There are two kind of registers; special purpose and general. Normally special purpose is not available
for the programmer and general purpose is available to the programmer. On this some sort of rule
there are exceptions like MCU’s were having access to special purpose registers is quite common.
For space consideration the A and flag register are built on one breadboard. Also the B and C register
are built on one breadboard. The instruction register is combined with the step counter and flag
masking register, which purpose will be explained later.
Additionally the flag register is connected to the bus via a 74HC245, this makes it possible to store the
PSW. PSW is the Program Status Word. All register use the 74HC377.
- 17 -
A and flag register. The IC left to the IC marked “S Z C”is the 74LS245 for connecting the flag register
to the bus. The flags register has a control signal for either getting input from the ALU or from the bus.
B and C register
Instruction Register
- 18 -
- 8 bit ALU with 74HCT181,
For the ALU I choose to use a 74HC181, kind of an obvious choice since there is not much to choose
from. Each IC handles for 4 bits so you need two, and the carry bit connect the lower 4 bits (nibble)
with the upper 4 bits.
The carry flag to the control EEPROM’s comes from the ALU with the higher 4 bits. The sign is the
MSB and the zero-flag I made identical as done in the built from Ben Eater with some additional logic
circuits.
Picture of the ALU, the green LED’s on the bottom left are the ALU output
- 19 -
- Two strange beasts, number 1: Flag masking
On the page with the instruction register or the picture were I explained the built on the step counter
there is an IC labelled FM. Now this is not an FM radio, it stands for flag masking. For what purpose
did I built this.
One of the charms of the 8 bit computer is that everything fits nicely on a breadboard. And Ben Eater
supplied a program suitable to program an array of EEPROM’s 28C16/64/256. So my plan was to stay
with that program and use an Arduino Nano.
Now as you might have noticed in one of the video’s Ben gets a warning, saying something like
“Low memory, stability errors might occur”. Which is computer language for “I cannot do this”. Ben’s
solution was to use the program area of the Arduino Nano by means of a different array definition
being: const uint8_t array_name PROGMEM. Problem solved.
Now Ben had two flag, 8 T states, 16 instructions and a 16 bit control word. And for n flags he need
2^n copies of the database.
In order to build SAP-3 I needed 4 flags, 16 T states, basically 256 instructions since the instruction
register is now 8 bits and with 6 control EEPROM’s I would have 48 bits on the control word.
And then it all goes wrong. The array I had to build to program the EEPROM became way too big,
could not be handled by an Arduino Nano, also switching to an Arduino UNO or Mega did not help.
They all could not handle the extreme large array. So I had to think out a solution, which I called FM.
Now it gets complicated to explain, and that’s why we engineers use DRAWINGS. But I will give it a
try.
I route the flags first through a multiplexer, a 74HC157. The select line of the multiplexer is connected
to the MSB of the instruction register. The A inputs of the 74HC157 are connected to ground the B
inputs to the flags. The outputs go to the EEPROM address lines. The result is the following.
My first 127 instructions were the MSB=0 select S=0 (the A inputs) and as such the EEPROM address
lines are zero always by definition. The first 127 instructions are defined as non-flag related. So in my
design for the non-flag related instructions the flag lines to the EEPROM are zero always.
This avoids me making multiple copies of the data array as Ben does, this helped a lot.
My flag related instructions are from 128 upwards, these are a handful and for them I have to make
multiple copies, but that's much easier to handle.
Additionally I skipped the idea of swappable EEPROM, before I program an EEPROM I have to
explicitly set which one. So each step in the microinstructions is only 8 bits. And with that it fits nicely
in an Arduino Nano.
Hopefully my writing is clear, it's always better to use drawings since that's the engineering language.
- 20 -
The flag masking 74HC157.
Note the LED’s in-between the step-counter and instruction register. This is the current instruction in
the IR. The red LED is the MSB, when it lights up it’s a flag related instruction and vice-versa.
Each instruction starts with fetching the next instruction which is pointed to by the PR and PC. Per
instruction I need to increase the program counter with 3, since each instruction is three bytes.
The instruction register is now loaded with the next instruction, this is always the same for each and
every instruction. Except one which has to do with the interrupt controller.
- 21 -
Below as example are the micro instructions for the LDA command.
T0 T1 T2 T3 T4 T5 T6 T7 T8 T9
ins_0, ins_1, ins_2, pco&mal, ro&prbi&CE, pco&mal, ro&mal, prbo&mah, ro&ai&CE, scr,
ins_0 = pro&mah
ins_1 = pco&mal
ins_2 = ro&ii&CE
T0, T1 and T2 are for fetching the instruction. I defined the microinstructions in the array with ins_0,
ins_1 and ins_2 for simplicity. Now a typical LDA instruction would have three bytes stored as
follows:
the instruction
the page register to jump to
the pc value to jump to.
The purpose for the PR buffer register becomes clear now. Once the instruction is loaded at T2 the
program counter is increased and now points to the PR from the LDA instruction. In T4 the program
counter gets out to the bus and is loaded into the MAR-L.
But you cannot say now RO&PR-load, meaning you want to load the PR of the LDA instruction into
PR, if you would do that than the PR is suddenly pointing to a completely new instruction on another
page which has nothing to do with the next step in the original instruction getting the PC value. And
that’s the function of the PR-buffer.
Therefore:
program counter is increased pointing to PR from the LDA instruction
PR from LDA instruction is outputted from RAM/ROM to the bus and stored in the PR-buffer
program counter is increased pointing to PC value from the LDA instruction
PC is outputted from RAM/ROM to the bus and stored in MAR-L
PR-buffer is outputted to the bus and stored in the MAR-H
MAR points now to the address which needs to be loaded in the A register
RAM/ROM value is outputted to the bus and loaded in the A register.
- 22 -
- 44 Instructions
This is a listing of all SAP-2 instructions
- 23 -
- The CALL instruction 18 T states in SAP-2
To start with I am not going to explain why you need CALL instructions and how they work together
with a stack, this is all nicely explained by Ben Eater in the follow video.
The return function works identical, only then the values stored in memory location 0xffff and 0xfffe are
loaded into the PC.
The next pages provide this in detail with as last the setup of the microinstructions as I think it is
supposed to work.
30 etc 120
- 24 -
25
26
27
Chapter 6, Additional minor Improvements SAP-2
No LED’s for the control bits and no inverters, this saves a lot of wiring with 48 control bits
Moving the control Logic to the middle of the board. This will reduce control wiring lengths
compared to having them mounted on the bottom of the backplane.
Looking from top left to bottom left the modules are ranked as follows:
Clock module
ROM RAM memory
Control register
FM / Step counter / Instruction register
In hindsight to reduce the control wiring even further I think the following sequence is even better:
Clock Module
Control register
ROM / RAM memory
FM / Step counter / Instruction register
I decided not to have reset signals to the registers this would save wiring.
Resetting the registers could be done on start-up via the ROM. There is one sneak, this works
perfectly fine for the A, B and C register and also or the HEX display.
However:
When the build was complete and I started testing, the computer was not working correctly. The step
counter as well as PR and PC still had a connection to the rest button, the instruction register however
not. So at start the IR would show a random instruction, if this instruction is not defined than control
signals will be undefined and weird things happen. Having resetted the PR and PC does not help if the
first things defined in the IR is not: PR|MAR-H and next PC|MAR-L and consequently RO|II.
The solution was to write on the EEPROM for each possible instruction the first above three steps.
Problem solved!
Von Neumann Cycle, first get the instruction. I did not think about this when I deleted the reset signal
on the instruction register. And I had to find a solution since an 74HC377 has no reset and I did not
wanted to go back to two 74HC161’s. This would use more breadboard space.
A modification I still want to do is installing HEX Schmitt triggers on the clock signal and separate the
clock from the left side of the board from the right side. Purpose of this is to improve the quality of
the clock signal. This is one of the things required in order to have the computer run on higher clock
speeds.
28
Improve brightness HEX display
The original display as built by Ben Eater is faint it helps placing the red foil on top of it, but still it’s not
the brightest display. Main reason for that is that an EEPROM is not supposed to drive a 7 segment
displays. This can very clearly be seen on the data output LED’s of my memory module. When RAM is
selected the LED’s burn a lot brighter than when the ROM (EEPROM) is selected.
I also wanted to build the complete display on one breadboard. I used a MAX 7219CNG in
combination with an 16F874 MCU. Beside the switch for one and two complement I also added two
pushbuttons for brightness control. It makes a lot of difference driving the LED’s with an
MAX7219CNG, especially since I also used a new 7 segment display a super bright version. This
makes the display very easily readable also during daytime. Initially I had the max current to the 7
segment display adjusted too high and the display was really (really) bright. In doing so I burned a
couple of segments on the display. After adjusting this at the MAX7219CNG, which means changing a
resistor, it worked like a charm. And obviously I had to buy a new display.
Disadvantage is the use of a microcontroller, making it less easy to reproduce for others. I have an
EASYPIC V7 MCU development boards and with that it’s a walk in the park.
The IC marked “out”is the 16F874. The IC next to the 7 segment display is the MAX7219CNG.
Currently I still have an 74HC377 connects the bus with the 16F874. Reason for that is that I only had
a 4MHz crystal on stock, making it too slow at higher clock speeds. In future I might change this,
although there is no real good reason. It works very well.
29
Use of less and a different setup of the LED’s
I did several posts on my 8 bit computer build, this one was popular. The original LED’s as used by
Ben Eater uses the round 8mm LED’s with separate wiring. This takes up a lot of space on the
breadboard and also more wiring. They also do not nicely line up with the breadboard.
So I took 2*5 mm square LED’s, ordered some 8*270 ohm resistor array, placed the resistor array
upside down and soldered the LED’s to the array. Add one additional ground wire and done. And they
line up perfectly with the breadboard,
30
Chapter 7, Starting to build
First I made a layout of how I thought the modules had to go on the backplane, nice word for a big
chunk of plywood. It looked something like this.
( to be made in KiCad )
31
I re-arranged the stack pointer and had more control signals and after some proper counting, I ended
up with this.
( to be made in KCAD )
I made a mistake with the MAR for a 16 bit address you need 16/4 = 4 74HC157’s, and not 2.
32
Below is a summary of IC count for the SAP-2
Beside the lessons learned from u/lordmonoxide on Reddit sub channel the 8 bit on how to build the 8
bit breadboard computer and some other tips which can be found in this document in chapter 5, there
is one other important thing while building the SAP-2 and 3. And that is TESTING it cannot be said
enough to test as much and extensive as possible. Once you connect everything together like
connecting the modules to the bus and connecting the control signals from the EEPROM’s and then
you really start testing, well it makes debugging a lot harder. The next paragraphs deals with testing.
Below a picture of my work bench, and it really helps a lot to have this once you need to troubleshoot.
33
Testing
To start, unfortunately of all technical disciplines electronics is one of the least forgiving. A civil design
will still work even if you miss it by 5mm (or more, sorry civil engineers). In electronics 1 mistake =>
zippo result. You have two options, get another hobby or get used to this and accept the
consequence. The consequence is: troubleshooting is part of the job, no way around it.
Next I strongly suggest that you read and fully understand below article:
http://web.mit.edu/6.101/www/reference/TheArtofDebuggingCircuits.pdf
Below I took from RC2014.co.uk ( with permission ), I edited it to remove what’s not applicable but I
think this can be very useful.
Take a look at every connection. Correctly wired and properly plugged in the board.
Are the correct value resistors and capacitors fitted. Check the individual assembly guides for visual
confirmation.
Are the correct ICs in the correct place. Note that 74LSxx and 74HCTxx chips are interchangeable but
not with 74HCxx.
Is the orientation of the ICs correct. Are the IC pins all seated cleanly in the breadboard. Make sure
none of the pins are bent underneath the chip.
If powering with 5v from a lab power supply set the proper max current, should not be too low,.
Is nothing getting way too hot or is smoke part of your build. Turn off the power directly
34
Trouble shooting with a multimeter
There are two types of test that are easy to do with a multimeter; continuity test and voltage test.
Continuity test
Set your multimeter to the continuity or beep setting. If you’re not familiar with how to do this, consult
the documentation that came with it, or use the internet. This is a very useful mode to see if you have
any doubt that something might not be properly connected. Power must be switched off for this.
Measure Vcc on all chips on the board. This should be above 4.7 Volt.
On the RC2014-Z80 Google Group, a simple signal tracer circuit can be found. I don’t know what
equipment you have but I’ll assume you have a multimeter that can read DC voltage.
A signal tracer can be used to check if signals travels from A to B. An LED with current limiting resistor
can also be used but less suitable for higher frequency signals. There is no longer much difference
between pulsing and permanently on.
One of the checks you want to do is to check if there is a clock. Unfortunately multi-meters are not
really suitable to check clock signals. So you need to construct an AC probe to read higher frequency
clock signals, once your build is stable and you want to increase the clock frequency. Luckily, this is
easy and cheap to do.
You take two 100k resistors, a 10 nF capacitor, a 1N4148 diode and to construct the following signal
tracer:
The component values are not critical at all. If you have 10k resistors, use 10k resistors. If you have a
100nF capacitor, use 100 nF.
Connect the ‘signal‘ end to the CLK signal, or any other high frequency signal, and GND to GND. Set
your multimeter to DC volts. The meter will read more than 1 V if there is a changing signal on the CLK
and much less (it should show something close to 0V) if there is a static signal (either 5V or 0V).
These tests above will give you more information where the problem lies. Of course, if you have an
oscilloscope, use that to actually look at the signals instead of using the probe.
35
Logic Probe
Logic probes are also a very useful tool for tracking signals. The better logic probes also have a pulse
signal indication
It cannot be said enough test each module very very careful before you connect everything together to
the bus and control signals.
Don’t proceed to the next module before the module you are working on is 100% correct. So an
approach like “I did not build it according the design from Ben Eater but somehow it works” will bring
you nowhere. I have seen things like “well I placed a 10 uF capacitor on RI and now it works”. NO, this
is incorrect. If you don’t know exactly what you change on a schematic than don’t do it.
Remember if you have an error: THE CIRCUIT BEHAVES EXACTLY AS YOU BUILD IT, this however
can be different from what you expect how it should behave. This is also a good analysing tool, which
modification do I need to make in the schematic to see it behave as it does.
Make a temporary bus with nothing else connected to it except bus status LED’s. Try writing values to
the bus and ty to load values from the bus. See if the CE signal works properly. Try this at higher
frequencies. If you don’t have a logic analyser place a digital camera above the computer and play it
on your PC to see if it works correct.
Once you have tested all the modules, they can be connected to the bus. Use jumpers for all control
signals and make them inactive. And then step by step start testing.
Bring a value from PC on the bus, try to write it in RAM. Try to write it in the A-register. Use several
values like 0x55 or 0Xf0. Try signal flow in all possible directions. Try to build a counter with the A
register, the temporary register and the HEX display.
And again work the problem if you have one, don’t proceed to the next step.
Then replay the 28+14 calculation as shown by Ben Eater, by manipulating the control wires with
jumpers.
And for SAP-2 this is a serious amount of testing. Only then as last step connect the control wires.
36
Chapter 9, Troubles with my SAP-2 design and Solutions
I did all the testing as previously described. Then I connected the control signals, entered a simple
program and executed this in manual mode step by step. Troubles; random behaviour and total not
stable.
Entering two values in RAM, and entered a simple program to add the two values. Well 5 + 5 showed
up as 7, 88, 25 etc and sometimes 10.
After some analysing I found the result, below scope screenshot was the root cause.
The 8 bit computer is controlled by means of the data outputs from the EEPROM, nothing new. But
when their address change the data outputs show glitches, they don't stay stable while you would
expect that in many cases. This happens on ALL data output signals of the EEPROM’S. The glitches
are short but long enough to cause problems. That's however is according the datasheet, when the
address changes the output is not defined for some time, while OE is low. This however causes many
issues since these lines control the computer. Top pictures and bottom-left picture are screen dumps
from my scope of such glitches.
As example the glitches would activate all 74HC245 to write to the bus. I simulated this by making all
OE signals of the 74HC245 IC’s low with jumpers. First off all this obviously causes total confusion on
the bus, but as side effect the power consumption of the computer increased from 0.7 amp on average
to over 2.1 amp.
I have installed pull-up resistors on active low signals and pull down resistor on active high signals on
the data output lines of the EEPROM’s.
The result is shown on the picture on the bottom on the right. They helped a lot.
37
Low Pass filters
The installation of pull-up and pull down resistor helped a lot but still the design was not 100%
stable. After more testing and measuring with the scope I installed low pass filters on the following
control lines:
Count Enable of the Program Counter
Instruction In of the instruction register
Count Enable of the step counter
And later on the control signals of the stack pointer
( to be made in KiCAD )
Above is an example how to build a low pass filter, in my build I used the 74HC14 which are Schmitt
trigger HEX inverters. They give a more clean output signal.
There is a very interesting video on You Tube from Dave from EEVblog on bad clock signals send to a
74HC161 counter.
https://www.youtube.com/watch?v=Ht48vv0rQYk&t=277s
After installing the pull-up and pull down resistor, add the low pass filter and changed from 74HCTxxx
logic to 74HCxx the computer worked like a charm.
38
Chapter 10 , Choices I made in building SAP-3
Stack Pointer
A stack is a portion of the memory and it’s a sequenced storage, the stack pointer is used to store or
retrieve data from the stack, since it point to the current location on the stack.
In contrary to the step counter and the PR and PC the stack pointer should be able to count up and
down. Since it must be able to execute PUSH and POP instruction. PUSH is bringing something to the
stack and POP is getting something from the stack. This something can be a return address but also
the content of any of a general purpose registers.
An instruction like PUSH_A, places something on the stack and then decreases the value of the stack
pointer. An instruction like POP_A, increases the value from the stack pointer and gets a value from
the stack
As said this increasing and decreasing of the value of the stack pointer can only be done by a device
which can count up and down, for this purpose I used the 74HC191, and since it’s 4 bits I needed four
of them. Due to the fact that I have an 8 bit bus I splitted the stack pointer in a SP-H and an SP-L,
identical to PR and PC.
Now assume you want to PUSH something on the stack. The sequence I have chosen is to write to
the stack and then decrease the value of the stack pointer, the other way around is also perfectly
possible. The following micro instruction steps must be taken.
Wanting to write something to the stack means that the value from SP-H and SP-L must be loaded
into MAR-H and MAR-L. Once that’s done the MAR points to the bottom of the stack. A stack always
works top down. You start at a certain address and while storing data you decrease the value of the
stack pointer. You could build a stack pointer which works the other way around, but it’s not common
practise.
So we were at the point where the MAR was loaded with the bottom of the stack. Now you can use the
RI command to write for instance the value of the A register to the stack. And as last action you have
to decrease the value of the stack pointer
39
THE instruction with the most micro instruction CALL
Now how did I build the CALL instruction in SAP-3, again this is what I dreamed up none of the books
referred to provides detailed info on this.
Let’s say the instruction is CALL xx yy, whereby xx is the page and yy the pc value to jump to.
1/ Fetch the instruction and load xx into the A register and yy to temporary register. PR and PC now
indicate the return address.
4/ Now load the value of PR into RAM, read stack. The return address page is now pushed on the
stack.
5/ Load MAR-H with SP-H and MAR-L with SP-L. This can be optimised by deleting load MAR-H with
SP-H, this is already done in step 2. This would limit the stack to 128 return addresses, but that is
more than enough. I have to see the first program built on a breadboard computer with a program
using a nested level of 127. Ain’t gonna happen.
7/ Now load the value of program counter into RAM, read stack. The return address program counter
value is now pushed on the stack. The complete return address is now saved on the stack.
8/ Final step load the page register with the content of the A register and load the program counter
with the content of the temporary register. This actually executes the jump to address xx yy.
For this instruction I needed 15T states as can be seen on the next slides, T0 to T14, which brought
the necessity of a 4 bit step counter. But it is 3 states more efficient than the CALL implementation in
SAP-2. And basically nested CALL can be executed 128 levels deep. After this page three pages are
shown on the CALL instruction with one page detailing the micro instructions.
( to be detailed later )
40
Abbreviations used
41
General functionality CALL instruction
42
The micro instruction
43
Indirect addressing
For the B and C register I made a couple of separate instruction in order to enable indirect addressing.
Here you can find a good explanation of what indirect addressing is.
( to be detailed later )
44
My instruction set for the SAP-3
Following is my instruction set definition, 110 instructions. Now this sounds way over the top and an
incredible amount of work. Well once you get the hang of it in writing micro instruction you can copy a
lot from previous instructions for quickly making a new instruction. Obviously you can only do this if
you fully understand the computer, how to program the EEPROM and what each instruction exactly is
supposed to do. But that’s the advantage of designing and building things yourself, that you know.
In the examples were 0's are shown, they are manditory for the hardware to be there
45
// ( I might one day line this up all nicely )
46
// Block 3 Logic instructions
#define CMA 0b00100000 // CMA 0, 0, Complement the accumulator
#define ANA_B 0b00100001 // ANA_B 0, 0, AND accumulator with content B register
#define ANA_C 0b00100010 // ANA_C 0, 0, AND accumulator with content C register
#define ORA_B 0b00100011 // ORA_B 0, 0, OR accumulator with content B register
#define ORA_C 0b00100100 // ORA_C 0, 0, OR accumulator with content C register
#define XRA_B 0b00100101 // XRA_B 0, 0, XOR accumulator with content B register
#define XRA_C 0b00100110 // XRA_C 0, 0, XOR accumulator with content C register
#define ANI 0b00100111 // ANI 0, 0b11110000 AND A register with 0b11110000
#define ORI 0b00101000 // ORI 0, 0b11001100 OR A register with 0b11001100
#define XRI 0b00101001 // XRI, 0, 0b10101010 XOR A register with 0b10101010
#define RAL 0b00101010 // RAL, 0, 0, Rotate ACC left
#define RAR 0b00101011 // RAR, 0, 0, Rotate ACC right CANT BE DONE WITH 74HCT181 !!
#define RALC 0b00101100 // RALC, 0, 0, Rotate ACC left with carry, maybe hardware change
#define RARC 0b00101101 // RARC, 0, 0, Rotate ACC right with carry, maybe hardware change
// 0b00101110 //
// 0b00101111 //
47
// Block 5 Various instructions and instructions related to indirect adrressing
#define JMP 0b01000000 // JMP, A, B, Jump to adress location with Page register=A and
Program Counter=B.
#define NOP 0b01000001 // NOP, 0, 0, No operation
#define HLT 0b01000010 // HLT, 0, 0, Halt the computer
// 0b01000011 //
#define LXI_B 0b01000100 // LXI_B A, B, Load Register Pair BC. B-register=A C-register=B
#define INX 0b01000101 // INX 0, a, Increment B register with a. No change C register
255 indirect adreses is sufficient
#define DCX 0b01000110 // DCX 0, b, Decrement B register with b. No change C register
255 indirect adreses is sufficient
#define MOV_AM 0b01000111 // MOV_AM 0, 0, Move content adress pointed at by BC-register pair to
A register
#define MOV_MA 0b01001000 // MOV_MA 0, 0, Move content A-register to adress point at by BC
register pair
#define MVI_M 0b01001001 // MVI_M 0, a, Move immediate value a to location pointed at by BC
register
// 0b01001010 //
// 0b01001011 //
// 0b01001100 //
// 0b01001101 //
// 0b01001110 //
// 0b01001111 //
48
// Block 7 Instructions related to the 320*240 LCD screen
#define LLCD_C 0b01100000 // LLCD_C, 0, a, content a to large LCD as command
#define LLCD_P 0b01100001 // LLCD_P, 0, a, content a to large LCD as parameter
#define LLCD_AC 0b01100010 // LLCD_AC, 0, 0, send content A register as command
#define LLCD_AP 0b01100011 // LLCD_AP, 0, 0, send content A register as parameter
#define LLCD_BP 0b01100100 // LLCD_BP, 0, 0, send content A register as parameter
#define LLCD_BP 0b01100101 // LLCD_CP, 0, 0, send content A register as parameter
// 0b01100110 //
// 0b01100111 //
// 0b01101000 //
// 0b01101001 //
// 0b01101010 //
// 0b01101011 //
// 0b01101100 //
// 0b01101101 //
// 0b01101110 //
// 0b01101111 //
49
// Block 9 Jump instructions flag related
#define JZ 0b10000000 // JZ, A, B, Jump if zero flag set to PR=A PC=B
#define JC 0b10000001 // JC, A, B, Jump if carry flag set to PR=A PC=B
#define JP 0b10000010 // JP, A, B, Jump if positive to PR=A PC=B
#define JNZ 0b10000011 // JNZ A, B, Jump if zero flag not set to PR=A PC=B
#define JNC 0b10000100 // JNC A, B, Jump if carry flag not set to PR=A PC=B
#define JM 0b10000101 // JP A, B, Jump if negative to PR=A PC=B
// 0b10000110 //
#define ADDC_B 0b10000111 // ADDC_B 0, 0,
no flag set. Add content B register to the A register
with carry
#define ADDC_C 0b10001000 // ADDC_C 0, 0, no flag set. Add content C register to the A register
with carry
#define SUBB_B 0b10001001 // SUBB_B 0, 0, no flag set. Subtract content B from the A register with
borrow
#define SUBB_C 0b10001010 // SUBB_C 0, 0, no flag set. Subtract content C from the A register
with borrow
#define ADDC_M 0b10001011 // ADDC_M A, B, no flag set. Add content memory location A,B to
the A register. With carry
#define SUBB_M 0b10001100 // SUBB_M A, B, no flag set. Subtract content memory location A,B
from the A register (A-M). With carry
// 0b10001101 //
// 0b10001110 //
// 0b10001111 //
50
Chapter 11, Add-ons I built while building the SAP-3
This chapter describes the additional functionality I built on top of the SAP-3.
Fixed crystal controlled clock speed possibility to allow for time critical applications.
( to be detailed later )
51
Fixed crystal controlled clock speed
Once you need fixed time intervals in your computer, like a fixed 1 second delay for an application or a
10 msec delay to control a display. Then it’s not very practical to do that with the standard clock circuit.
Try setting the potmeter every time at the exact value. Solution, I took a 2MHz crystal oscillator and
divided the signal with an 74HC161.
I however have one problem in my build, above 220 KHZ the computer goes ballistic, with the AWG I
found that I could increase the clock frequency to 500 KHz if I reduced the duty cycle to 25%. On
above hand drawn schematic that’s the function RC circuit with diode, the diode eliminates negative
undershoot. The additional Schmitt triggers are required to clean up the signal, two could be removed ,
but for now I just linked them.
In the mean time I found the reason for the limit of 220 KHz.
Basically I followed Ben Eater Video for the LCD display on the 6502 and build it in the 8 bit.
( to be detailed later )
52
The 320*240 LCD screen
Before I go into detail how I build this, first below photo impression. I had to do some serious
debugging to get this to work, needed my scope, logic analyser and logic probe. Well in the end it
worked. I found the display’s, three of them on, Dutch Ebay (Marktplaats) for 15 Euro. So I thought
that’s interesting to connect to the 8 bit computer.
The display itself is a 320*240 display, and from what I could trace were manufactured by EPSON,
type number is a AG320240A. This display is controlled by a SED1330, control looks a lot like the
control on the 2*16 LCD controller with the exception that the SED1330 is more complicated.
To give an idea:
Initialization is almost 90 instructions,
Clearing the graphical and text address area another 30 instructions. There is no instruction
like on the HD44780, used for the 2*16 LCD screen, to clear the display. You have to write
some code for that.
Writing the above message 150 instructions, this however was done inefficiently. I want to
write a subroutine to do this more efficient.
And obviously I had to write some instruction to control the display, like:
- LLCD_C value, write value as command to the large LCD
- LLCD_P value, write value as parameter to the large LCD
53
An interrupt controller alike the 8085
The final “piece the resistance”, building an interrupt controller. I first tried using an Arduino Nano
since they fit nicely on the breadboards, but I ended up in finding out that I did not had enough I/O. I
could have used a shift register to create more I/O but that would also consume too much space. I
also needed to place the four buttons for the interrupt control lines somewhere. And on the far right is
an IC for building the zero flag of the ALU. That left me little options, I have quit a range of MCU’s on
stock and I have the EasyPIC v4 and V7 MCU development kit. So it was kind of logic to use an MCU.
I know the 16F877A quit well, having it used on other electronic projects. It’s a quit versatile device.
10 instruction
20 etc
30 Scan emergency button and if pressed do something really urgent
40 etc
… etc
500 Conditional CALL “do something else”
510 etc
… etc
600 Back to 10
The program runs instructions from 10 to 600 and sometimes executes the CALL “do something else”.
In the loop at instruction 30 it checks if the emergency button is pressed, and if yes, it switches on a
big red warning sign saying “STOP RIGHT NOW !”
54
But, the program does this emergency button check only when it is at instruction 30. If the emergency
button is pressed when the program is at instruction 50, then the check has to wait until the program
has gone to all instruction up to 600, go back to 10 and then at 30 the emergency button is checked.
This obviously takes time, this takes even longer if the program executes the CALL “do something
else”. Most likely you don’t have this much time to wait. Upon pressing the emergency button, you
want the sign “STOP RIGHT NOW !” to turn on DIRECTLY. Otherwise it might come way too late.
Here comes an interrupt controller in handy, what does it do. An interrupt controller has inputs, and
when these are activated the interrupt controller takes over control of the computer.
Imagine having an interrupt controller and at instruction 50 they emergency button is pressed.
So now with the interrupt controller no matter were the main program is in execution, the big red sign
“STOP RIGHT NOW !” is directly activated if the emergency button is pushed.
I tried to build part of the interrupt facilities as provided on the 8085 microprocessor of the 80’s, yes it’s
an ancient device. The 8085 has hardware and software interrupts, I only tried to build the hardware
interrupt. The 8085 has the following hardware interrupts:
TRAP
RST5.5
RST6.5
RST7.5
INTR
I did not built the INTR functionality. Next to be discussed is interrupt enabling/disabling and masking.
For RST5.5, RST6.5 and RST7.5 the interrupt functionality has to be activated with the software EI
command, EI stands for enable interrupt. There is also the software command DI, disable interrupt. So
RST5.5, RST6.5 and RST7.5 interrupt facility can be turned on or off. With TRAP this cannot be done,
it’s always active.
Next there is the possibility by setting an interrupt mask, this switches on or off the individual interrupt
facility of RST5.5, RST6.5 and RST7.5.
The microcontroller scans the TRAP, RST5.5, RST6.5, RST7.5 and the scr (step counter reset) inputs.
If one of the interrupt inputs is active and a running instruction reaches the scr step the microcontroller
halts execution of the computer. (and of course depending on EI/DI and SIM but that does not change
the principle) It does this by raising the CS signal of the control register EEPROM's high. The same
mechanism I use to access the RAM to write programs via the Arduino Nano in RAM.
The microcontroller now takes over control of the computer. The microcontroller writes the
PUSH_PRPC instruction into the instruction register. This is a new instruction I made and it uses a
part of the CALL instruction. In specific the part where the page register and program counter are
saved on the stack.
55
The control then goes back to the control register EEPROM's and the PUSH_PRPC instruction is
executed. When that's done, the return address were I need to go back to after the ISR has run is
saved on the stack. When reaching the SCR step in the PUSH_PRPC instruction the microcontroller
steps in and takes over control again.
Next depending on which interrupt was active the relevant interrupt vector is written into the page
register and program counter. Then the microcontroller gives control back to the EEPROM's and the
interrupt specific ISR is run. At the end of the ISR the RET instruction is executed and the program
proceeds happily were it was interrupted.
That's roughly it, obviously I also need to PUSH/PULL the content of the PSW and A, B and C register
to/from the stack. And I had to resolve a couple of nasty details.
It's a bit cheating using a microcontroller but as said required space on the breadboard was a serious
consideration. I had only 2 breadboards left and one is intended for something else, eliminating the
permanent OE low on the EEPROM’s. That left me with 1 breadboard on which already one chip was
mounted for the ALU zero flag. Then 4 pushbuttons. That leaves room for 2 TTL chips, or 1 EEPROM
and 1 TTL chip. I don't see how you could implement an interrupt controller with that little hardware.
The microcontroller has over 200 lines of code, so do that with 74HCxxx logic.
56
Chapter 12, KiCAD drawings
All drawings I might be making will be stored on my Github account
Screwdriver comes in handy to get IC’s from the board without damaging pins
And a couple of boxes with all kind of electronics componnets in a variety of values, very handy
57
Chapter 14, List of used Components
( to be detailed later )
http://buildyourcomputer.org/aGuideToComputerScience.pdf.
58