Charles Babbage’s
A Java Simulation
Charles Babbage designed (but never built) a
computing machine in the mid-1800s, a machine that
today would be called a digital computer. On this
machine model, Ada Lovelace designed the first
computer program. Babbage’s initial
descriptions of the machine predate Alan Turing's
model of a general purpose computer by about
a hundred years. This Java simulation of the
Analytical Engine will help to preserve the
legacy of these pioneers of computing.
The Analytical Engine machinery is
essentially the same as that of a (simple) modern
digital computer. Here is a brief
description of the hardware including both
the old and new terminologies.
Each instruction for the machine comes on
a card with holes punched in it to indicate which
instruction it represents (that is, its
“opcode”). A program is provided as a
sequence of cards.
The random access memory of the machine is
called the store and the arithmetic logic unit is
called the mill. The mill performs arithmetic
operations on integers, each of which is stored
in a register called an axis
(plural axes). Integers read from
memory are stored in ingress axes and the
result of arithmetic operations are stored
in egress axes. The run-up lever is a flag that is
set when an arithmetic operation causes an
integer overflow.
The machine is controlled by
an attendant who loads programs, starts
and operates the machine while the program
executes, and provides a final report
showing the output of the machine. The machine can
produce textual output via a printer as well as
graphical output via a curve drawing
apparatus, a device we would call a
plotter.
The source code for the Analytical Engine
simulator is available
from GitHub.
This project uses Maven for build
management and requires Java 1.8 to compile.
To create a shell script that runs the simulator,
run
This produces a script that executes the
simulator
at The programming language for this machine is
arcane, but relatively similar to
modern machine languages. For the purposes of
this simulator, an Analytical
Engine program is a text file in which each line
represents a card.
To load a number into the store, use the Store the number 123 in memory location 1.
Blank lines, lines beginning with a period, and
lines beginning withspaces are treated as
comment cards.
In this example there are five comment cards,
three of which are blank.
Arithmetic operations are
automatically executed once the
two operands are loaded from memory into the ingress
axes. The result of the operation is
automatically extracted from the
egress axis and stored in a memory location
indicated by a store card.
Add two numbers from memory locations 1 and
2 and store the result in memory location 3.
Division can be performed on a dividend of
“double width” by loading the lower-order
digits of the dividend in the first main ingress axis and
the higher-order digits of the dividend into the
“prime” ingress axis. The prime axis must be loaded
after the main axis.
The quotient of a division can be accessed from
the prime egress axis, and the remainder from the main
egress axis. If the quotient is too large for the main
axis, the run-up lever is set, and the quotient and
remainder will be set to 0.
Compute 1050 + 1 divided by two. By
default, each axis can store a decimal number of
fifty digits, so loading 1 into the lower
order ingress axis and another 1 into the prime ingress
axis represents 1050 + 1.
Performing the same operation twice does not
require re-specifying the operation.
Multiply two by two, and then multiply that
product by two. Store the result in memory
address 1.
Numbers can be printed to the printer by using
the Print the sum of 123 and 456 to the printer.
What gives this machine its computational
power is conditional execution. A
conditional jump card checks whether the run-up lever has
been set and either reverses or advances the card
chain by a specified number of cards.
In this example, the Engine computes a
factorial by accumulating the
product of n with (n - 1)!
iteratively. At the end of each
iteration, the conditional jump card
decides whether to perform another
iteration of the loop or to terminate and
print the result based on whether the run-up lever has been set.
The simulator performs the following
high-level algorithm
The The One might replace the default Then make your Copyright 2014–2016 Jeffrey Finkelstein.
The code is distributed under the terms of
the GNU General Public License
version 3. It is derived from
the original
code by John Walker, which
was dedicated
to the public domain.
This document is distributed under the terms
of the Creative
Commons Attribution-ShareAlike 4.0
International license.
Hardware overview
Preparing the simulator
git clone https://github.com/jfinkels/analyticalengine
mvn package appassembler:assemble
target/appassembler/bin/analyticalengine
. An
Analytical Engine program stored in a file
named myprogram.ae
, for example, can then be
executed via
target/appassembler/bin/analyticalengine myprogram.ae
Programming the Engine
N
card.
Example
N1 123
Example
This program loads two numbers into the store.
N1 123
. This is another comment.
N2 456
Example
N1 123
N2 456
+
L1
L2
S3
Example
N1 1
N2 1
N3 2
/
L2
L1'
L3
Print the quotient.
S3'
P
Print the remainder.
S4
P
Example
N1 2
N2 2
*
L1
L2
S1
L1
L2
S1
P
card. To remain historically
accurate, the simulator does not print
directly to standard output when it
encounters a print card; instead, the attendant
collects all printed strings and makes them available only
after the program has terminated.
Example
N1 123
N2 456
+
L1
L2
S3
P
Example
Computes 6! using a loop.
Address 0 is the current value of n.
Address 1 is the accumulator for the product.
Address 2 represents the constant 1.
N0 6
N1 1
N2 1
*
L1
L0
S1
-
L0
L2
S0
If 1 - n < 0, go back 12 cards (including this one!)
L2
L0
CB?12
L1
P
Programmatic access to the simulator
Attendant
interface and
the AnalyticalEngine
interface are the most
significant parts of the simulator
interface. Once a card chain is prepared, the
attendant loads the cards into the Engine using the
Attendant.loadProgram()
method. Once the cards are
loaded, calling the AnalyticalEngine.run()
method
executes the program. Any output is
collected by the attendant and can be accessed
via the Attendant.finalReport()
method.
AnalyticalEngine
must be
programmatically assembled before
it can execute a program. The Engine needs a
mill, a store, a card reader, etc., each of which can be set
using the appropriately named setter
methods. The code is designed so that each
component can be easily reimplemented
and replaced.
Example
HashMap
-based
implementation of Store
with
one backed by a remote key-value data store. In this
example, we
use Jedis, a
Redis client for the Java programming language:
package example;
import java.math.BigInteger;
import java.net.URI;
import analyticalengine.components.Store;
import redis.clients.jedis.Jedis;
public class RedisStore implements Store {
private Jedis client = null;
public RedisStore(URI uri) {
this.client = new Jedis(uri);
}
public BigInteger get(long address) {
String addressString = String.valueOf(address);
String valueString = this.client.get(addressString);
BigInteger value = new BigInteger(valueString);
return value;
}
public void put(long address, BigInteger value) {
String addressString = String.valueOf(address);
String valueString = String.valueOf(value)
String status = this.client.set(addressString, valueString);
if (!status.equals("OK")) {
throw new RuntimeException(status);
}
}
public void reset() {
this.client.clear();
}
}
AnalyticalEngine
instance use this
store:
AnalyticalEngine engine = new DefaultAnalyticalEngine();
engine.setStore(new RedisStore());
Copyright license