SHOW:
|
|
- or go back to the newest paste.
1 | #!/usr/bin/env python3 | |
2 | ||
3 | """tptasm.py | |
4 | ||
5 | 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. | |
6 | ||
7 | Here is an overview of its syntax: | |
8 | ||
9 | * Comments start with semicolons (";") e.g. "; This is a comment" | |
10 | * RAM addresses are represented with square brackets e.g. | |
11 | "[6]" = RAM SLOT 6. | |
12 | * To set A, B, C, RAM addresses or STDOUT to something | |
13 | use "SET" e.g. "SET A, [6]" (sets A to the value at RAM | |
14 | SLOT 7) or "SET [15], 5" (sets RAM SLOT 15 to 5) or "SET | |
15 | OUT, [54]" (outputs RAM SLOT 54 to STDOUT) | |
16 | * To utilise the ALU use "ADD", "AND" etc. e.g. "ADD [4]" | |
17 | (adds A + B and puts the result in RAM SLOT 4) or | |
18 | "OR C" (puts A OR B in C) | |
19 | * To jump to a line use "JMP" e.g. "JMP 3" (jump to ROM SLOT 3) | |
20 | * To conditionally jump to a line use "IFE" e.g. "IFE 5" (if | |
21 | A doesn't equal B, jump to line 5) | |
22 | * To quit use END by itself on a line. | |
23 | """ | |
24 | ||
25 | import re | |
26 | import sys | |
27 | ||
28 | # The regex for a TPTASM number | |
29 | number_re = r"(?:[0-9]+|0[xX][0-9a-fA-F]+|0[bB][0-1]+)" | |
30 | ||
31 | # The regex for a line of TPTASM code | |
32 | line_re = re.compile(r""" | |
33 | ^ | |
34 | \s* | |
35 | (?P<line> | |
36 | (?P<name>SET|ADD|AND|OR|IFE|JMP|END) | |
37 | ( (?<=SET)\s+(?P<set1>A|B|C|OUT|\[{number}\])\s*,\s*(?P<set2>A|B|C|\[{number}\]|{number}) | |
38 | | (?<=ADD)\s+(?P<add1>\[{number}\]|C) | |
39 | | (?<=AND)\s+(?P<and1>\[{number}\]|C) | |
40 | | (?<= OR)\s+(?P<or1>\[{number}\]|C) | |
41 | | (?<=IFE)\s+(?P<ife1>{number}) | |
42 | | (?<=JMP)\s+(?P<jmp1>{number}) | |
43 | | (?<=END) | |
44 | ) | |
45 | )? | |
46 | \s* | |
47 | (?P<comment>;.*)? # comment | |
48 | $ | |
49 | """.format(number=number_re), re.X) | |
50 | ||
51 | def isaddr(s): | |
52 | """Check if a string is a PTASM memory address.""" | |
53 | return s.startswith('[') | |
54 | ||
55 | def isnum(s): | |
56 | """Check if a string is a valid Python integer.""" | |
57 | try: | |
58 | int(s, 0) | |
59 | except: | |
60 | return False | |
61 | return True | |
62 | ||
63 | def num(s): | |
64 | """Turn a number string or RAM address into a Python integer.""" | |
65 | if s.startswith('['): | |
66 | return int(s[1:-1], 0) | |
67 | return int(s, 0) | |
68 | ||
69 | def binary(s, width): | |
70 | """Make a number binary. Also restrict it to a certain width by adding | |
71 | preceding zeros (when necessary).""" | |
72 | return ("{:>0%s}" % width).format(bin(num(s))[2:]) | |
73 | ||
74 | def parse(text): | |
75 | """Parse multiple lines of code and output the compiled program in | |
76 | a list of binary strings.""" | |
77 | lines = text.upper().split('\n') | |
78 | out = [] | |
79 | for lineno, line in enumerate(lines): | |
80 | line2 = line_re.match(line) | |
81 | if line2 is None: | |
82 | raise BaseException("syntax error on line {}".format(lineno + 1)) | |
83 | gd = line2.groupdict() | |
84 | if gd['line'] is None: | |
85 | continue | |
86 | if gd['name'] == 'SET': | |
87 | set1 = gd['set1'] | |
88 | set2 = gd['set2'] | |
89 | if set1 == 'A' and isnum(set2): | |
90 | out.append("{}0000000000000001".format(binary(set2, 8))) | |
91 | elif set1 == 'B' and isnum(set2): | |
92 | out.append("{}0000000000000010".format(binary(set2, 8))) | |
93 | elif set1 == 'C' and isnum(set2): | |
94 | out.append("{}0000000000000011".format(binary(set2, 8))) | |
95 | elif isaddr(set1) and isnum(set2): | |
96 | out.append("{}0{}0000100".format(binary(set1, 7), binary(set2, 8))) | |
97 | elif set1 == 'A' and isaddr(set2): | |
98 | out.append("111111110{}00000101".format(binary(set2, 7))) | |
99 | elif set1 == 'B' and isaddr(set2): | |
100 | out.append("111111110{}00000110".format(binary(set2, 7))) | |
101 | elif set1 == 'C' and isaddr(set2): | |
102 | out.append("111111110{}00000111".format(binary(set2, 7))) | |
103 | elif set1 == 'A' and set2 == 'C': | |
104 | out.append("000000000000000000001000") | |
105 | elif set1 == 'B' and set2 == 'C': | |
106 | out.append("000000000000000000001001") | |
107 | elif isaddr(set1) and set2 == 'C': | |
108 | out.append("000000000{}00001010".format(binary(set1, 7))) | |
109 | elif set1 == 'OUT' and isaddr(set2): | |
110 | out.append("111111110{}00001101".format(binary(set2, 7))) | |
111 | else: | |
112 | raise Exception("unsupported SET operation") | |
113 | elif gd['name'] == 'ADD': | |
114 | if gd['add1'] == 'C': # register C | |
115 | out.append("000000000000000000011011") | |
116 | else: # RAM value | |
117 | number = bin(int(gd['add1'][1:-1], 0))[2:] # convert to binary | |
118 | out.append("000000000{:>07}00011100".format(number)) | |
119 | elif gd['name'] == 'AND': | |
120 | if gd['and1'] == 'C': # register C | |
121 | out.append("000000000000000000101011") | |
122 | else: # RAM value | |
123 | number = bin(int(gd['and1'][1:-1], 0))[2:] # convert to binary | |
124 | out.append("000000000{:>07}00101100".format(number)) | |
125 | elif gd['name'] == 'OR': | |
126 | if gd['or1'] == 'C': # register C | |
127 | out.append("000000000000000000111011") | |
128 | else: # RAM value | |
129 | number = bin(int(gd['or1'][1:-1], 0))[2:] # convert to binary | |
130 | out.append("000000000{:>07}00111100".format(number)) | |
131 | elif gd['name'] == 'IFE': | |
132 | number = bin(int(gd['ife1'], 0))[2:] # convert to binary | |
133 | out.append("000000000{:>07}01000000".format(number)) | |
134 | elif gd['name'] == 'JMP': | |
135 | number = bin(int(gd['jmp1'], 0))[2:] # convert to binary | |
136 | out.append("000000000{:>07}01010000".format(number)) | |
137 | elif gd['name'] == 'END': | |
138 | out.append("000000001000000000000000") | |
139 | else: | |
140 | raise Exception("something strange happened! Please report this bug!") | |
141 | ||
142 | ||
143 | return out | |
144 | ||
145 | if __name__ == "__main__": | |
146 | print( | |
147 | """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. | |
148 | ||
149 | Here is an overview of its syntax: | |
150 | ||
151 | * Comments start with semicolons (";") e.g. "; This is a comment" | |
152 | * RAM addresses are represented with square brackets e.g. | |
153 | "[6]" = RAM SLOT 6. | |
154 | * To set A, B, C, RAM addresses or STDOUT to something | |
155 | use "SET" e.g. "SET A, [6]" (sets A to the value at RAM | |
156 | SLOT 7) or "SET [15], 5" (sets RAM SLOT 15 to 5) or "SET | |
157 | OUT, [54]" (outputs RAM SLOT 54 to STDOUT) | |
158 | * To utilise the ALU use "ADD", "AND" etc. e.g. "ADD [4]" | |
159 | (adds A + B and puts the result in RAM SLOT 4) or | |
160 | "OR C" (puts A OR B in C) | |
161 | * To jump to a line use "JMP" e.g. "JMP 3" (jump to ROM SLOT 3) | |
162 | * To conditionally jump to a line use "IFE" e.g. "IFE 5" (if | |
163 | A doesn't equal B, jump to line 5) | |
164 | * To quit use END by itself on a line. | |
165 | """) | |
166 | print("Enter code (press {} to finish):".format(("Ctrl-D", "Ctrl-Z")[sys.platform == "win32"])) | |
167 | program = sys.stdin.read() | |
168 | result = parse(program) | |
169 | print("Here is your program! Enjoy!") | |
170 | for i in result: | |
171 | - | print(i) |
171 | + | print(i) |
172 | sys.stdin.readline() |