View difference between Paste ID: edAEkfYa and hk6H9PsJ
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()