## Digi-Comp ii Simulator Program using Python

The Digi-Comp ii was a very cool mechanical computer from the 1960s. See https://en.wikipedia.org/wiki/Digi-Comp_II.

Original Digi-Comp ii Manual

Original Digi-Comp ii was made of plastic

A couple of years ago I created a large, but simplified variation (no subtraction), made of laser-cut acrylic plastic and called it GraviComp:

GraviComp under construction

GraviComp completed

GraviComp video

https://channel9.msdn.com/Shows/themakershow/The-Maker-Show-Mini-GraviComp-Mechanical-Computer

As part of the design process for GraviComp, I wrote a simulation program in Python. A reader of this blog site recently asked me for a copy of the Python code, so I dusted off an old version I found and converted the code from Python 2 (which I was using two years ago) to Python 3.

Simulation program using Python

Warning: The code is quite tricky and I didn’t spend very much time on the simulation program and so I’m certain that the code has a few minor bugs.

My demo sets up a 3 * 13 problem and then simulates the balls rolling down the machine, toggling flip-fops and switches. The result ends up in the A register and is (1, 1, 1, 0, 0, 1, 0) = one 1, one 2, one 4, no 8, no 16, one 32, no 64 = 1 + 2 + 4 + 32 = 39.

```# digi_comp_sim.py
# Simulates the Digi-Comp ii mechanical computer
# Python 3.x

#==================================================================

class DigiComp:
# top, bottom - number balls in top, bottom reservoirs
# multiply, clear, count - control switches. off is to the right
# cf1, cf2 - carry flags. cf1 off is left, cf2 off to the right
# d1, d2, d3 - distributor flip-flops. off to the right
# q1, q2, q3 - aka MQ - multipurpose. off to the right
# m1, m2, m3, m4 - 4-bit memory. off to the right
# a1, a2, a3, a4, a5, a6, a7 - 7-bit accumulator. off to the left
# t1, t2, t3, t4, t5, t6 - toggles for accumulators. off to left
# overflowHalt - halt on overflow? off to the left
# complement - used for subtraction and division. off to right
# manual - manual/automatic switch. off to the right

def __init__(self):
pass

# manual calls this procedure 'Initialize'
def initialize(self, top):
self.top = top
self.bottom = 0
self.multiply = self.clear = self.count = 0
self.cf1 = self.cf2 = 0
self.d1 = self.d2 = self.d3 = 0
self.q1 = self.q2 = self.q3 = 0
self.m1 = self.m2 = self.m3 = self.m4 = 0
self.a1 = self.a2 = self.a3 = self.a4 = self.a5 = \
self.a6 = self.a7 = 0
self.t1 = self.t2 = self.t3 = self.t4 = self.t5 = \
self.t6 = 0
self.overflowHalt = 0
self.complement = 0
self.manual = 0

def display(self):
print('=====================================================')
print('Top reservoir:                     ' , self.top , \
' balls')
print('Multiply, Clear, Count switches:   ' , self.multiply  ,\
' ' , self.clear , ' ' , self.count)
print('CF1, CF2 carry flags:              ' , self.cf1 , ' ' ,\
self.cf2)
print('D1, D2, D3 distributor flip-flops: ' , self.d1 , ' ' ,\
self.d2 , ' ' , self.d3)
print('Q1, Q2, Q3 multiply register:      ' , self.q1 , ' ' ,\
self.q2 , ' ' , self.q3)
print('M1, M2, M3, M4 memory register:    ' , self.m1 , ' ' ,\
self.m2 , ' ' , self.m3 , ' ' , self.m4)
print('A1, A2, . . . A7 memory register:  ' , self.a1 , ' ' ,\
self.a2 , ' ' , self.a3 , ' ' , self.a4 , ' ' , self.a5 , ' ' ,\
self.a6 , ' ' , self.a7)
print('T1, T2, . . . T6 toggle switches:  ' , self.t1 , ' ' ,\
self.t2 , ' ' , self.t3 , ' ' , self.t4 , ' ' , self.t5 , ' ' ,\
self.t6)
print('Overflow halt switch:              ' , \
self.overflowHalt)
print('Complement switch:                 ' , \
self.complement)
print('Manual operation switch:           ' , \
self.manual)
print('Bottom reservoir:                  ' , \
self.bottom , ' balls')
print('=====================================================')

#==================================================================

def do_Start(self):
if self.top == 0:
print("No balls in top reservoir")
self.display()
else:
self.top = self.top - 1 # launch ball
self.do_Multiply()

def do_Multiply(self):
if self.multiply == 0: self.do_Clear() # go right
elif self.multiply == 1: self.do_D2() # go left

def do_Clear(self): # clear the accumulator
if self.clear == 0: self.do_count()
elif self.clear == 1:
a1 = a2 = a3 = a4 = a5 = a6 = a7 = 0
self.do_Manual()

def do_Count(self):
if self.count == 0: self.do_CF1()
elif self.count == 1: self.do_A1()

def do_Manual(self):
if self.manual == 0:
self.bottom = self.bottom + 1
self.do_Start() # man = 0 -> do automatic
elif self.manual == 1:
self.bottom = self.bottom + 1
input('Hit enter to continue')

#==================================================================

def do_D2(self): # top D flip-flop
if self.d2 == 0: self.d2 = 1; self.do_D1() # 0 is right. go left
elif self.d2 == 1: self.d2 = 0; self.do_D3() # go right, toggle

def do_D1(self): # left D flip-flop
if self.d1 == 0: self.d1 = 1; self.do_Q1() # go left and toggle
elif self.d1 == 1: self.d1 = 0; self.do_M3() # go right, toggle

def do_D3(self): # right D flip-flop
if self.d3 == 0: self.d3 = 1; self.do_M2()
elif self.d3 == 1: self.d3 = 0; self.do_M1()

#==================================================================

def do_Q1(self):
if self.q1 == 0: self.q1 = 1; self.do_Q2()
elif self.q1 == 1: self.q1 = 0; self.do_M4()

def do_Q2(self):
if self.q2 == 0: self.q2 = 1; self.do_Q3()
elif self.q2 == 1: self.q2 = 0; self.do_M4()

def do_Q3(self):
if self.q3 == 0:
self.q3 = 1
self.bottom = self.bottom + 1 # drop through and halt
elif self.q3 == 1:
self.q3 = 0; self.do_M4()

#==================================================================

def do_M1(self):
if self.m1 == 0: self.do_manual()
elif self.m1 == 1: self.do_A1()

def do_M2(self):
if self.m2 == 0: self.do_Manual()
elif self.m2 == 1: self.do_A2()

def do_M3(self):
if self.m3 == 0: self.do_manual()
elif self.m3 == 1: self.do_A3()

def do_M4(self):
if self.m4 == 0: self.do_manual()
elif self.m4 == 1: self.do_A4()

#==================================================================

def do_A1(self):
if self.a1 == 0: self.a1 = 1; self.do_T1()
elif self.a1 == 1: self.a1 = 0; self.do_A2()

def do_A2(self):
if self.a2 == 0: self.a2 = 1; self.do_T2()
elif self.a2 == 1: self.a2 = 0; self.do_A3()

def do_A3(self):
if self.a3 == 0: self.a3 = 1; self.do_T3()
elif self.a3 == 1: self.a3 = 0; self.do_A4()

def do_A4(self):
if self.a4 == 0: self.a4 = 1; self.do_T4()
elif self.a4 == 1: self.a4 = 0; self.do_A5()

def do_A5(self):
if self.a5 == 0: self.a5 = 1; self.do_T5()
elif self.a5 == 1: self.a5 = 0; self.do_A6()

def do_A6(self):
if self.a6 == 0: self.a6 = 1; self.do_T6()
elif self.a6 == 1: self.a6 = 0; self.do_A7()

def do_A7(self):
if self.a7 == 0: self.a7 = 1; self.do_Complement()
elif self.a7 == 1: self.a7 = 0; self.do_OverflowHalt()

#==================================================================

def do_T1(self): # off = 0 = to the left
if self.t1 == 0: self.do_T2()
elif self.t1 == 1: self.do_A2()

def do_T2(self):
if self.t2 == 0: self.do_T3()
elif self.t2 == 1: self.do_A3()

def do_T3(self):
if self.t3 == 0: self.do_T4()
elif self.t3 == 1: self.do_A4()

def do_T4(self):
if self.t4 == 0: self.do_T5()
elif self.t4 == 1: self.do_A5()

def do_T5(self):
if self.t5 == 0: self.do_T6()
elif self.t5 == 1: self.do_A6()

def do_T6(self):
if self.t6 == 0: self.do_Complement()
elif self.t5 == 1: self.do_A7()

#==================================================================

def do_Complement(self):
if self.complement == 0: self.do_Manual() # 0 is to the right
elif self.complement == 1: self.do_CF2()

def do_OverflowHalt(self): # left = 0 = do not halt
if self.overflowHalt == 0:
self.do_Complement() # go automatic
elif self.overflowHalt == 1:
self.bottom = self.bottom + 1 # drop through and halt

#==================================================================

def do_CF1(self): # left is 0!!
if self.cf1 == 0:
self.cf1 = 1
# hidden, underneath operations!
if self.t1 == 0: self.t1 = 1
elif self.t1 == 1: self.t1 = 0
if self.t2 == 0: self.t2 = 1
elif self.t2 == 1: self.t2 = 0
if self.t3 == 0: self.t3 = 1
elif self.t3 == 1: self.t3 = 0
if self.t4 == 0: self.t4 = 1
elif self.t4 == 1: self.t4 = 0
if self.t5 == 0: self.t5 = 1
elif self.t5 == 1: self.t5 = 0
if self.t6 == 0: self.t6 = 1
elif self.t6 == 1: self.t6 = 0
self.do_Manual();
elif self.cf1 == 1: self.cf1 = 0; self.do_A1()

def do_CF2(self): # right is 0!!
if self.cf2 == 0:
self.cf2 = 1
self.do_Manual();
elif self.cf2 == 1:
self.cf2 = 0
self.bottom = self.bottom + 1 # drop through and halt

def do_Manual(self):
if self.manual == 0:
self.bottom = self.bottom + 1
self.do_Start() # man = 0 -> do automatic
elif self.manual == 1:
input('Hit enter to continue')

# end class DigiComp

#==================================================================

print("\nBegin Digi-Comp ii simulation \n")
dc = DigiComp()
dc.initialize(35)   # put 35 balls in top reservoir
print("\nInitial state: ")
dc.display()

# set up 3 * 13 problem
dc.q1 = 1; dc.q2 = 1; dc.q3 = 0  # 110 = 1+2 = 3d in Q register
dc.m1 = 1; dc.m2 = 0; dc.m3 = 1; dc.m4 = 1  # 1011 = 13d in M reg
dc.multiply = 1   # multiply operation code
print("\nSettings to compute 3 * 13 (in Q and M registers):")
dc.display()
# result in A should be 1110010 (top-to-bottom) = 1+2+4+32 = 39d

#==================================================================

##set up a count-the-balls problem
#dc.count = 1
##result in A should be 35d = 1100010 = 1+2+32

## set up 13 + 71 problem
## 100 = '+' operation code (note pdf manual has 'top' on right
## 100 = '+' operation code (note pdf manual has 'top' on right
#dc.q1 = 1; dc.q2 = 0; dc.q3 = 0
#dc.multiply = 1 # 2nd part of '+' op code
#dc.m1 = 1; dc.m2 = 0; dc.m3 = 1; dc.m4 = 1 # 1011 = 1+2+8 = 13d
#dc.a1 = 1; dc.a2 = 1; dc.a3 = 1; dc.a4 = 0; dc.a5 = 0;
# dc.a6 = 0; dc.a7 = 1 # 1110001 = 1+2+4+64 = 71d
#print("\nSettings to compute 13 + 71 (in M and A registers):")
#dc.display()
## result in A should be 84d = 0010101 = 4+16+64

#==================================================================

## set up 12 - 4 problem
#dc.complement = 1 # for subtraction
## 0011 = 12d (larger number goes in M) (15 maximum)
#dc.m1 = 0; dc.m2 = 0; dc.m3 = 1; dc.m4 = 1
#dc.a1 = 0; dc.a2 = 0; dc.a3 = 1; dc.a4 = 0; dc.a5 = 0; dc.a6 = 0;
#dc.a7 = 0
## 0010000 = 4d (smaller value goes in accumulator) (14 maximum)
## Q = 100 is addition (used for subtraction too)
#dc.q1 = 1; dc.q2 = 0; dc.q3 = 0
#dc.do_Start() # will pause
#dc.multiply = 1 # now 'really' add
#dc.complement = 0 # complement off
## result in A should be 0001000 = 8d

#==================================================================

# set up 25 / 5 problem
#dc.q1 = 1; dc.q2 = 1; dc.q3 = 1 # divide op code
#dc.complement = 1; # part of divide op code
#dc.a1 = 1; dc.a2 = 0; dc.a3 = 0; dc.a4 = 1; dc.a5 = 1; dc.a6 = 0;
# dc.a7 = 0 # 1001100 = 1+8+16 = 25d (numerator in A)
## 1010 = 5d (denominator in M)
#dc.m1 = 1; dc.m2 = 0; dc.m3 = 1; dc.m4 = 0
#dc.do_Start() # will pause
#dc.complement = 0
#dc.multiply = 1
#dc.overflowHalt = 1
## toggled result is in Q -> for 25/5 Q should have 010 (actual)
## -> 101 (mentally toggle) = 1+4 = 5d

#==================================================================

dc.do_Start()
print("\nResult - answer in A register except division problems:")
dc.display()

print("\nEnd Digi-Comp ii simulation \n")

# end of script
```
This entry was posted in Miscellaneous. Bookmark the permalink.