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
## so reads 001)
## 100 = '+' operation code (note pdf manual has 'top' on right
## so reads 001)
#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
Advertisements
This entry was posted in Miscellaneous. Bookmark the permalink.