asm.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #!env python
  2. # Created:20080312
  3. # By Jeff Connelly
  4. #
  5. # 3-trit computer assembler
  6. import sys, os
  7. def main():
  8. if len(sys.argv) < 2:
  9. print """usage: %s program.t
  10. input file: program.t - assembly code
  11. outputs:
  12. \tprogram.3 - assembled tritstream
  13. \tprogram.sp - SPICE file suitable for use with swrom-fast
  14. """ % (sys.argv[0])
  15. raise SystemExit
  16. if not sys.argv[1].endswith(".t"):
  17. print "Input assembly filename must end in .t"
  18. raise SystemExit
  19. asmfile = file(sys.argv[1], "rt")
  20. tritstream_filename = sys.argv[1].replace(".t", ".3")
  21. tritstream_file = file(tritstream_filename, "wt")
  22. spice_filename = sys.argv[1].replace(".t", ".sp")
  23. spice_file = file(spice_filename, "wt")
  24. tritstream = assemble(asmfile)
  25. tritstream_file.write(tritstream)
  26. spice_file.write(tospice(tritstream))
  27. def tospice(tritstream):
  28. """Convert tritstream to SPICE source for 3x3 swrom-fast."""
  29. # Read down, then across (one instruction per _column_, not row)
  30. code = [
  31. [None,None,None],
  32. [None,None,None],
  33. [None,None,None]
  34. ]
  35. all_instr = []
  36. for i in range(0,3):
  37. all_instr.append(tritstream[i*3:i*3+3])
  38. code[0][i] = tritstream[i*3 + 0]
  39. code[1][i] = tritstream[i*3 + 1]
  40. code[2][i] = tritstream[i*3 + 2]
  41. s = "; swrom-fast include file, generated to by asm/asm.py, for tritstream:\n"
  42. for instr in all_instr:
  43. s += "; " + instr + "\n"
  44. s += "\n"
  45. s += """; Select a voltage value based on the logic input at A
  46. .func choose(A,for_n,for_z,for_p) {if(A<={V_N_max},for_n,if(A>={V_P_min},for_p,for_z))}
  47. ; Threshold voltages
  48. .param V_N_max=-2
  49. .param V_P_min=2
  50. """
  51. i = 0
  52. for row in code:
  53. s += ".func program_%s(A) {choose(A," % ({0:"i", 1:0, 2:1}[i],)
  54. i += 1
  55. voltages = []
  56. for col in row:
  57. v = "V("
  58. if col == "i":
  59. v += "_1"
  60. elif col == "1":
  61. v += "1"
  62. else:
  63. v += "0"
  64. v += ")"
  65. voltages.append(v)
  66. s += ",".join(voltages)
  67. s += ")}\n"
  68. return s
  69. def assemble(asmfile):
  70. """Return a serialized tritstream of asmfile assembled."""
  71. pc = 0 # PC goes 0, 1, -1 _NOT_ -1, 0, 1 (0 on power-up)
  72. labels = {}
  73. opcode_map = { "lwi": [0], "cmp": [-1], "be": [1] }
  74. register_map = { "in": [-1], "out": [0], "a": [1] }
  75. # TODO: balanced trinary conversion routines
  76. literal_map = {
  77. "4": [1, 1],
  78. "3": [1, 0],
  79. "2": [1, -1],
  80. "1": [0, 1],
  81. "0": [0, 0],
  82. "-1": [0, -1],
  83. "-2": [-1, 1],
  84. "-3": [-1, 0],
  85. "-4": [-1, -1]
  86. }
  87. tritstream = []
  88. while True:
  89. line = asmfile.readline()
  90. # End on EOF
  91. if len(line) == 0:
  92. break
  93. # Skip blank lines
  94. line = line.strip()
  95. if len(line) == 0:
  96. continue
  97. label, opcode, operands = parse_line(line)
  98. if opcode == None:
  99. continue
  100. #print [label, opcode, operands]
  101. machine_code = []
  102. if label is not None:
  103. labels[label] = [pc]
  104. machine_code.extend(opcode_map[opcode])
  105. for op in operands:
  106. x = register_map.get(op,
  107. labels.get(op,
  108. literal_map.get(op)))
  109. if x is None:
  110. print "Bad register, label, or literal: %s" % (op, )
  111. print "Labels: %s" % (labels,)
  112. raise SystemExit
  113. else:
  114. machine_code.extend(x)
  115. assert len(machine_code) == 3, \
  116. "Incorrect operand count, instruction trits:: %s" % (machine_code,)
  117. tritstream.extend(machine_code)
  118. pc += 1
  119. if pc > 3:
  120. print "Too many instructions, need exactly 3 but pc=%d" % (pc,)
  121. raise SystemExit
  122. if pc != 3:
  123. print "Too few instructions, need exactly 3 but pc=%d" % (pc,)
  124. raise SystemExit
  125. #print labels
  126. # Execution goes 0, 1, i
  127. # but we ordered i, 0, 1.
  128. # Arrange the instructions so that the first
  129. # instruction written is placed at 0, next at
  130. # 1, then the last instruction at i--since
  131. # execution begins at 0, this will perserve the order.
  132. tritstream_ordered = (
  133. tritstream[3*2:3*2+3] + # last instruction executed
  134. tritstream[3*0:3*0+3] + # <-- PC starts here (0)
  135. tritstream[3*1:3*1+3] # second instruction executed
  136. )
  137. # Serialize tritstream
  138. s = ""
  139. for t in tritstream_ordered:
  140. if t == -1:
  141. s += "i"
  142. else:
  143. s += str(t)
  144. return s
  145. def parse_line(line):
  146. # Strip comments
  147. without_comment = line.split(";", 1)[0]
  148. if len(without_comment) == 0:
  149. return (None, None, None)
  150. # Separate label from instruction
  151. label_and_instruction = without_comment.split(":", 1)
  152. if len(label_and_instruction) > 1:
  153. label, instruction = label_and_instruction
  154. label = label.strip()
  155. else:
  156. instruction, = label_and_instruction
  157. label = None
  158. instruction = instruction.strip()
  159. # Separate opcode from operands
  160. tokens = instruction.split()
  161. opcode = tokens[0]
  162. operands = []
  163. for token in tokens[1:]:
  164. token = token.replace(",", "")
  165. operands.append(token)
  166. return (label, opcode, operands)
  167. if __name__ == "__main__":
  168. main()