https://www.dos4ever.com/flyback/flyback.html

# Flyback Converters for Dummies

## Ronald Dekker

### Special thanks to Frans Schoofs, who really understands how flyback converters work

 If you are interested in Flyback Converters you might want to keep track of my present project: the µTracer: a miniature radio-tube curve-tracer

## introduction

In the NIXIE clocks that I have built, I did not want to have the big and ugly mains transformer in the actual clock itself. Instead I use an AC adapter that fits into the mains wall plug. This means that I have to use some sort of an up-converter to generate the 180V anode supply for the NIXIEs.

This page describes a simple boost converter and a more efficient flyback converter both of which can be used as a high voltage power supply for a 6 NIXIE tube display. Frans Schoofs beautifully explained to me the working of the flyback converter and much of what he explained to me you find reflected on this page. I additionally explain the essentials of inductors and transformers that you need to know. This is just a practical guide to get you going, it is not a scientific treatise on the topic.

## What you need to know about inductors

Consider the simple circuit consisting of a battery connected to an inductor with inductance L and resistance R (Fig. 1). When the battery is connected to the inductor, the current does not immediately change from zero to its maximum value V/R. The law of electromagnetic induction, Faraday’s law prevents this. What happens instead is the following. As the current increases with time, the magnetic flux through this loop proportional to this current increases. The increasing flux induces an e.m.f. in the circuit that opposes the change in magnetic flux. By Lenz’s law, the induced electric field in the loop must therefore be opposite to the direction of the current. As the magnitude of the current increases, the rate of the increase lessens and hence the induced e.m.f. decreases. This opposing e.m.f. results in a linear increase in current at a rate I=(V/L)*t. The increase in current will finally stop when it becomes limited through the series resistance of the inductor. At that moment the amount of magnetic energy stored in the inductor amounts to E=0.5*L*I*I.

Figure 1

http://www.crisvandevel.de/

## Cris’ site on antique mechanical four-function calculators

http://public.beuth-hochschule.de/~hamann/

 Prof. Dr.-Ing. Christian-M. Hamann ( verantwortlich für den Inhalt dieser HomePage ) Spezialgebiete: ( ehem. Leiter des Labors für Künstliche Intelligenz ) LISP & PROLOG Anwendung Expertensysteme Evolutionsstrategien & Genetische Algorithmen Simulation Neuronaler Netze Positionale Logische Algebra Fuzzy-Logik OVER 777 HISTORICAL/TECHNICAL OBJECTS TO EXPLORE … THE ON-LINE MUSEUM RechenMaschinen, RechenStäbe, … , SchreibMaschinen, Telefone, Uhren

https://www.computerhistory.org/babbage/howitworks/

# How it Works

© Computer History Museum | Credits

http://w-hasselo.nl/mechn/

# Wim Hasselo

## Early Canon Desktop Calculators

### Contents

Canon “Canola” Model 167P, 1971

http://www.azillionmonkeys.com/qed/sqroot.html

# √Square Roots

## 1. Compute a square root now!

Enter a non-negative number in the Input field below:

http://www.vcalc.net/hp-code.htm

# Microcode: Electronic Building Blocks For Calculators

Last Update: January 22, 2003 — THE HP REFERENCE

Hewlett Packard Personal Calculator Digest
Vol. 3, 1977
pp 4-6

Just as DNA can be called the building blocks of the human organism. Microcode can be called the building blocks of the electronic calculator.

But, while the way the human organism works is determined by heredity, the way an electronic calculator works is determined by the highly personal–and even idiosyncratic–creative impulses of a programming specialist.

The principles of programming can be learned, of course. But anyone who has programmed his own calculator quickly discovers that techniques may vary widely from person to person.

Consider the challenge faced by the professional programmer When you press the key labeled SIN, for example, you expect the calculator to display the sine of the value you have keyed into it–and presto, it does. But in that less-than-a-second interval between keystroke and display, the calculator has executed an internal program of about 3500 steps. And it does this according to the highly individualistic microcode that the programmer has created.

The development of microcode in Hewlett-Packard personal calculators began with the development developments of the microprocessor in the HP-35–and not coincidentally, since both were developed by the same Hewlett-Packard engineer.

It is the microprocessor that determines the “language” of the internal microcode. If you are familiar with computer languages such as BASIC, FORTRAN, and COBOL, you know that these languages structure the way you write your program on the computer. You can only do what the language lets you do.

The microprocessor is similar to the computer. It provides a language that a clever engineer can then build into a function on the keyboard.

The original HP-35 microprocessor has remained essentially unchanged through the years and is the heart of the new HP-19C and HP-29C. Compared with computer processors, the binary-coded decimal microprocessor is very simple it does not handle byte data well, but is, in fact, specially designed for 10-digit floating point numbers (See figure 1.). The resulting microcode language most closely resembles machine language, which is programming at as most basic level.

Most microprocessors use 8-bit instructions and two or three instructions are usually combined to perform one operation. The beauty of HP’s calculator microcode is that 10-bit instructions are used and each usually performs a complete operation by itself.

The language’s strongest point is its robust arithmetic section of 37 instructions combined with eight field-select options. The field-select options allow the program to apply the instruction to any word-select portion of the register (See figure 2.).

The language is also designed to use very little storage; only seven registers were used in the HP-35 of which five were user registers. This is done to reduce costs and to save valuable space. For the design engineer it means that he must accomplish all of his miracles within the program itself.

Based on warranty card analysis and other market research, parameters regarding the desired function set and price are given to the design engineer. It is his job then to determine the specific functions for the calculator and to attempt to fit them in the allotted memory. Price is an important factor to the engineer because it directly influences the amount of memory he has to work with.

Only after several months of hard work writing and compacting microcode will he know if the function set will fit. If it is not possible, the product may be redefined at a higher price with greater performance to increase the available memory. More likely however, the engineer will be forced to pare functions until his program fits.

To give you an idea of how much memory is required, the HP-35 used three pages of 256 instructions each. Each page required a separate ROM read-only memory.

• HP-35—-768 instructions–3/4 quad (3 pages of 256 instructions each)

The HP-45 originally took six pages of instructions. But about that time the quad ROM was developed, which, as its name implies, was the equivalent of four conventional ROM’S. So for the HP-45, two quad ROM’s were used. It was in the leftover two pages that an enterprising designer placed the celebrated HP-45 clock. Later calculators, listed below, continued to use quad ROM’s.

Note: 1 quad = 1024 instructions or 4 pages of 256 instructions each. -Rick-

Writing the microcode is where the designer’s personality is stamped indelibly on the Calculator. While it is true that the fundamental algorithms for commuting the complex mathematical functions found in HP personal calculators have remained essentially the same since the HP-35, the individual code is substantially different.

```+---------------------------------------------------------------------+
| NUMBER   REGISTER REPRESENTATION                                    |
|                                                                     |
|          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+  |
|   23.4   | 0 | 2 | 3 | 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |  |
|          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+  |
|                                                                     |
|            a                                                        |
|          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+  |
|  -123.   | 9 | 1 | 2 | 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 2 |  |
|          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+  |
|                                                                     |
|                                                                   b |
|          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+  |
|  .002    | 0 | 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 9 | 9 | 7 |  |
|          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+  |
|                                                                     |
|          a. A "9" in the sign position indicates a negative number. |
|          b. Exponents are kept in 10's complement form.             |
|                                                                     |
+---------------------------------------------------------------------+
|  Figure 1. All numbers in registers are in scientific notation with |
|  the mantissa portion of the number left justified in the mantissa  |
|  portion of the register.                                           |
+---------------------------------------------------------------------+
```
```+---------------------------------------------------------------------+
|          mantissa sign                               exponent sign  |
| pointer    |                                           |            |
| positions+---+---+---+---+---+---+---+---+---+---+---+---+---+---+  |
|    ----> |13 |12 |11 |10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |  |
|          +---+---+---+---+---+---+---+---+---+---+---+---+---+---+  |
|          |   |                                       |           |  |
|          |   |                                       |           |  |
|          |   +------------- mantissa ----------------+- exponent +  |
|          |                                           |           |  |
|          +----------- mantissa and sign -------------+           |  |
|          |                                                       |  |
|          +----------------------  word --------------------------+  |
|                                                                     |
+---------------------------------------------------------------------+

+---------------------------------------------------------------------+
|Instructions                                                         |
+--------------+------------------------------------------------------+
|  A = 0       |                                                      |
|  B = 0       | Clears word-select portion.                          |
|  C = 0       |                                                      |
+--------------+------------------------------------------------------+
|  B = A       | Copies word-select portion                           |
|  C = B       |   from specified register                            |
|  A = C       |   to specified register.                             |
+--------------+------------------------------------------------------+
|  AB EX       | Exchanges word-select portion                        |
|  AC EX       |   between specified registers.                       |
|  CB EX       |                                                      |
+--------------+------------------------------------------------------+
|  A = A + B   |                                                      |
|  A = A + C   |                                                      |
|  C = A + C   |                                                      |
|  C = C + C   |                                                      |
|  A = A - B   |                                                      |
|  C = A - C   |                                                      |
|  A = A - C   | Performs stated arithmetic                           |
|  A = A + 1   |   on word-select portion.                            |
|  C = C + 1   |                                                      |
|  A = A - 1   |                                                      |
|  C = C - 1   |                                                      |
|  C = -C      |                                                      |
|  C = -C - 1  |                                                      |
+--------------+------------------------------------------------------+
|  A SR        |                                                      |
|  B SR        | Shifts word-select portion right.                    |
|  C SR        |                                                      |
+--------------+------------------------------------------------------+
|              |                                                      |
|  A SL        | Shifts word-select portion left.                     |
|              |                                                      |
+--------------+------------------------------------------------------+
|              | Circular shifts whole A                              |
|  A SLC       |   register but does not have                         |
|              |   word-select option.                                |
+--------------+------------------------------------------------------+
|  ? B = 0     |                                                      |
|  ? C = 0     |                                                      |
|  ? A => C    | Tests word-select portion of                         |
|  ? A => B    |   given register.                                    |
|  ? A = 0     |                                                      |
|  ? C = 0     |                                                      |
+--------------+------------------------------------------------------+
| Field-Select Options                                                |
|                                                                     |
|  1. Mantissa (M)                                                    |
|  2. Mantissa and Sign (MS)                                          |
|  3. Exponent (X)                                                    |
|  4. Exponent Sign (XS)                                              |
|  5. Sign of Mantissa (S)                                            |
|  6. Pointer (P)                                                     |
|  7. Word (W)                                                        |
|  8. Word thru Pointer (WP)                                          |
|                                                                     |
+---------------------------------------------------------------------+
|  Figure 2. Registers are 14 digits long with each digit being four  |
|  bits. An additional four bit register is used as a pointer. The    |
|  programmer can set the pointer to any digit position, change that  |
|  digit or all digits up to that position.                           |
+---------------------------------------------------------------------+
```

Some of the major routines such as those for sine, cosine, and tangent are the same from calculator to calculator. But in most cases if the code cannot be borrowed exactly as it exists in another calculator, it must be rewritten.

Some designer begin by strictly flowcharting the entire program Others tackle the code directly and leave all but basic flowcharting till the end for documentation purposes only. The code is written originally on paper just as you would write a program on an HP programming pad. It is then either punched on cards or typed into a handy CRT.

The major task in writing the microcode is not only to have the functions produce the correct answers when a key is pressed, but to fit the code into a given amount of memory and make it execute as fast as possible. Compacting the code, that is, rewriting sections of the program to make them more space-efficient, is easy enough, but sometimes results in a loss of speed. These are tradeoffs to which the designer must be constantly attentive.

The processor executes approximately three instructions per millisecond, with each instruction taking the same amount of time. The designer takes into consideration the type of function and the necessary speed when writing the code. Straight-line code with no branches of any I kind executes the fastest. For the label search function on the HP-67 and the HP-97 the designer duplicated a great deal of code to; make it faster. The print instructions for the new HP-19C, on the other hand, did not need to be as fast to keep up with the printer, mechanism. So the designer compacted these codes, making them very complex.

```+---------------------------------------------------------------------+
|     +---------------+    +---------------+    +---------------+     |
|     |   A. Answer   |    |B. Multiplicand|    | C. Multiplier |     |
|     +---------------+    +---------------+    +---------------+     |
|                                                                     |
+--------+-----------+--------+---------------------------------------+
| LABEL  | CODE      |        | Explanation                           |
+--------+-----------+--------+---------------------------------------+
|        | A = 0     | W      | Clearing space for answer.            |
|        | P =       | 3      | Starting at least significant digit.  |
|        | GoTo      | Mpy90  | Starting in middle of loop for speed. |
| Mpy90  | A = A + B | W      | Add Multiplicand to partial product   |
|        |           |        |   the number of times specified by    |
|        |           |        |   digit being worked on.              |
+--------+-----------+--------+---------------------------------------+
| Mpy100 | C = C - 1 | P      |                                       |
|        | GoNC      | Mpy90  | NC stands for no carry, i.e. a GoTo   |
|        |           |        |   is executed unless a digit is       |
|        |           |        |   carried (or borrowed).              |
|        | ? P =     | 12     | Have we reached the end of the world? |
|        | GoYes     | END    |                                       |
|        |           |        |                                       |
+--------+-----------+--------+---------------------------------------+
|        | P = P + 1 |        | Move on the next digit.               |
|        | A SP      | W      | This does a divide by 10 to line up   |
|        |           |        |   for the next decade.                |
|        | GoTo      | Mpy100 | Go reenter loop.                      |
|        |           |        |                                       |
+--------+-----------+--------+---------------------------------------+
|  Figure 3. Multiplying two numbers uses the basic routine shown     |
|  above and involves three registers The starting point of this      |
|  code assumes the sign and exponent of the answer have already      |
|  been calculated. END is a common function return which would       |
|  perform operations such as display formatting, printing, etc.      |
+---------------------------------------------------------------------+
```

In general, programmable calculators go through more gyrations for each function than do preprogrammed calculators because they must generate an intermediate keycode. Where simple functions such as Change Sign, x exchange y, and ENTER require only 100 steps of code on a preprogrammed calculator, the same functions on a programmable calculator might take 150 steps. And a complex function such as rectangular-to-polar conversion might take over 4000 steps, depending on the argument keyed in.

The engineer uses a computer simulator to write and debug his code. Special programs written for this simulator furnish him with status bit information, register contents, and intermediate answers as an aid in this process.

Once the microcode is completed on the computer, it is transferred to an E-ROM (erasable read only-memory) simulator for further debugging (See photo.). The simulator is ideal for this because it is portable and easily updated as bugs are found and corrected. Simulated calculators are given to application engineers and quality assurance engineers to help locate problems.

After much editing, the microcode is ready to be converted into hardware. This usually takes several weeks. In the meantime, the simulators continue to be used heavily and, in most cases, additional bugs are found.

When the completed integrated circuit chips return, the first working models of the calculator are constructed. Final testing is then initiated. Some problems can only be discovered at this stage because of the peculiarities of simulated operation. For example, this is the first time low battery indicators can be checked since the E-ROM simulator does not work on batteries.

At long last, the revised code is ready to be sent for final chips. Although no problems are anticipated at this stage, testing continues to assure traditional Hewlett-Packard reliability.

The tremendous emphasis on testing of the calculators is for practical reasons as well incorrect programs cannot be easily corrected as they can be on large computers. Once the code is set in hardware, changes are costly and inconvenient.

When the final chips are approved for production, the development cycle is complete. The design engineer has spent anywhere from six months to 13 months perfecting his building block design. And whether the end product is a high-powered financial calculator like the HP-92 or a versatile keystroke programmable like the HP-19C, it is first an expression of his personality and creativity.

The Calculator Reference by Rick Furr (rfurr@vcalc.net)

Back to The HP Page

Back to The Calculator Reference

Division, part 1: Integer division

# DIVISION, PART 1: INTEGER DIVISION

Been a long time since I said anything here, and I and my Nippon HL-21 are parted for a few weeks, so I’ll talk algorithms for a while. Thus far we’ve seen addition, subtraction, and multiplication. The missing operation is division. Now, we’re going to start with “fourth-grade” division: the sort where you divide one whole number by another whole number and get a remainder. Later we’ll talk about decimals.

To start with, we might look at what integer division means, and how we do it by hand; the latter will inform our design of algorithms. Let’s suppose we want to divide, say, 52301 by 13.

http://www.pythondiary.com/blog/Oct.15,2014/building-cpu-simulator-python.html

## Wednesday, October15th, 2014

### Building a CPU simulator in Python

You can find the entire source listing from this article, plus more interesting code on my Python Experiments BitBucket page. Plus, the source code there will evolve and grow.

Early this morning in my Planet Python feed, I read this really interesting article called Developing Upwards: CARDIAC: The Cardboard Computer, which is about this cardboard computer called the Cardiac. As some of my followers and readers might know, I have another project that I have been working on and evolving for the past months called simple-cpu, which I have released the source code for. I really should give that project a proper license so that others might feel interested in using it in their own projects… Anyways, hopefully I’ll have that done by the time this publishes.

```class Cardiac(object):
"""
This class is the cardiac "CPU".
"""
def __init__(self):
self.init_cpu()
self.reset()
self.init_mem()
self.init_output()
def reset(self):
"""
This method resets the CPU's registers to their defaults.
"""
self.pc = 0 #: Program Counter
self.ir = 0 #: Instruction Register
self.acc = 0 #: Accumulator
self.running = False #: Are we running?
def init_cpu(self):
"""
This fancy method will automatically build a list of our opcodes into a hash.
This enables us to build a typical case/select system in Python and also keeps
things more DRY.  We could have also used the getattr during the process()
method before, and wrapped it around a try/except block, but that looks
a bit messy.  This keeps things clean and simple with a nice one-to-one
call-map.
"""
self.__opcodes = {}
classes = [self.__class__] #: This holds all the classes and base classes.
while classes:
cls = classes.pop() # Pop the classes stack and being
if cls.__bases__: # Does this class have any base classes?
classes = classes + list(cls.__bases__)
for name in dir(cls): # Lets iterate through the names.
if name[:7] == 'opcode_': # We only want opcodes here.
try:
opcode = int(name[7:])
except ValueError:
raise NameError('Opcodes must be numeric, invalid opcode: %s' % name[7:])
self.__opcodes.update({opcode:getattr(self, 'opcode_%s' % opcode)})
def init_mem(self):
"""
This method resets the Cardiac's memory space to all blank strings, as per Cardiac specs.
"""
self.mem = ['' for i in range(0,100)]
self.mem[0] = '001' #: The Cardiac bootstrap operation.
"""
This method initializes the input reader.
"""
self.reader = [] #: This variable can be accessed after initializing the class to provide input data.
def init_output(self):
"""
This method initializes the output deck/paper/printer/teletype/etc...
"""
self.output = []
```

Hopefully I left enough comments in the code above for you to understand exactly what’s going on here. You might notice here a key difference from my simple-cpuproject, is the method which opcodes are handled. I actually plan on incorporating this new way of detecting opcodes into that project in the near future. This method makes it easier for other developers of the library to extend it for their own requirements. As mentioned before, that project has been evolving as I started to understand more about how things should be done. In fact, taking on a CPU simulator project is a really incredible learning experience all on it’s own. If you are truly a computer scientist, you should understand the under workings of a CPU, and how each opcode is processed at it’s lowest level. Plus, developing and seeing a custom built CPU simulator made by your own imagination is a very gratifying experience. It’s almost like giving birth, as this machine was entirely built by your mind alone, and seeing it work, is something magical.

In the next part of this class, we will focus on the utility functions that we may need to call and use multiple times, these methods can also be overridden in subclasses to alter how the CPU actually functions:

```    def read_deck(self, fname):
"""
"""
def fetch(self):
"""
This method retrieves an instruction from memory address pointed to by the program pointer.
Then we increment the program pointer.
"""
self.ir = int(self.mem[self.pc])
self.pc +=1
def get_memint(self, data):
"""
Since our memory storage is *string* based, like the real Cardiac, we need
a reusable function to grab a integer from memory.  This method could be
overridden if say a new memory type was implemented, say an mmap one.
"""
return int(self.mem[data])
"""
This function pads either an integer or a number in string format with
zeros.  This is needed to replicate the exact behavior of the Cardiac.
"""
orig = int(data)
data = '%s%s' % (padding, abs(data))
if orig < 0:
return '-'+data[-length:]
return data[-length:]
```

These are the various utility functions, all of which might be overridden in a subclass. Later in this article, I will also provide an alternate source output which displays how this simulator can be built using Python Mixin classes, to make things even more pluggable. Finally, we find ourselves at the final part of code to get this simulator running, the actual processing methods:

```    def process(self):
"""
Process a single opcode from the current program counter.  This is
normally called from the running loop, but can also be called
manually to provide a "step-by-step" debugging interface, or
to slow down execution using time.sleep().  This is the method
that will also need to used if you build a TK/GTK/Qt/curses frontend
to control execution in another thread of operation.
"""
self.fetch()
opcode, data = int(math.floor(self.ir / 100)), self.ir % 100
self.__opcodes[opcode](data)
def opcode_0(self, data):
""" INPUT Operation """
def opcode_1(self, data):
""" Clear and Add Operation """
self.acc = self.get_memint(data)
def opcode_2(self, data):
self.acc += self.get_memint(data)
def opcode_3(self, data):
""" Test Accumulator contents Operation """
if self.acc < 0:
self.pc = data
def opcode_4(self, data):
""" Shift operation """
x,y = int(math.floor(data / 10)), int(data % 10)
for i in range(0,x):
self.acc = (self.acc * 10) % 10000
for i in range(0,y):
self.acc = int(math.floor(self.acc / 10))
def opcode_5(self, data):
""" Output operation """
self.output.append(self.mem[data])
def opcode_6(self, data):
""" Store operation """
def opcode_7(self, data):
""" Subtract Operation """
self.acc -= self.get_memint(data)
def opcode_8(self, data):
""" Unconditional Jump operation """
self.pc = data
def opcode_9(self, data):
""" Halt and Reset operation """
self.reset()
def run(self, pc=None):
""" Runs code in memory until halt/reset opcode. """
if pc:
self.pc = pc
self.running = True
while self.running:
self.process()
print "Output:\n%s" % '\n'.join(self.output)
self.init_output()

if __name__ == '__main__':
c = Cardiac()
try:
c.run()
except:
print "IR: %s\nPC: %s\nOutput: %s\n" % (c.ir, c.pc, '\n'.join(c.output))
raise
```

As mentioned above, I will now refactor the code into Mixin classes and provide a full source output of that:

```class Memory(object):
"""
This class controls the virtual memory space of the simulator.
"""
def init_mem(self):
"""
This method resets the Cardiac's memory space to all blank strings, as per Cardiac specs.
"""
self.mem = ['' for i in range(0,100)]
self.mem[0] = '001' #: The Cardiac bootstrap operation.
def get_memint(self, data):
"""
Since our memory storage is *string* based, like the real Cardiac, we need
a reusable function to grab a integer from memory.  This method could be
overridden if say a new memory type was implemented, say an mmap one.
"""
return int(self.mem[data])
"""
This function pads either an integer or a number in string format with
zeros.  This is needed to replicate the exact behavior of the Cardiac.
"""
orig = int(data)
data = '%s%s' % (padding, abs(data))
if orig < 0:
return '-'+data[-length:]
return data[-length:]

class IO(object):
"""
This class controls the virtual I/O of the simulator.
To enable alternate methods of input and output, swap this.
"""
"""
This method initializes the input reader.
"""
self.reader = [] #: This variable can be accessed after initializing the class to provide input data.
def init_output(self):
"""
This method initializes the output deck/paper/printer/teletype/etc...
"""
self.output = []
"""
"""
def format_output(self):
"""
This method is to format the output of this virtual IO device.
"""
return '\n'.join(self.output)
def get_input(self):
"""
This method is used to get input from this IO device, this could say
be replaced with raw_input() to manually enter in data.
"""
try:
except IndexError:
# Fall back to raw_input() in the case of EOF on the reader.
return raw_input('INP: ')[:3]
def stdout(self, data):
self.output.append(data)

class CPU(object):
"""
This class is the cardiac "CPU".
"""
def __init__(self):
self.init_cpu()
self.reset()
try:
self.init_mem()
except AttributeError:
raise NotImplementedError('You need to Mixin a memory-enabled class.')
try:
self.init_output()
except AttributeError:
raise NotImplementedError('You need to Mixin a IO-enabled class.')
def reset(self):
"""
This method resets the CPU's registers to their defaults.
"""
self.pc = 0 #: Program Counter
self.ir = 0 #: Instruction Register
self.acc = 0 #: Accumulator
self.running = False #: Are we running?
def init_cpu(self):
"""
This fancy method will automatically build a list of our opcodes into a hash.
This enables us to build a typical case/select system in Python and also keeps
things more DRY.  We could have also used the getattr during the process()
method before, and wrapped it around a try/except block, but that looks
a bit messy.  This keeps things clean and simple with a nice one-to-one
call-map.
"""
self.__opcodes = {}
classes = [self.__class__] #: This holds all the classes and base classes.
while classes:
cls = classes.pop() # Pop the classes stack and being
if cls.__bases__: # Does this class have any base classes?
classes = classes + list(cls.__bases__)
for name in dir(cls): # Lets iterate through the names.
if name[:7] == 'opcode_': # We only want opcodes here.
try:
opcode = int(name[7:])
except ValueError:
raise NameError('Opcodes must be numeric, invalid opcode: %s' % name[7:])
self.__opcodes.update({opcode:getattr(self, 'opcode_%s' % opcode)})
def fetch(self):
"""
This method retrieves an instruction from memory address pointed to by the program pointer.
Then we increment the program pointer.
"""
self.ir = self.get_memint(self.pc)
self.pc +=1
def process(self):
"""
Process a single opcode from the current program counter.  This is
normally called from the running loop, but can also be called
manually to provide a "step-by-step" debugging interface, or
to slow down execution using time.sleep().  This is the method
that will also need to used if you build a TK/GTK/Qt/curses frontend
to control execution in another thread of operation.
"""
self.fetch()
opcode, data = int(math.floor(self.ir / 100)), self.ir % 100
self.__opcodes[opcode](data)
def opcode_0(self, data):
""" INPUT Operation """
self.mem[data] = self.get_input()
def opcode_1(self, data):
""" Clear and Add Operation """
self.acc = self.get_memint(data)
def opcode_2(self, data):
self.acc += self.get_memint(data)
def opcode_3(self, data):
""" Test Accumulator contents Operation """
if self.acc < 0:
self.pc = data
def opcode_4(self, data):
""" Shift operation """
x,y = int(math.floor(data / 10)), int(data % 10)
for i in range(0,x):
self.acc = (self.acc * 10) % 10000
for i in range(0,y):
self.acc = int(math.floor(self.acc / 10))
def opcode_5(self, data):
""" Output operation """
self.stdout(self.mem[data])
def opcode_6(self, data):
""" Store operation """
def opcode_7(self, data):
""" Subtract Operation """
self.acc -= self.get_memint(data)
def opcode_8(self, data):
""" Unconditional Jump operation """
self.pc = data
def opcode_9(self, data):
""" Halt and Reset operation """
self.reset()
def run(self, pc=None):
""" Runs code in memory until halt/reset opcode. """
if pc:
self.pc = pc
self.running = True
while self.running:
self.process()
print "Output:\n%s" % self.format_output()
self.init_output()

class Cardiac(CPU, Memory, IO):
pass

if __name__ == '__main__':
c = Cardiac()
try:
c.run()
except:
print "IR: %s\nPC: %s\nOutput: %s\n" % (c.ir, c.pc, c.format_output())
raise
```

You can find the code for deck1.txt from the Developing Upwards: CARDIAC: The Cardboard Computer article, I used the counting to 10 example.

Hopefully this article was inspiring to you and gives you a brief understanding on how to built class-bases modular and pluggable code in Python, and also gave you a nice introduction to developing a CPU simulator. In the next article which I hope to publish soon, will guide you through making a basic assembler for this CPU, so that you can easily and effortlessly create decks to play around with in the simulator.

Comment #1: Posted 4 years, 6 months ago by Calvin Spealman

This is such cool work! The simplicity of the CARDIAC is certainly something to admire. If you don’t mind I’ll probably follow up with a nod towards this. Are you planning to put the code up at a proper repo anywhere?

Comment #2: Posted 4 years, 6 months ago by Kevin Veroneau

@Calvin, the code is up in a repo, look inside the information tooltip box at the very top of this article for a click-able link to the BitBucket page.

Names Kevin, hugely into UNIX technologies, not just Linux. I’ve dabbled with the demons, played with the Sun, and now with the Penguins.
Kevin Veroneau Consulting Services
Do you require the services of a Django contractor? Do you need both a website and hosting services? Perhaps I can help.

## This Month

If you like what you read, please consider donating to help with hosting costs, and to fund future books to review.

Python Powered | © 2012-2019 Kevin Veroneau

https://www.cs.drexel.edu/~bls96/museum/cardiac.html

## Background

The acronym CARDIAC stands for “CARDboard Illustrative Aid to Computation.” It was developed by David Hagelbarger at Bell Labs as a tool for teaching how computers work in a time when access to real computers was extremely limited. The CARDIAC kit consists of a folded cardboard “computer” and an instruction manual. In July 1969, the Bell Laboratories Record contained an article describing the system and the materials being made available to teachers for working with it.

As illustrated in the following pictures, the CARDIAC computer consisted of a left-hand CPU section and a right-hand memory section. On the CPU side there are five sliders:

• One slider of input “cards”
• One slider for the accumulator sign
• Three sliders for the digits of an instruction

The memory side has a single slider of output “cards.” Portions of the sliders show through cutouts in the card frame. The cutouts for the input and output card sliders each show the current card to be read or written. The combination of the accumulator sign and the three instruction sliders show steps through cutouts that describe the operation of the selected instruction. Effectively, the sliders and cutouts are the instruction decoder of the CPU. Finally, each memory location has a hole in it. A small carboard ladybug serves as the program counter which is moved from location to location in response to the steps described on the CPU side.

The CARDIAC manual is 50+ pages divided into 16 sections describing the basics of computers from a late 1960s perspective. The first six sections cover things like flow charts, instructions, data, addresses, and the stored program concept. Sections 7–12 discuss the CARDIAC and some basic programming techniques including loops and multiplication. Sections 13 and 14 discuss the techniques for bootstrapping and subroutines, both of which we elaborate on below. Section 15 focuses on the development of a program to play NIM. Finally, Section 16 discusses assemblers and compilers. Although there is some duplication of information, the material on this page is not intended to replace the manual. Rather, the material here expands on that in the manual, particularly from the point of view of one who is already familiar with the most basic concepts of computers and programming.

### Pictures

Click on these pictures for larger versions.

## CARDIAC Architecture

### Memory

The CARDIAC has a grand total of 100 memory locations identified by the two-digit decimal numbers 00 through 99. Each memory location holds a signed three-digit decimal numer. (With the exception of a single code example, the CARDIAC book is actually silent on whether memory contains signed or unsigned values.) Locations 00 and 99 are special. Location 00 always contains the value 001, which as we see below is the instruction to read a card into location 01. This special value is used the the bootstrapping process discussed later. Location 99 always contains a value between 800 and 899. The tens and ones digits of the number are the value of the program counter after a jump instruction is executed. This provides the mechanism for a return from subroutine.

### CPU

The CARDIAC CPU is a single-accumulator single-address machine. Thus each instruction operates optionally on a single memory location and the accumulator. For example, the ADD instruction reads the data in one memory location, adds it to the current value of the accumulator and stores the result back into the accumulator. The ALU supports addition, subtraction, and decimal shifting. CARDIAC’s CPU architecture is illustrated in the following figure:

The CARDIAC accumulator holds a signed 4-digit number, which seems odd given that everything else is oriented around 3-digit numbers. The manual includes the statement:

Since CARDIAC’s memory can store only 3-digit numbers, you may be puzzled by the inclusion of an extra square in the accumulator. It is there to handle the overflow that will result when two 3-digit numbers whose sum exceeds 999 are added.

What’s not clear is under what conditions that overflow/carry digit is kept or discarded. From the discussion of the SFT instruction in Section 12 of the manual, exactly four digits are kept for the intermediate value between the left and right shift operations. However, the manual doesn’t state whether all four digits are kept between instructions nor what happens when storing the accumulator to memory if the accumulator contains a number whose magnitude is greater than 999. In the case of our simulator, we retain all four digits, effectively implementing a 4-digit ALU. However, when storing the accumulator to memory, we discard the fourth digit. I.e. the number stored in memory is a mod 1000, where a is the contents of the accumulator.

### I/O

The CARDIAC has exactly one input device and one output device. These are a card reader and a card punch. Unlike real punch cards, the CARDIAC input and output cards can each hold exactly one signed three-digit number. When a card is read by way of the INP instruction, it is taken into the card reader and removed from the stack of cards to be read. Similarly, on each OUT instruction, a new card is “punched” with the specified value on it, and the card moved to the output card stack.

## Instruction Set

The CARDIAC’s instuction set has only 10 instructions, each identified by an operation code (opcode) of 0 through 9. The instructions are as follows:

Opcode Mnemonic Operation
0 INP Read a card into memory
3 TAC Test accumulator and jump if negative
4 SFT Shift accumulator
5 OUT Write memory location to output card
6 STO Store accumulator to memory
7 SUB Subtract memory from accumulator
8 JMP Jump and save PC
9 HRS Halt and reset

### Encoding

All instructions are non-negative numbers expressed as three-digit decimal numerals. The CARDIAC manual doesn’t describe what happens if an attempt is made to execute a negative instruction. In our simulator, we treat negative instructions as no-ops (i.e. they are ignored and the program continues on to the next instruction). The operation code is the most significant of those three digits, i.e., o=⌊i /100⌋, where i is the contents of the instruction register (IR) loaded from the memory location specified by the PC. For most instructions, the lower-order digits are the address of the operand, i.e. a=i mod 100. This arrangement is illustrated in the following figure.

In the cases of the INP and STO instructions, a is the destination address for the data coming from either an input card or the accumulator, respectively. In the cases of the CLA, ADD, OUT, and SUB instructions, a is the source address of the second operand to the ALU or the source address of the operand being written to an output card. For the TAC, JMP, and HRS instructions, a is the address to be loaded into the PC (conditionally, in the case of the TAC instruction). The remaining instruction, SFT, doesn’t treat the lower-order digits as an address. Instead, each of the lower-order digits is a number of digit positions to shift first left, then right. The left shift count is given by l=⌊a /10⌋, and the right shift count is given by r=a mod 10. The instruction format for the SFT instruction is shown in the following figure:

### Instruction Execution

The instructions operate as described here. In this discussion, we use the following notation:

Notation Meaning
ACC Contents of the accumulator
PC Contents of the program counter
a Operand address as described in the previous subsection
MEM[x] Contents of memory location x
INPUT Contents of one card read from the input
OUTPUT Contents of one card written to the output
INP
The INP instruction reads a single card from the input and stores the contents of that card into the memory location identified by the operand address. (MEM[a] ← INPUT)
CLA
This instruction causes the contents of the memory location specified by the operand address to be loaded into the accumulator. (ACC ← MEM[a])
The ADD instruction takes the contents of the accumulator, adds it to the contents of the memory location identified by the operand address and stores the sum into the accumulator. (ACC ← ACC + MEM[a])
TAC
The TAC instruction is the CARDIAC’s only conditional branch instruction. It tests the accumulator, and if the accumulator is negative, then the PC is loaded with the operand address. Otherwise, the PC is not modified and the program continues with the instruction following the TAC. (If ACC < 0, PC ← a)
SFT
This instruction causes the accumulator to be shifted to the left by some number of digits and then back to the right some number of digits. The amounts by which it is shifted are shown above in the encoding for the SFT instruction. (ACC ← (ACC × 10^l) / 10^r)
OUT
The OUT instruction takes the contents of the memory location specified by the operand address and writes them out to an output card. (OUTPUT ← MEM[a])
STO
This is the inverse of the CLA isntruction. The accumulator is copied to the memory location given by the operand address. (MEM[a] ← ACC)
SUB
In the SUB instruction the contents of the memory location identified by the operand address is subtracted from the contents of the accumulator and the difference is stored in the accumulator. (ACC ← ACC − MEM[a])
JMP
The JMP instruction first copies the PC into the operand part of the instruction at address 99. So if the CARDIAC is executing a JMP instruction stored in memory location 42, then the value 843 will be stored in location 99. Then the operand address is copied into the PC, causing the next instruction to be executed to be the one at the operand address. (MEM[99] ← 800 + PC; PC ← a)
HRS
The HRS instruction halts the CARDIAC and puts the operand address into the PC. (PC ← a; HALT)

### Assembly Language

All of the code fragments and complete program examples on this page are shown in an assembly language format with each line organized into six columns:

1. Address: The first column shows the memory address respresented by that line.
2. Contents: In the second column, we put the number that is stored in that memory location.
3. In most cases, this is a instruction, but for lines with a DATA pseudo-op, it is a data value.

4. Label: The third column contains an optional label on the memory location, allowing it to be identified by name, rather than by address.
5. Opcode: Instruction mnemonics are places in the fourth column. In addition to the ten instructions discussed above, we will use on pseudo-op (or assembler directive), DATA. For memory locations containing a DATA item, the operand is the literal data value stored in the memory location, rather than an operand for an instruction. This pseudo-op is particularly useful when labeled for creating variables.
6. Operand: The fifth column is the operand part of the instruction or the literal data for a DATA directive. Numerical operands are included directly in the address field of the instruction. When a label name appears as an oeprand, the memory address associated with that label is placed in the address field of the instruction.
7. Comment: Any desired descriptive text can be placed after the operand.

## Indirection, Indexing, and Pointers

Notice that the only way of specifying the address of a memory location we want to use is in the instruction itself. Most comptuer architectures provide a mechanism whereby the address we want to use can be stored in a register or another memory location. Variables which contains memory addresses are usually referred to as pointers.

Even though the CARDIAC doesn’t have hardware support for using pointers directly, we can still do simple indirect addressing. Suppose we have a variable stored in a memory location called ptr and it has the value 42 in it. Now if we want to load the accumulator with the contents of memory location 42, we can do something like:

```05	100	loader	DATA	100
06	042	ptr	DATA	042

```

Notice that even though we have specified that we will load from location 00 in the instruction at location 23, we will have changed it to load from location 42 by the time we run execute that instruction. For that matter, it doesn’t matter if we’ve loaded anything into location 23 before starting this sequence. It will get set before we use it.

### Indirect Stores

Storing the accumulator to a memory location identified by a pointer is similar. We just have to be careful not to lose the value we want to store while we’re fiddling about with the store instruction and in the following bit of code:

```05	600	storer	DATA	600
06	042	ptr	DATA	042
07	000	acc	DATA	000

20	607		STO	acc
21	105		CLA	storer
23	625		STO	indstor
24	107		CLA	acc
25	600	indstor	STO	00
```

### Array Indexing

Often we aren’t so much interested in a pointer that identifies a single memory location as we are in an array of memory locations we can refer to by index. We will identify our array locations starting at index 0. So the first element of the array is at index 0, the second at index 1, and so on. If we have a variable called base that holds the first address of the array, then we can just add the base and the index together to get the address of a particular element. This is just a slight modification of the indirect accesses above. In particular, to load from an array element:

```05	100	loader	DATA	100
06	042	base	DATA	042
07	000	index	DATA	000

```

and for storing to an array element:

```05	600	storer	DATA	600
06	042	base	DATA	042
07	000	index	DATA	000
08	000	acc	DATA	000

20	608		STO	acc
21	105		CLA	storer
24	626		STO	arrstor
25	108		CLA	acc
26	600	arrstor	STO	00
```

If we’re dealing with only one array, we could eliminate one add instruction from each sequence by pre-adding the base and loader and pre-adding the base and storer.

## Stacks

Another use of indirect address is the stack data structure. If you’re not familiar with a stack, think of it like a stack of plates in a cafateria. A plate is always placed on top of the stack. Likewise, the one removed is always the one on the top of the stack. We refer to the process of putting an element onto a stack as pushing and the process of taking an element off of a stack as popping.Note that we always pop that most recently pushed element. Because of this, the stack is often referred to as a last-in, first-out (LIFO) data structure. Pushing and popping are very similar to storing and loading indirectly, except that we must also adjust the value of the pointer that identifies the top of the stack. In the following code we’ll use a memory location named tos (for top-of-stack) for the pointer. Also, we’ll do as is often done in hardware stacks and let the stack grow downward. That is to say, as we push data onto the stack, the stack pointer moves toward lower memory addresses. With that in mind, here is a fragment of code for pushing the accumulator onto the stack:

```05	600	storer	DATA	600
07	089	tos	DATA	089
08	000	acc	DATA	000

20	608		STO	acc
21	107		CLA	tos
23	628		STO	stapsh
24	107		CLA	tos
25	700		SUB	00
26	607		STO	tos
27	108		CLA	acc
28	600	stapsh	STO	00
```

And similarly to pop from the top of the stack:

```20	107		CLA	tos
22	607		STO	tos
24	625		STO	stapop
25	100	stapop	CLA	00
```

These code fragments (slightly modified) are used in the example below that uses the LIFO properties of the stack to reverse the order of a list of numbers on the input cards.

## Subroutines

There are many reasons why we might wish to subdivide a program into a number of smaller parts. In the context of higher level languages and methodologies, these subdivisions are often referred to by names like procedures, functions, and methods. All of these are types of subroutines, the name we usually use when working at the hardware or machine language level. In these sections, we look at the techniques for creating and using subroutines on the CARDIAC. Each subsection progressively builds from the simplest subroutine technique to more complex and advanced techiques. Don’t worry if not all of it makes sense on a first reading. You can get a good sense of the general idea of subroutines without necessarily understanding the details of how recursion is implemented on a machine as limited as the CARDIAC.

### Single Simple Subroutines

In the CARDIAC, the JMP instruction is effectively a jump-to-subroutine instruction, storing the return address in location 99. Because the address stored in location 99 is prefixed by the opcode 8, the instruction in that location becomes a return-from-subroutine instruction. Thus any segment of code whose last instruction is at location 99 can be called as a subroutine, simply by jumping to its first instruction. For example, a simple routine to double the value of the accumulator could be coded as:

```96	000	accval	DATA	000

97	696	double	STO	accval
99	800		JMP	00
```

```	897		JMP	double
```

### Multiple Subroutines

Clearly, if our subroutine executes a JMP instruction or if it calls another subroutine, then we will lose our return address, because it will be overwritten by the JMP instruction. Along similar lines, if we have more than one subroutine in our program, only one of them can be at the end of the memory space and flow directly into location 99.

As a result, in many cases, we’ll need a more involved subroutine linkage mechanism. One way to accomplish this is to save the return address somewhere and restore it when needed. If we use this method, we’ll have to devise a mechansism to transfer control to location 99 with the right return address. Although location 99 can itself be used as the return from subroutine instruction, it doesn’t have to be. In many cases, it will be easier to copy it to the end of our actual subroutine. Using this approach, we can write a subroutine that outputs the value of the accumulator as follows:

```80	686	aprint	STO	86
81	199		CLA	99
82	685		STO	aexit
83	586		OUT	86
84	186		CLA	86
85	800	aexit	JMP	00
```

Similarly, our doubling routine would look like:

```90	696	double	STO	96
91	199		CLA	99
92	695		STO	dexit
93	196		CLA	96
95	800	dexit	JMP	00
```

See below for an example of a program that uses these subroutines to produce a list of powers of two.

### Recursion

There’s one more limitation on subroutines still in the techniques we have developed. What happens if a subroutine calls itself? You might reasonably as, is it even useful for a function call itself? The answer is, yes, and it called recursion.

The key to making it possible for a subroutine to call itself is to realize that no matter where we’re called from, we always want to return to the place from which we were most recently called that we haven’t already returned to. That should sound familiar. We should use the return addresses in the same LIFO order that a stack provides. In other words, when we call a recursive subroutine, we want to push the return address onto a stack and then pop it back off when we return from that subroutine. With a little reflection, we can see that this approach applies to all subroutine calls, not just to those that are recursive. This is why pushing return addresses on a stack is the basis for hardware subroutine call support in most architectures since about the 1970s on.

On the CARDIAC, we can implement this technique with a modification of the multiple subroutine technique above. When entering a subroutine, rather than copying location 99 to the return from subroutine instruction, we push the contents of location 99 onto the stack. Then when we’re about to return from the subroutine, we pop the return address off the stack into the return from subroutine instruction. So our code would look something like:

```	1xx		CLA	tos
6zz		STO	stapsh
1xx		CLA	tos
700		SUB	00
6xx		STO	tos
199		CLA	99
zz	600	stapsh	STO	00
.
.		body of the subroutine
.
1xx		CLA	tos
6xx		STO	tos
6ss		STO	stapop
ss	100	stapop	CLA	00
6rr		STO	rts
rr	800	rts	JMP	00
```

There’s one more aspect of recursive subroutines that is also suitable for other subroutines as well. In particular, subroutines often need input data passed to them by whatever code has called them or temporary variables that are needed during the course of their operation. If a subroutine is not recursive, we can get away with just allocating some fixed memory locations for these. However, in the case of recursive subroutines, we need to make sure that we have fresh ones for each time the subroutine is called and not overwrite the ones that might still be needed by other instances we might return back to. The most natural way to handle this is to allocate them on the stack along with the return address.

Putting all these things together, we can summarize the steps for calling a subroutine in the most general cases:

1. Before calling the subroutine, we push any inputs (also called arguments or parameters) onto the stack.
2. Transfer control to the first instruction of the subroutine, saving the PC (which holds the return address) in the process.
3. If the hardware has not already saved the PC onto the stack, the first thing we do in the subroutine is copy it to the stack.
4. Move the stack pointer to resever space on the stack for any temporary (local) variables the subroutine will need.
5. Before returning, the subroutine readjusts the stack pointer to remove the temporary variables it allocated.
6. If the hardware does not already expect the return address to be on the stack, we need to pop it off the stack and copy it back to where it does need to be.
7. Return control from the subroutine back to the code that called it.
8. Finally, the calling code adjusts the stack pointer to remove the arguments it pushed onto the stack before calling the subroutine.

## Bootstrapping

Like many of the early system designs, the mechanism for loading an initial program into the CARDIAC and getting it running involves a small amount of hardware support and a lot of cleverness. The whole enterprise is often somewhat remenescent of the image of a person attempting to lift themselves off the ground by pulling on their own bootstraps. This is why we usually refrer to the process as bootstrapping or often just booting.

If this is all we did, we’d read all the remaining cards into memory and then the computer would halt when there were no more cards to read. But there’s another trick we can play. If we make the last address-data card pair change location 02 from 800 to a jump to the first instruction of our program, the loader loop will stop and control will transfer to the program we just loaded. So after all of our address-data card pairs, we’ll append the cards 002 and 8xx where xx is the address of the first instruction of our program. The net effect is that we can now load a program and start running it without any manual intervention.

The last piece of this puzzle is how do we include the data we want the program to operate on? It turns out, that’s a simple as just appending the data after the 002 and 8xx cards. When control transfers to the program we loaded, any remaining cards will still be in the reader waiting to be read. When the program executes its first INP instruction, it will happily read the next card, not knowing that there were a bunch of other cards read ahead of it.

So putting all the pieces together, we bootstrap the CARDIAC by putting together a card deck that looks like:

```002
800
.
.
002
8xx	where xx is address of the first instruction
.
.	data cards
.
```

Then we put that deck into the card reader, and start the computer at address 00. The CARDIAC will first load the two-card bootstrap loader, then load the program into memory, then transfer control to the newly loaded program. If the program itself also includes INP instructions, they read the remaining data cards.

## Simulator

We have developed a CARDIAC simulator suitable for running the code discussed on this page. All of the examples in the next section have been tested using this simulator.

To avoid any unnecessary requirements on screen layout, the simulator is laid out a little differently than the physical CARDIAC. At the top of the screen is the CARDIAC logo from a photograph of the actual unit. This picture is also a link back to this page. The next section of the screen is the CARDIAC memory space as appears on the right hand side of the physical device. When the simulator starts up, the value 001 in location 00 and the value 8– in location 99 are preloaded. As a simplification, we don’t use a picture of a ladybug for the program counter, but instead highlight the memory location to which the PC points with a light green background. Each memory location is editable (including the ones that are intended to be fixed), and the tab key moves focus down each column in memory address order.

The bottom section of the simulator is the I/O and CPU. Input is divided into two text areas. The first is the card deck and is editable. The second area is the card reader, and as cards are consumed by the reader they are removed from the listing in the reader. Cards in the deck are loaded into the reader with the Load button. Output cards appear in the Output text area as they are generated with the OUT instruction.

The CPU section of the simulator has four parts showing the status of the CPU and buttons for control. On the top of the CPU section, the Program Counter is shown in an editable text box. Below that is the instruction decoder with non-editable text boxes showing the contents of the Instruction Register and a breakdown of the instruction decoding in the form of an opcode mnemonic and numeric operand. The Accumulator is shown below the instuction decoder. Below the register display are six buttons that control the operation of the simulator:

Reset
The Reset button clears the instruction register, resets the PC and accumulators to 0 and clears the output card deck.
Clear Mem
This button resets all memory locations to blank and re-initializes location 00 to 001 and location 99 to 8–.
Step
Clicking on the Step button causes the simulator to execute the single instruction highlighted in the memory space as pointed to by the program counter. Upon completion of the instruction, the screen is updated to show the state of the computer after the instruction.
Slow
The Slow button causes the simulator to begin executing code starting at the current PC. Instructions are executed at the rate of 10 per second with the screen being updated after each instruction. When the program is run in this way, the movement of the highlighted memory shows the flow of control in the program very clearly.
Run
In the current version of the simulator, the Run button causes the program to be executed beginning from the current PC at the full speed of the JavaScript interpreter. Because of the way JavaScript is typically implemented, the screen contents will not show the effects of code execution until the simulator executes the HRS instruction and the program halts.
Halt
Pressing the Halt button while the program is running in slow mode causes the simulator to stop after the current instruction. The state of the machine remains intact and can be continued with any of the StepSlow, or Run buttons.

## Examples

The remainder of this page are a number of examples of programs written for the CARDIAC. They have all been tested using the simulator described above. Because the memory space of the CARDIAC is so limited, none of the programs are particularly complex. You won’t find a compiler, operating system, or web browser here. However, we do have a few of more complexity than you might expect. There’s a pretty simple program for generating a list of the powers of 2. There’s one that recursively solves the Towers of Hanoi problem. For each of them, we include the assembly language source code with assembled machine language code and a card deck suitable for bootstrapping on the CARDIAC.

Note that most of these examples aren’t the most compact way of solving the problem. Rather, they illustrate techniques as described through this page. The primary exception is the Towers of Hanoi solution which requried some effort to squeeze it into the limited memory space of the CARDIAC.

When we take these programs and turn them into decks of cards to be bootstrapped on the CARDIAC, we get the card decks listed below the program listings. If you cut and paste the list into the input deck of the simulator, hit load, and hit slow, you can see the program get loaded into memory and run.

### Count from 1 to 10

This is sort of our CARDIAC version of “Hello World.” Our objective is simply to print out a set of output cards with the values 1 to 10. We keep two variables to control the process. One, called n keeps track of how many cards we still have left to print. At any point in time it represents that we need to print n+1 more cards. We also have a variable called cntr wich is the number to print out. Each time through the loop, we check to see if n is negative and if so, we’re done. If not, we decrement it, print cntr and then increment cntr.

#### Program Listing

```04	009	n	DATA	009
05	000	cntr	DATA	000

10	100		CLA	00	Initialize the counter
11	605		STO	cntr
12	104	loop	CLA	n	If n < 0, exit
13	322		TAC	exit
14	505		OUT	cntr	Output a card
15	105		CLA	cntr	Increment the card
17	605		STO	cntr
18	104		CLA	n	Decrement n
19	700		SUB	00
20	604		STO	n
21	812		JMP	loop
22	900	exit	HRS	00
```

```002
800
010
100
011
605
012
104
013
322
014
505
015
105
016
200
017
605
018
104
019
700
020
604
021
812
022
900
004
009
002
810
```

### List Reversal

Our next example uses the stack techniques described above to take in a list of cards and output the same list in reverse order. The first card in the input deck (after the bootstrapping and the program code) is the count of how many cards we’re operating on. The remainder of the input deck are the cards to reverse. In the example card deck, we are reversing the first seven Fibonacci numbers.

#### Program Listing

```04	600	storer	DATA	600
06	089	tos	DATA	089	Stack pointer
07	000	acc	DATA	000	Temp for saving accumulator
08	000	n1	DATA	000	Write counter
09	000	n2	DATA	000	Read counter

10	008		IN	n1	Get the number of cards to reverse
11	108		CLA	n1	Initialize a counter
12	609		STO	n2
13	109	rdlp	CLA	n2	Check to see if there are any more cards to read
14	700		SUB	00
15	327		TAC	wrlp
16	609		STO	n2
17	007		IN	acc	Read a card
18	106		CLA	tos	Push it onto the stack
20	625		STO	stapsh
21	106		CLA	tos
22	700		SUB	00
23	606		STO	tos
24	107		CLA	acc
25	600	stapsh	STO	00
26	813		JMP	rdlp
27	108	wrlp	CLA	n1	Check to see if there are any more cards to write
28	700		SUB	00
29	339		TAC	done
30	608		STO	n1
31	106		CLA	tos	Pop a card off the stack
33	606		STO	tos
35	636		STO	stapop
36	100	stapop	CLA	00
37	890		JMP	aprint	Output a card
38	827		JMP	wrlp
39	900	done	HRS	00

90	696	aprint	STO	96	Write a card containing the contents of the accumulator
91	199		CLA	99
92	695		STO	aexit
93	596		OUT	96
94	196		CLA	96
95	800	aexit	JMP	00
```

```002
800
004
600
005
100
006
089
007
000
008
000
009
000
010
008
011
108
012
609
013
109
014
700
015
327
016
609
017
007
018
106
019
204
020
625
021
106
022
700
023
606
024
107
025
600
026
813
027
108
028
700
029
339
030
608
031
106
032
200
033
606
034
205
035
636
036
100
037
890
038
827
039
900
090
696
091
199
092
695
093
596
094
196
095
800
002
810
007
001
001
002
003
005
008
013
```

### Powers of 2

This is a slightly more interesting version of the list from 1 to 10. In this case, we are printing the powers of 2 from 0 to 9. The main difference is that instead of incrementing the number to output, we call a subroutine that doubles it. The program illustrates the use of multiple subroutines as discussed above.

#### Program Listing

```04	000	n	DATA	000
05	009	cntr	DATA	009

10	100		CLA	00	Initialize the power variable with 2^0
11	880		JMP	aprint
12	604	loop	STO	n
13	105		CLA	cntr	Decrement the counter
14	700		SUB	00
15	321		TAC	exit	Are we done yet?
16	605		STO	cntr
17	104		CLA	n
18	890		JMP	double	Double the power variable
19	880		JMP	aprint	Print it
20	812		JMP	loop
21	900	exit	HRS	00

80	686	aprint	STO	86	Print a card with the contents of the accumulator
81	199		CLA	99
82	685		STO	aexit
83	586		OUT	86
84	186		CLA	86
85	800	aexit	JMP	00

90	696	double	STO	96	Double the contents of the accumulator
91	199		CLA	99
92	695		STO	dexit
93	196		CLA	96
95	800	dexit	JMP	00
```

```002
800
005
009
010
100
011
880
012
604
013
105
014
700
015
321
016
605
017
104
018
890
019
880
020
812
021
900
080
686
081
199
082
685
083
586
084
186
090
696
091
199
092
695
093
196
094
296
002
810
```

### Towers of Hanoi

By far the most complex example we include is a solution to the Towers of Hanoi problem. The puzzle consists of three posts on which disks can be placed. We begin with a tower of disks on one post with each disk smaller than the one below it. The other two posts are empty. The objective is to move all of the disks from one post to another subject to the following rules:

1. Only one disk at a time may be moved.
2. No disk may be placed on top of a smaller disk.

According to legend, there is a set of 64 disks which a group of monks are responsible for moving from one post to another. When the puzzle with 64 disks is finally solved, the world will end.

Although the puzzle sounds like it would be difficult to solve, it’s very easy if we think recursively. Moving n disks from Post a to Post b using Post c as a spare can be done as follows:

1. Move n−1 disks from Post a to Post c.
2. Move one disk from Post a to Post b.
3. Move n−1 disks from Post c to Post b.

The CARDIAC doesn’t have enough memory to solve a 64-disk puzzle, but we can solve smaller instances of the problem. In particular, the program we show here can solve up to six disks. The actual number of disks to solve is given by the first data card, and the initial assignment of source destination and spare posts is given on the second data card. The post assignments as well as the output encoding are shown in the following table.

Output Disk Move
000 1 → 3
001 2 → 3
002 3 → 2
003 3 → 1
004 2 → 1
005 1 → 2

For example, the post assignments indicated by a card with the value 3 are that Post 3 is a, Post 2 is c and Post 1 is b. Similarly, an output card with 3 indicates that we are to move a disk from Post 3 to Post 1.

Before trying to understand the details of this program, note that there are several tricks used to reduce the memory usage. The amount of memory available for the stack allows for a puzzle of up to six disks to be solved with this program. Be aware, however, that slow running this program on six disks takes the better part of a half hour to run.

#### Program Listing

```03	031	tos	DATA	031
05	600	storer	DATA	600
06	107	r2ld	DATA	r2
07	001	r2	DATA	001
08	000		DATA	000
09	005	five	DATA	005
10	004		DATA	004
11	003	three	DATA	003
12	002		DATA	002

34	033		INP	32	Get the number of disks from the cards
35	032		INP	31	Get the column ordering from the cards
36	838		JMP	tower	Call the tower solver
37	900		HRS

38	199	tower	CLA	99	Push the return address on the stack
39	890		JMP	push
40	111		CLA	three	Fetch n from the stack
41	870		JMP	stkref
42	700		SUB	00	Check for n=0
43	366		TAC	towdone
44	890		JMP	push	Push n-1 for a recursive call
45	111		CLA	three	Get the first recursive order
46	870		JMP	stkref
47	669		STO	t1
48	109		CLA	five
49	769		SUB	t1
50	890		JMP	push
51	838		JMP	tower	Make first recursive call
52	880		JMP	pop
53	111		CLA	three	Get move to output
54	870		JMP	stkref
55	669		STO	t1
56	569		OUT	t1
57	111		CLA	three	Get second recursive order
58	870		JMP	stkref
60	661		STO	t2
61	100	t2	CLA	00
62	890		JMP	push
63	838		JMP	tower	Make second recursive call
64	880		JMP	pop
65	880		JMP	pop
66	880	towdone	JMP	pop
67	668		STO	towret
68	800	towret	JMP	00

70	679	stkref	STO	refsav	Replace the accumulator with the contents
71	199		CLA	99	of the stack indexed by the accumulator
72	678		STO	refret
73	179		CLA	refsav
76	677		STO	ref
77	100	ref	CLA	00
78	800	refret	JMP	00

80	199	pop	CLA	99	Pop the stack into the accumulator
81	688		STO	popret
82	103		CLA	tos
84	603		STO	tos
86	687		STO	popa
87	100	popa	CLA	00
88	800	popret	JMP	00

90	689	push	STO	pshsav	Push the accumulator on to the stack
91	103		CLA	tos
93	698		STO	psha
94	103		CLA	tos
95	700		SUB	00
96	603		STO	tos
97	189		CLA	pshsav
98	600	psha	STO	00
```

```002
800
003
031
004
100
005
600
006
107
007
001
008
000
009
005
010
004
011
003
012
002
034
033
035
032
036
838
037
900
038
199
039
890
040
111
041
870
042
700
043
366
044
890
045
111
046
870
047
669
048
109
049
769
050
890
051
838
052
880
053
111
054
870
055
669
056
569
057
111
058
870
059
206
060
661
061
100
062
890
063
838
064
880
065
880
066
880
067
668
068
800
070
679
071
199
072
678
073
179
074
203
075
204
076
677
077
100
078
800
080
199
081
688
082
103
083
200
084
603
085
204
086
687
087
100
088
800
090
689
091
103
092
205
093
698
094
103
095
700
096
603
097
189
098
600
002
834
003
000
```

### Pythagorian Triples

The next example comes courtesy of Mark and Will Tapley. It finds sets of three integers which satisfy the Pythagorian property of x2+y2=z2.

#### Discussion

There is much motivation and explanation for this program at:

##### Subroutine to calculate square of a number:

In finding pythagorean triplets, the operation of squaring a number occurs very often, so the program uses a subroutine to perform this function.

• Addresses 076–099 are loaded with the subroutine to utilize the return function hard-wired at address 099.
• Addresses 072–075 are used for data storage for the subroutine.
• Address 072 is loaded with the value 32, one larger than the largest allowable input. The calling program can test an input by subtracting this value from the prospective input and branching if the result is negative. (Negative value means legal input.)
• Address 073 accepts the input to the subroutine. On return, the absolute value of the input will be in this location.
• Address 074 is used as a counter during routine execution.
• Address 075 will contain the calculated square, an integer between 0 and 961 inclusive.
##### Subroutine INPUT:

Store the number to be squared in address 073

-OR-

Load the number to be squared into the accumulator

##### Subroutine OUTPUT:

On return, the square of the input number is in address 075.

The subroutine has a single loop (addresses 090–098). In each loop, it subtracts one from a counter which is initially set to one greater than the input number N, then adds a copy of N into the output address. When the counter reaches 1, the output address contains the sum of N copies of N=N2 and the loop exits, returning program control to the location from which it was called (per the return capability special function of location 99).

##### Limitations:

The square of the input number must have 3 or fewer digits to comply with cell storage limitations. Therefore the input number is checked to be 31 or less (since 322=1024). Violating this condition will cause the subroutine to terminate execution (HRS) with the program counter pointing at location 086. The input number is converted from negative to positive if it was negative, so if the calling program needs a copy of the input, it should store it in some location other than Address 073 (SQIN). After the subroutine executes, that location will contain the absolute value of the input.

##### Main Program:

The main program searches over all allowable lengths of the shortest side S of the right triangles corresponding to pythagorean triplets. For each shortest side, it then searches over all possible lengths of the intermediate side L. For each combination of short and intermediate sides, it checks whether there is a hypotenuse H that satisfies the condition S2+L2=H2. The short side (S) search starts at 0, to avoid missing any triplets with very small values. (This results in identifying the degenerate triplet (0,1,1) which does satisfy 02+12=12 but does not really correspond to a right triangle.) The long side (L) search for each value of S starts at S+1, because L cannot equal S for an integer triplet (see URL above) and if L<S, the corresponding triplet should already have been found with a smaller S. (So, this program will identify (3,4,5) but will not identify (4,3,5).) The hypotenuse (H) search starts at 1.4 times S, since the minimum possible length of the hypotenuse is greater than the square root of 2 (1.404…) times S. (Note: 1.4 times S is calculated by shifting S right and then adding four copies of the result, which is truncated to an integer, to S. For S<10, the result is just S, so the search takes needlessly long until S≥10.)

With the starting values for S, L, and H, the program calculates S2 + L2−H2. If the result is <0, H is too long. In this case, the program increments L and tries again. If the result is =0, a triplet has been found and is printed out. The program then increments L and tries again. If the result is >0, H is too short. In this case, H is incremented and the program tries again. When H is long enough that no more triplets can be found for this value of S, the value of S is incremented, new L and H starting values are calculated, and the loop repeats.

• Addresses 004–009 are used for data storage.
• Address 004 contains S, the smallest member of the triplet (length of the short leg of the triangle) and is initially set to 0.
• Address 005 contains S2, calculated each time S is changed.
• Address 006 contains L, the intermediate member of the triplet (length of the long “leg” of the triangle) and is re-initialized for each smallest member loop to one greater than the smallest member (which is always the minimum possible value for L; see above)
• Address 007 contains L2, calculated each time L is changed.
• Address 008 contains H, the largest member of the triplet (length of the hypotenuse of the triangle) and is initialized for each smallest member to a value <1.4×(the smallest value) (which is always shorter than the minimum possible value for H)
• Address 009 contains H2, calculated each time H is changed. The same address also contains S/10 (S shifted right by one place), used to initialize H each time S is changed. This value is used to set the initial value of H to 1.4 S, which is just less than √2S.

The “outside” loop of the program (addresses 010–067) tests for all possible sets of triplets with the smallest value S stored in 004. After each loop, it increments the value of S and tries again. This loop will terminate when the value of 1.4×S exceeds 31, since the subroutine will no longer be able to calculate correct squares for any possible hypotenuse value (H). The subroutine will halt execution when this input is sent to it. (The outer loop also contains a check to verify that the value of S itself doesn’t exceed 31, but this check is never reached.)

The next-inner loop (addresses 032–061) starts with a value of L=S+1. Any smaller, and L would take the role of S (and hence, the resulting triplet would have already been found with a smaller S) or would be qual to S (and the length of the corresponding hypotenuse would be irrational). This loop terminates on one of two conditions: first, when the value of H exceeds 31 (in which case the subroutine to calculate squares can no longer work); or second, when 2L>S2. This latter condition applies because once L exceeds S2/2, L2 and H2 cannot differ by as little as S2 even if H=L+1. At that point, H2−L2 = (L+1)2−L2=2L+1>S2.

The innermost section (addresses 032–044) calculates the difference S2+L2−H2. If the difference is positive, H is incremented and the loop repeats. If the difference is zero, a triplet has been found and the values of S, L, and H are printed out. If the difference is negative or zero, L is then incremented and the loop repeats. In any case where H is incremented, its new value is checked against the limit for inputs to the subroutine, and if it exceeds that limit, the inner two loops terminate and the outer loop progresses to the next value of S.

##### Independent Verification:

The code below is instructions to Mathematica (tested on versions 8 and 3) which should compute the same output as the above program, but using a more general (and slower) algorithm. It will also generate a plot of triplets by (short side) against (intermediate side).

```candid =
Table[
Table[
Table[
{i, j, k},
{k, j, i^2/2 + 2}
],
{j, i+1, i^2/2 + 1}
],
{i, 0, 31}
];

trips = Select[Flatten[candid, 2], #1[[1]]^2 + #1[[2]]^2 == #1[[3]]^2 & ];

smalltrips = Select[trips, #1[[3]] < 32 & ]

ListPlot[(Take[#1, 2] & ) /@ trips]
```

#### Program Listing

##### Symbol map:
```Address		Variable
04		S			short side = 0 initially
05		S2			square of short side
06		L			long side
07		L2			square of long side
08		H			hypotenuse
09		H2			square of hypotenuse. (Also used
to store S/10 in picking initial
value of H each loop.)
--		----
72		SQLIM			maximum input to Square = 30 initially
73		SQIN			input to square subroutine
74		SQCNT			counter for square subroutine
75		SQOUT			output for square subroutine

Address		Name (as referenced by JMP instruction)
00		BootLp
10		S_Loop
32		L_Loop
45		Next_H
49		PrintTr
52		Inc_L
62		Next_S
--		-----
76		SQacc
77		SQmem
83		SQpos
87		SQgood
90		SQloop

BootLp:
002	800		JMP 	BootLp	Bootstrap loop. Code self-modifies

004	000		(variable)	S Initial value for Short side = 0
072	032		(constant)	SQLIM Limit on input to square = 32

S_Loop:
010	104		CLA	S
011	673		STO	SQIN	Input to square subroutine
012	200		ADD	1	(Using ROM value)
013	606		STO	L	Save long side L
014	877		JMP	SQmem	Square subroutine (saved entry)
015	175		CLA	SQOUT	Retrieve result of subroutine
016	605		STO	S2	Store square of S
017	106		CLA	L	Load L
018	876		JMP	SQacc	Square subroutine, entry using ACC
019	175		CLA	SQOUT	Retrieve result of subroutine
020	607		STO	L2	Store square of L
021	104		CLA	S	Load S
022	401		SFT	01	Divide by 10
023	609		STO	H2	Save S/10 temporarily in H2 location
024	209		ADD	H2	Sum into accumulator
025	209		ADD	H2	Sum into accumulator
026	209		ADD	H2	Sum into accumulator
027	204		ADD	S	Sum is now between S and 1.4 S ~ S sqrt(2)
028	608		STO	H	Store initial hypotenuse H
029	876		JMP	SQacc	Square subroutine (accumulator entry)
030	175		CLA	SQOUT
031	609		STO	H2	Store square of H

L_Loop:
032	105		CLA	S2	Load short side squared
034	709		SUB	H2	Subtract hyp. squared
035	352		TAC	Inc_L	if H2 too big, increment L
036	700		SUB	1	Subtract 1 (ROM)
037	349		TAC	PrintTr	H was just right - print
038	108		CLA	H	H too small, so load H
040	608		STO	H	Store back
041	673		STO	SQIN	Save in input to Square routine
042	772		SUB	SQLIM	Subtract limit for input
043	345		TAC	Next_H	Go on if negative (input < 32)
044	862		JMP	Next_S	Branch to next value of S if not.

Next_H:
045	877		JMP	SQmem	(saved entry)
046	175		CLA	SQOUT	Get result
047	609		STO	H2
048	832		JMP	L_Loop	Try again

PrintTr:
049	504		OUT	S	Print S
050	506		OUT	L	Print L
051	508		OUT	H	Print H

Inc_L:
052	106		CLA	L	Load L
054	606		STO	L	Store
055	876		JMP	SQacc	Square subr.
056	175		CLA	SQOUT	get result
057	607		STO	L2	Store new L squared
058	106		CLA	L	Load new L
059	206		ADD	L	Double it
060	705		SUB	S2	Subtract S^2
061	332		TAC	L_Loop	If S^2 still bigger, keep looking

Next_S:
062	104		CLA	S	Load short side S
064	604		STO	S	Store short side S
065	772		SUB	SQLIM	Subtract upper limit for Square
066	310		TAC	S_Loop	If result is negative, new S is low
enough to loop again
067	900		HRS		Else, S is longer than Square can handle,
so Done - exit.

---	---		--------
SQacc:
076	673		STO	SQIN	Jump here if input value is in ACC

SQmem:
077	173		CLA	SQIN	Jump here if input is already in SQIN
078	773		SUB	SQIN	Input was in both accumulator and SQIN, so this gets 0
079	675		STO	SQOUT	initialize output to 0 for use later
080	773		SUB	SQIN	This gets negative of SQIN
081	383		TAC	SQpos	If the negative is negative, SQIN is positive - good.
082	673		STO	SQIN	If the negative is positive, store that in SQIN.

SQpos:
083	173		CLA	SQIN	Load Absolute value of input
084	772		SUB	SQLIM	Compare against limit value
085	387		TAC	SQgood	Quit if number to square > limit
086	986		HRS		Halt if error on input.

SQgood:
087	173		CLA	SQIN	Retrieve number
089	674		STO	SQCNT	Count is input + 1

SQloop:
090	174		CLA	SQCNT	load counter
091	700		SUB	0	subtract 1
092	674		STO	SQCNT	save new counter value
093	175		CLA	SQOUT	load output
095	675		STO	SQOUT	store cumulative sum
096	100		CLA	0	load 1 (from ROM)
097	774		SUB	SQCNT	subtract counter
098	390		TAC	SQloop	loop again if counter was > 1

Jump out of boot loop to 10 (skips initial increment to S)
002	810		JMP	S_Loop
```

```002
800
004
000
072
032
010
104
011
673
012
200
013
606
014
877
015
175
016
605
017
106
018
876
019
175
020
607
021
104
022
401
023
609
024
209
025
209
026
209
027
204
028
608
029
876
030
175
031
609
032
105
033
207
034
709
035
352
036
700
037
349
038
108
039
200
040
608
041
673
042
772
043
345
044
862
045
877
046
175
047
609
048
832
049
504
050
506
051
508
052
106
053
200
054
606
055
876
056
175
057
607
058
106
059
206
060
705
061
332
062
104
063
200
064
604
065
772
066
310
067
900
076
673
077
173
078
773
079
675
080
773
081
383
082
673
083
173
084
772
085
387
086
986
087
173
088
200
089
674
090
174
091
700
092
674
093
175
094
273
095
675
096
100
097
774
098
390
002
810
```