Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python3
- """tptasm.py
- TPTASM is an assembly language for The Powder Toy's "8-bit processor v1.0" (see http://powdertoy.co.uk/Discussions/Thread/View.html?Thread=17358). Assembly languages make low-level programming a lot easier by getting rid of the bit manipulation and replacing it with friendlier words and numbers that actually show what the code does.
- Here is an overview of its syntax:
- * Comments start with semicolons (";") e.g. "; This is a comment"
- * RAM addresses are represented with square brackets e.g.
- "[6]" = RAM SLOT 6.
- * To set A, B, C, RAM addresses or STDOUT to something
- use "SET" e.g. "SET A, [6]" (sets A to the value at RAM
- SLOT 7) or "SET [15], 5" (sets RAM SLOT 15 to 5) or "SET
- OUT, [54]" (outputs RAM SLOT 54 to STDOUT)
- * To utilise the ALU use "ADD", "AND" etc. e.g. "ADD [4]"
- (adds A + B and puts the result in RAM SLOT 4) or
- "OR C" (puts A OR B in C)
- * To jump to a line use "JMP" e.g. "JMP 3" (jump to ROM SLOT 3)
- * To conditionally jump to a line use "IFE" e.g. "IFE 5" (if
- A doesn't equal B, jump to line 5)
- * To quit use END by itself on a line.
- """
- import re
- import sys
- # The regex for a TPTASM number
- number_re = r"(?:[0-9]+|0[xX][0-9a-fA-F]+|0[bB][0-1]+)"
- # The regex for a TPTASM name
- name_re = r"(?:[A-Za-z_][A-Za-z0-9_]*)"
- # The regex for a line of TPTASM code
- line_re = re.compile(r"""
- ^
- \s*
- (?:(?P<label>{name}):)? # label
- \s*
- (?P<thingy>
- (?P<line>
- (?P<name>SET|ADD|SUB|AND|OR|IFE|IFG|IFL|JMP|CLR|END)
- ( (?<=SET) \s+(?P<set1>A|B|C|OUT|\[{number}\]|\${name}|\[B\])\s*,\s*(?P<set2>A|B|C|\[{number}\]|\${name}|{number}|INP)
- | (?<=ADD) \s+(?P<add1>\[{number}\]|\${name}|C)
- | (?<=SUB) \s+(?P<sub1>\[{number}\]|\${name}|C)
- | (?<=AND) \s+(?P<and1>\[{number}\]|\${name}|C)
- | (?<= OR) \s+(?P<or1>\[{number}\]|\${name}|C)
- | (?<=IFE) \s+(?P<ife1>{number}|{name})
- | (?<=IFG) \s+(?P<ifg1>{number}|{name})
- | (?<=IFL) \s+(?P<ifl1>{number}|{name})
- | (?<=JMP) \s+(?P<jmp1>{number}|{name})
- | (?<=CLR) \s+(?P<clr1>\[(?:A|B|{number})\]|\${name})
- | (?<=END)
- )
- )
- )?
- \s*
- (?P<comment>;.*)? # comment
- $
- """.format(number=number_re, name=name_re), re.X)
- def isaddr(s):
- """Check if a string is a TPTASM memory address."""
- return s.startswith('[') and isnum(s[1:-1])
- def isvar(s):
- """Check if a string is a TPTASM variable."""
- return s.startswith("$")
- def isnum(s):
- """Check if a string is a valid Python integer."""
- try:
- int(s, 0)
- except:
- return False
- return True
- def num(s):
- """Turn a number string or RAM address into a Python integer."""
- if s.startswith('['):
- return int(s[1:-1], 0)
- return int(s, 0)
- def binary(s, width):
- """Make a number binary. Also restrict it to a certain width by adding
- preceding zeros (when necessary)."""
- # YAY double string formatting :)
- return ("{:>0%s}" % width).format(bin(num(s))[2:])
- def getvar(name, varmap):
- """Get a variable's address from a varmap.
- If the variable does not exist, create it and give it an address."""
- if name not in varmap:
- if len(varmap) == 0:
- n = 1
- else:
- n = sorted(varmap.values())[-1] + 1
- varmap[name] = n
- #print("Added var {} [{}]".format(name, n))
- if n > 100:
- print("WARNING: RAM all used up!")
- return "[" + str(varmap[name]) + "]"
- def parse(text):
- """Parse multiple lines of code and output the compiled program in
- a list of binary strings."""
- lines = text.upper().split('\n')
- out = []
- labelmap = {}
- varmap = {}
- instrno = -1
- for lineno, line in enumerate(lines):
- line2 = line_re.match(line)
- if line2 is None:
- raise BaseException("syntax error on line {}".format(lineno + 1))
- if line2.groupdict()['label'] is not None:
- labelmap[line2.groupdict()['label']] = instrno + 2
- if line2.groupdict()['line'] is None:
- continue
- instrno += 1
- instrno = -1
- for lineno, line in enumerate(lines):
- line2 = line_re.match(line)
- gd = line2.groupdict()
- if gd['line'] is None:
- continue
- instrno += 1
- if gd['name'] == 'SET':
- set1 = gd['set1']
- set2 = gd['set2']
- if isvar(set1):
- set1 = getvar(set1, varmap)
- if isvar(set2):
- set2 = getvar(set2, varmap)
- if set1 == 'A' and isnum(set2):
- out.append("{}00000000000000001".format(binary(set2, 8)))
- elif set1 == 'B' and isnum(set2):
- out.append("{}00000000000000010".format(binary(set2, 8)))
- elif set1 == 'C' and isnum(set2):
- out.append("{}00000000000000011".format(binary(set2, 8)))
- elif isaddr(set1) and isnum(set2):
- out.append("{}0{}000000100".format(binary(set2, 8), binary(set1, 7)))
- elif set1 == 'A' and isaddr(set2):
- out.append("111111110{}000000101".format(binary(set2, 7)))
- elif set1 == 'B' and isaddr(set2):
- out.append("111111110{}000000110".format(binary(set2, 7)))
- elif set1 == 'C' and isaddr(set2):
- out.append("111111110{}000000111".format(binary(set2, 7)))
- elif set1 == 'A' and set2 == 'C':
- out.append("0000000000000000000001000")
- elif set1 == 'B' and set2 == 'C':
- out.append("0000000000000000000001001")
- elif isaddr(set1) and set2 == 'C':
- out.append("000000000{}000001010".format(binary(set1, 7)))
- elif set1 == 'OUT' and isaddr(set2):
- out.append("111111110{}000001101".format(binary(set2, 7)))
- elif set1 == '[B]' and set2 == 'A':
- out.append("0000000000000000000001110")
- elif isaddr(set1) and set2 == "INP":
- out.append("000000000{}000010010".format(binary(set1, 7)))
- else:
- raise Exception("unsupported SET operation on line {}".format(lineno + 1))
- elif gd['name'] == 'ADD':
- if isvar(gd['add1']):
- gd['add1'] = getvar(gd['add1'], varmap)
- if gd['add1'] == 'C': # register C
- out.append("0000000000000000000101011")
- else: # RAM value
- number = bin(int(gd['add1'][1:-1], 0))[2:] # convert to binary
- out.append("000000000{:>07}000101100".format(number))
- elif gd['name'] == 'AND':
- if isvar(gd['and1']):
- gd['and1'] = getvar(gd['and1'], varmap)
- if gd['and1'] == 'C': # register C
- out.append("0000000000000000001001011")
- else: # RAM value
- number = bin(int(gd['and1'][1:-1], 0))[2:] # convert to binary
- out.append("000000000{:>07}001001100".format(number))
- elif gd['name'] == 'OR':
- if isvar(gd['or1']):
- gd['or1'] = getvar(gd['or1'], varmap)
- if gd['or1'] == 'C': # register C
- out.append("0000000000000000001101011")
- else: # RAM value
- number = bin(int(gd['or1'][1:-1], 0))[2:] # convert to binary
- out.append("000000000{:>07}001101100".format(number))
- elif gd['name'] == 'IFE':
- if isnum(gd['ife1']): # line number
- number = bin(int(gd['ife1'], 0))[2:] # convert to binary
- else: # label
- number = bin(labelmap[gd['ife1']])[2:]
- out.append("000000000{:>07}010001011".format(number))
- elif gd['name'] == 'JMP':
- if isnum(gd['jmp1']): # line number
- number = bin(int(gd['jmp1'], 0))[2:] # convert to binary
- else: # label
- number = bin(labelmap[gd['jmp1']])[2:]
- out.append("000000000{:>07}010101011".format(number))
- elif gd['name'] == 'IFG':
- if isnum(gd['ifg1']): # line number
- number = bin(int(gd['ifg1'], 0))[2:] # convert to binary
- else: # label
- number = bin(labelmap[gd['ifg1']])[2:]
- out.append("000000000{:>07}011001011".format(number)) # made up
- elif gd['name'] == 'IFL':
- if isnum(gd['ifl1']): # line number
- number = bin(int(gd['ifl1'], 0))[2:] # convert to binary
- else: # label
- number = bin(labelmap[gd['ifl1']])[2:]
- out.append("000000000{:>07}011101011".format(number)) # made up
- elif gd['name'] == 'SUB':
- if isvar(gd['sub1']):
- gd['sub1'] = getvar(gd['sub1'], varmap)
- if gd['add1'] == 'C': # register C
- out.append("0000000000000000100001011") # made up
- else: # RAM value
- number = bin(int(gd['sub1'][1:-1], 0))[2:] # convert to binary
- out.append("000000000{:>07}100001100".format(number)) # made up
- elif gd['name'] == 'CLR':
- if isvar(gd['clr1']):
- gd['clr1'] = getvar(gd['clr1'], varmap)
- if gd['clr1'] == '[A]':
- out.append("0000000000000000000010000")
- elif gd['clr1'] == '[B]':
- out.append("0000000000000000000010001")
- else: # RAM address
- out.append("000000000{}000001111".format(binary(gd['clr1'], 7)))
- elif gd['name'] == 'END':
- out.append("0000000010000000000000000")
- else:
- raise Exception("something strange happened! Please report this bug!")
- if not all([len(i) == 25 for i in out]):
- print(out)
- for i, v in enumerate(out):
- if len(v) != 25:
- print(i, v)
- raise Exception("uh-oh, something bad happened! Please report this bug!")
- return out
- def print_(s):
- print("[{}][{}][{}][{}][{}]".format(s[:8], s[8:9], s[9:16], s[16:20], s[20:]))
- def main(argv):
- f = sys.stdin
- if len(argv) > 1:
- f = open(argv[1])
- if f == sys.stdin:
- print(
- """TPTASM is an assembly language for The Powder Toy's "8-bit processor v1.0" (see http://powdertoy.co.uk/Discussions/Thread/View.html?Thread=17358). Assembly languages make low-level programming a lot easier by getting rid of the bit manipulation and replacing it with friendlier words and numbers that actually show what the code does.
- Here is an overview of its syntax:
- * Comments start with semicolons (";") e.g. "; This is a comment"
- * RAM addresses are represented with square brackets e.g.
- "[6]" = RAM SLOT 6.
- * To set A, B, C, RAM addresses or STDOUT to something
- use "SET" e.g. "SET A, [6]" (sets A to the value at RAM
- SLOT 7) or "SET [15], 5" (sets RAM SLOT 15 to 5) or "SET
- OUT, [54]" (outputs RAM SLOT 54 to STDOUT)
- * To utilise the ALU use "ADD", "AND" etc. e.g. "ADD [4]"
- (adds A + B and puts the result in RAM SLOT 4) or
- "OR C" (puts A OR B in C)
- * To make a label use colons e.g. "mainloop:"
- * To jump to a line use "JMP" e.g. "JMP 3" (jump to ROM SLOT 3)
- or use labels e.g. "JMP mainloop" (jump to the label "mainloop")
- * To conditionally jump to a line use "IFE" e.g. "IFE 5" (if
- A doesn't equal B, jump to line 5)
- * To quit use END by itself on a line.
- """)
- print("Enter code (press {} to finish):".format(("Ctrl-D", "Ctrl-Z")[sys.platform == "win32"]))
- program = f.read()
- result = parse(program)
- if f == sys.stdin:
- print("Here is your program! Enjoy!")
- for i in result:
- yield i
- if len(result) > 100:
- print("WARNING: program too long! ({})".format(len(result)))
- if f == sys.stdin:
- sys.stdin.readline()
- if __name__ == "__main__":
- for i in main(sys.argv):
- print_(i)
Advertisement
Add Comment
Please, Sign In to add comment