Quellcode durchsuchen

New assembler

Upgraded assember to be similar to NASM in syntax, as well separated
asm_compiler.py to be as a library to support different architectures
Min vor 6 Jahren
Ursprung
Commit
029d2fba25
2 geänderte Dateien mit 495 neuen und 230 gelöschten Zeilen
  1. 346 230
      tools/asm_compiler.py
  2. 149 0
      tools/risc8asm.py

+ 346 - 230
tools/asm_compiler.py

@@ -1,19 +1,28 @@
 #!/usr/bin/python3
-import sys
-import argparse
 
-from os import path
+import re
+import math
+import traceback
+
+label_re = re.compile(r"^[\w\$\#\@\~\.\?]+$", re.IGNORECASE)
+hex_re = re.compile(r"^[0-9a-f]+$", re.IGNORECASE)
+bin_re = re.compile(r"^[0-1_]+$", re.IGNORECASE)
+oct_re = re.compile(r"^[0-8]+$", re.IGNORECASE)
+
+
+def match(regex, s):
+    return regex.match(s) is not None
 
 
 def decode_bytes(val: str):
     try:
         if val.endswith('h'):
-            return [int(val[i:i+2], 16) for i in range(0, len(val)-1, 2)]
+            return [int(val[i:i + 2], 16) for i in range(0, len(val) - 1, 2)]
         if val.startswith('0x'):
-            return [int(val[i:i+2], 16) for i in range(2, len(val), 2)]
+            return [int(val[i:i + 2], 16) for i in range(2, len(val), 2)]
         if val.startswith('b'):
             val = val.replace('_', '')[1:]
-            return [int(val[i:i+8], 2) for i in range(0, len(val), 8)]
+            return [int(val[i:i + 8], 2) for i in range(0, len(val), 8)]
     except ValueError:
         raise ValueError(f"Invalid binary '{val}'")
     if val.isdigit():
@@ -64,228 +73,335 @@ def decode_reg(r):
     raise ValueError(f"Invalid register name '{r}'")
 
 
-def assemble(file):
-    odata = []
-    afile = open(file, 'r')
-    failed = False
-    refs = dict()
-    for lnum, line in enumerate(afile.readlines()):
-        lnum += 1  # Line numbers start from 1, not 0
-        if '//' in line:
-            line = line[:line.index('//')]
-        if ':' in line:
-            rsplit = line.split(':', 1)
-            ref = rsplit[0]
-            if not ref.isalnum():
-                print(f"{file}:{lnum}: Invalid pointer reference '{ref}'")
-                failed = True
-                continue
-            if ref in refs:
-                if refs[ref][1] is not None:
-                    print(f"{file}:{lnum}: Pointer reference '{ref}' is duplicated with {file}:{refs[ref][0]}")
-                    failed = True
-                    continue
-            refs[ref] = [lnum, len(odata)]
-            line = rsplit[1]
-        line = line.replace('\n', '').replace('\r', '').replace('\t', '')
-        line = line.strip(' ')
-        if line == '':
-            continue
-        ops = line.split()
-        instr = ops[0].upper()
-        rops = 3
-        if instr == 'CPY' or instr == 'COPY':
-            iname = 'COPY'
-            inibb = 0
-        elif instr == 'ADD':
-            iname = 'ADD'
-            inibb = 1
-        elif instr == 'SUB':
-            iname = 'SUB'
-            inibb = 2
-        elif instr == 'AND':
-            iname = 'AND'
-            inibb = 3
-        elif instr == 'OR':
-            iname = 'OR'
-            inibb = 4
-        elif instr == 'XOR':
-            iname = 'XOR'
-            inibb = 5
-        elif instr == 'MUL':
-            iname = 'MUL'
-            inibb = 6
-        elif instr == 'DIV':
-            iname = 'DIV'
-            inibb = 7
-        elif instr == 'SLL':
-            iname = 'SLL'
-            inibb = 9
-        elif instr == 'SHFR':
-            iname = 'SHTR'
-            inibb = 7
-            ops.append(1)
-        elif instr == 'ROTR':
-            iname = 'ROTR'
-            inibb = 7
-            ops.append(2)
-        elif instr == 'LW':
-            iname = 'LW'
-            inibb = 8
-        elif instr == 'SW':
-            iname = 'SW'
-            inibb = 9
-        elif instr == 'JEQ':
-            iname = 'JEQ'
-            rops = 4
-            inibb = 10
-        elif instr == 'JMP' or instr == 'JUMP':
-            iname = 'JUMP'
-            rops = 2
-            inibb = 11
-        elif instr == 'PUSH':
-            iname = 'PUSH'
-            rops = 2
-            inibb = 14
-        elif instr == 'POP':
-            iname = 'POP'
-            rops = 2
-            inibb = 15
+class CompilingError(Exception):
+    def __init__(self, message):
+        self.message = message
+
+
+class InstructionError(Exception):
+    def __init__(self, message):
+        self.message = message
+
+
+class Instruction:
+    def __init__(self, name: str, opcode: str, operands=0, alias=None):
+        name = name.strip().lower()
+        if not name or not name.isalnum():
+            raise InstructionError(f"Invalid instruction name '{name}'")
+        self.name = name.strip()
+        self.alias = alias or []
+        self.opcode = decode_bytes(opcode.replace('?', '0'))[0]
+        self.reg_operands = 0
+        if len(opcode) == 10:
+            if opcode[6:8] == '??':
+                self.reg_operands += 1
+            if opcode[8:10] == '??':
+                self.reg_operands += 1
+        self.imm_operands = operands
+        self.compiler = None
+
+    @property
+    def length(self):
+        return self.imm_operands + 1
+
+    def __len__(self):
+        return self.length
+
+    def _gen_instr(self, regs, imm):
+        instr = self.opcode
+        if len(regs) != self.reg_operands:
+            raise CompilingError(f"Invalid number of registers: set {len(regs)}, required: {self.reg_operands}")
+        limm = 0
+        for i in imm:
+            if isinstance(i, str):
+                if i in self.compiler.labels:
+                    d = self.compiler.labels[i]
+                    limm += len(d)
+                else:
+                    limm += self.compiler.address_size
+            else:
+                limm += len(i)
+        if limm != self.imm_operands:
+            raise CompilingError(f"Invalid number of immediate: set {limm}, required: {self.reg_operands}")
+        if len(regs) == 2:
+            if regs[1] is None:
+                raise CompilingError(f"Unable to decode register name {regs[1]}")
+            if regs[0] is None:
+                raise CompilingError(f"Unable to decode register name {regs[0]}")
+            instr |= regs[1] << 2 | regs[0]
+        elif len(regs) == 1:
+            if regs[0] is None:
+                raise CompilingError(f"Unable to decode register name {regs[0]}")
+            instr |= int(regs[0]) << 2
+        return instr
+
+    def compile(self, operands):
+        regs = []
+        imm = []
+        for i, arg in enumerate(operands):
+            if self.reg_operands > i:
+                regs.append(self.compiler.decode_reg(arg))
+            else:
+                imm.append(self.compiler.decode_bytes(arg))
+
+        instr = self._gen_instr(regs, imm)
+        return [instr] + imm
+
+
+class CompObject:
+    def __init__(self, instr, operands, line_num):
+        self.instr = instr
+        self.operands = operands
+        self.line_num = line_num
+        self.code = []
+        self.code_ref = 0
+
+    def compile(self):
+        self.code = self.instr.compile(self.operands)
+        return self.code
+
+
+class Compiler:
+    def __init__(self, address_size=2, byte_order='little'):
+        self.instr_db = {}
+        self.data = []
+        self.caddress = 0
+        self.labels = {}
+        self.order = byte_order
+        self.regnames = {}
+        self.address_size = address_size
+
+    def decode_reg(self, s: str):
+        s = s.strip()
+        # if s in self.labels:
+        #     b = self.labels[s]
+        if s in self.regnames:
+            b = self.regnames[s]
+        else:
+            b = self.decode_bytes(s)
+        if isinstance(b, bytes):
+            i = int.from_bytes(b, byteorder=self.order)
+        elif isinstance(b, int):
+            i = b
+        else:
+            raise CompilingError(f"Unrecognised register name: {s}")
+        if i not in self.regnames.values():
+            raise CompilingError(f"Invalid register: {s}")
+        return i
+
+    def decode_bytes(self, s: str):
+        s = s.strip()
+        typ = ""
+        # Decimal numbers
+        if s.isnumeric():
+            typ = 'int'
+        elif s.endswith('d') and s[:-1].isnumeric():
+            s = s[:-1]
+            typ = 'int'
+        elif s.startswith('0d') and s[2:].isnumeric():
+            s = s[2:]
+            typ = 'int'
+
+        # Hexadecimal numbers
+        elif s.startswith('0') and s.endswith('h') and match(hex_re, s[1:-1]):
+            s = s[1:-1]
+            typ = 'hex'
+        elif (s.startswith('$0') or s.startswith('0x') or s.startswith('$0')) and match(hex_re, s[2:]):
+            s = s[2:]
+            typ = 'hex'
+
+        # Octal numbers
+        elif (s.endswith('q') or s.endswith('o')) and match(oct_re, s[:-1]):
+            s = s[:-1]
+            typ = 'oct'
+        elif (s.startswith('0q') or s.startswith('0o')) and match(oct_re, s[2:]):
+            s = s[2:]
+            typ = 'oct'
+
+        # Binary number
+        elif (s.endswith('b') or s.endswith('y')) and match(bin_re, s[:-1]):
+            s = s[:-1].replace('_', '')
+            typ = 'bin'
+
+        elif (s.startswith('0b') or s.startswith('0y')) and match(bin_re, s[2:]):
+            s = s[2:].replace('_', '')
+            typ = 'bin'
+
+        # ASCII
+        elif s.startswith("'") and s.endswith("'") and len(s) == 3:
+            s = ord(s[1:-1]).to_bytes(1, self.order)
+            typ = 'ascii'
+
+        # Convert with limits
+        if typ == 'int':
+            numb = int(s)
+            for i in range(1, 9):
+                if -2 ** (i * 7) < i < 2 ** (i * 8):
+                    return numb.to_bytes(i, self.order)
+        elif typ == 'hex':
+            numb = int(s, 16)
+            return numb.to_bytes(int(len(s) / 2) + len(s) % 2, self.order)
+
+        elif typ == 'oct':
+            numb = int(s, 8)
+            for i in range(1, 9):
+                if -2 ** (i * 7) < i < 2 ** (i * 8):
+                    return numb.to_bytes(i, self.order)
+
+        elif typ == 'bin':
+            numb = int(s, 2)
+            return numb.to_bytes(int(len(s) / 8) + len(s) % 8, self.order)
+
         else:
-            if len(ops) == 1:
-                try:
-                    odata += decode_bytes(ops[0])
-                    continue
-                except ValueError:
-                    pass
-            print(f"{file}:{lnum}: Instruction '{ops[0]}' not recognised")
-            failed = True
-            continue
-        if len(ops) != rops:
-            print(f"{file}:{lnum}: {iname} instruction requires {rops - 1} arguments")
-            failed = True
-            continue
-        try:
-            if iname == 'JUMP':
-                odata.append(inibb << 4)
-                try:
-                    odata += decode_bytes(ops[1])
-                except ValueError:
-                    if not ops[1].isalnum():
-                        print(f"{file}:{lnum}: Invalid pointer reference '{ops[1]}'")
-                        failed = True
-                        continue
-                    if ops[1] in refs:
-                        odata.append(refs[ops[1]][1])
-                    else:
-                        refs[ops[1]] = [lnum, None]
-                        odata.append(ops[1])
-                continue
-
-            rd = decode_reg(ops[1])
-            if iname == 'COPY' and not is_reg(ops[2]):
-                imm = decode_bytes(ops[2])[0]
-                odata.append((inibb << 4) | (rd << 2) | rd)
-                odata.append(int(imm))
-                continue
-
-            if iname == 'PUSH' or iname == 'POP':
-                odata.append((inibb << 4) | (rd << 2) | rd)
-                continue
-
-            rs = decode_reg(ops[2])
-            if iname == 'COPY' and rd == rs:
-                print(f"{file}:{lnum}: {iname} cannot copy register to itself")
-                failed = True
-                continue
-
-            odata.append((inibb << 4) | (rd << 2) | rs)
-            if iname == 'JEQ':
-                try:
-                    odata += decode_bytes(ops[3])
-                except ValueError:
-                    if not ops[3].isalnum():
-                        print(f"{file}:{lnum}: Invalid pointer reference '{ops[3]}'")
-                        failed = True
-                        continue
-                    if ops[3] in refs:
-                        odata.append(refs[ops[3]][1])
-                    else:
-                        refs[ops[3]] = [lnum, None]
-                        odata.append(ops[3])
-                continue
-        except ValueError as e:
-            print(f"{file}:{lnum}: {e}")
-            failed = True
-            continue
-
-    afile.close()
-    # Convert jumps
-    for i, l in enumerate(odata):
-        if isinstance(l, str):
-            if refs[l][1] is None:
-                print(f"{file}:{refs[l][0]}: Pointer reference '{l}' does not exist!")
-                failed = True
-                continue
-            odata[i] = refs[l][1]
-
-    return not failed, odata
-
-
-def readable_size(num, disp_bytes=True):
-    num = abs(num)
-    if num < 1024 and disp_bytes:
-        return "[%3.0fB]" % num
-    if num < 1024 and not disp_bytes:
-        return ""
-    num /= 1024.0
-    for unit in ['Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
-        if abs(num) < 1024.0:
-            return "[%3.1f%sB]" % (num, unit)
-        num /= 1024.0
-    return "[%.1f%sB]" % (num, 'Yi')
-
-
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser(description='Assembly compiler', add_help=True)
-    parser.add_argument('file', help='Files to compile')
-    parser.add_argument('-t', '--output_type', choices=['bin', 'mem', 'binary'], default='mem', help='Output type')
-    parser.add_argument('-o', '--output', help='Output file')
-    parser.add_argument('-f', '--force', action='store_true', help='Force override output file')
-    args = parser.parse_args(sys.argv[1:])
-    if not path.isfile(args.file):
-        print(f'No file {args.file}!')
-        sys.exit(1)
-
-    output = args.output
-    if not output:
-        opath = path.dirname(args.file)
-        bname = path.basename(args.file).rsplit('.', 1)[0]
-        ext = '.out'
-        if args.output_type == 'mem':
-            ext = '.mem'
-        elif args.output_type == 'bin':
-            ext = '.bin'
-        output = path.join(opath, bname + ext)
-    if not args.force and path.isfile(output):
-        print(f'Output file already exists {output}!')
-        sys.exit(1)
-
-    success, data = assemble(args.file)
-    if success:
-        print(f"Saving {args.output_type} data to {output}")
-        print(f"Program size: {len(data)}B {readable_size(len(data), False)}")
-        with open(output, 'wb') as of:
-            if args.output_type == 'binary':
-                a = '\n'.join([format(i, '08b') for i in data])
-                of.write(a.encode())
-            elif args.output_type == 'mem':
-                a = [format(i, '02x') for i in data]
-                for i in range(int(len(a) / 8) + 1):
-                    of.write((' '.join(a[i * 8:(i + 1) * 8]) + '\n').encode())
-            elif args.output_type == 'bin':
-                of.write(bytes(data))
-    else:
-        print(f'Failed to compile {args.file}!')
-        sys.exit(1)
-    sys.exit(0)
+            return s
+
+    @staticmethod
+    def _hash_instr(name, operands):
+        return hash(name) + hash(operands)
+
+    def add_reg(self, name, val):
+        self.regnames[name] = val
+        self.regnames['$' + name] = val
+
+    def add_instr(self, instr: Instruction):
+        instr.compiler = self
+        operands = instr.reg_operands + instr.imm_operands
+        # ihash = self._hash_instr(instr.name, operands)
+
+        if instr.name in self.instr_db:
+            raise InstructionError(f"Instruction {instr.name} operands={operands} duplicate!")
+        self.instr_db[instr.name] = instr
+        for alias in instr.alias:
+            # ahash = self._hash_instr(alias, operands)
+            if alias.lower() in self.instr_db:
+                raise InstructionError(f"Instruction alias {alias} operands={operands} duplicate!")
+            self.instr_db[alias.lower()] = instr
+
+    def __func(self, f, args):
+        for arg in args:
+            if arg == '|':
+                pass
+            if arg == '^':
+                pass
+            if arg == '&':
+                pass
+            if arg == '<<':
+                pass
+            if arg == '>>':
+                pass
+            if arg == '+':
+                pass
+            if arg == '-':
+                pass
+            if arg == '*':
+                pass
+            if arg == '/' or arg == '//':
+                pass
+            if arg == '%' or arg == '%%':
+                pass
+
+    def __precompile(self, line):
+        line = line.split(';', 1)[0]
+        if ':' in line:
+            linespl = line.split(':', 1)
+            line = linespl[1]
+            label = linespl[0]
+            if label in self.labels:
+                raise CompilingError(f"Label {label} duplicate")
+            self.labels[label] = (self.caddress).to_bytes(self.address_size, self.order)
+        if line.startswith('%define'):
+            sp = list(filter(None, line.split(' ', 3)))
+            if len(sp) != 3:
+                raise CompilingError(f"Invalid %define")
+            if '(' in sp[1] and ')' in sp[1]:  # Function
+                raise CompilingError(f"%define functions not implemented")
+            self.labels[sp[1]] = self.decode_bytes(sp[2])
+            return
+        instr0 = list(filter(None, line.strip().split(' ', 1)))
+        if len(instr0) == 0:
+            return
+        instr = instr0[0]
+        if len(instr0) == 1:
+            instr0.append('')
+        operands = list(filter(None, map(lambda x: x.strip(), instr0[1].split(','))))
+        if instr.lower() not in self.instr_db:
+            raise CompilingError(f"Instruction {instr} operands={operands} is not recognised!")
+        co = CompObject(self.instr_db[instr.lower()], operands, 0)
+        return co
+
+    def compile(self, file, code):
+        failure = False
+        instr = []
+        binary = []
+        for lnum, line in enumerate(code):
+            lnum += 1
+            try:
+                co = self.__precompile(line)
+                if co is not None:
+                    co.line_num = lnum
+                    self.caddress += co.instr.length
+                    instr.append(co)
+            except CompilingError as e:
+                failure = True
+                print(f"ERROR {file}:{lnum}: {e.message}")
+        for co in instr:
+            try:
+                binary += co.compile()
+            except CompilingError as e:
+                failure = True
+                print(f"ERROR {file}:{co.line_num}: {e.message}")
+            except Exception:
+                failure = True
+                print(f"ERROR {file}:{co.line_num}: Unexpected error:")
+                traceback.print_exc()
+
+        nbin = bytearray()
+        for b in binary:
+            if isinstance(b, int):
+                nbin += b.to_bytes(1, self.order)
+            elif isinstance(b, bytes):
+                nbin += b
+            elif isinstance(b, str):
+                if b in self.labels:
+                    nbin += self.labels[b]
+                else:
+                    failure = True
+                    print(f"ERROR {file}: Unable to find label '{b}'")
+        if failure:
+            return None
+        return nbin
+
+
+def convert_to_binary(data):
+    a = '\n'.join([format(i, '08b') for i in data])
+    return a.encode()
+
+
+def convert_to_mem(data):
+    x = b''
+    fa = f'0{math.ceil(int(math.log2(len(data)))/4)}x'
+    a = [format(d, '02x') for d in data]
+    for i in range(int(len(a) / 8) + 1):
+        y = a[i * 8:(i + 1) * 8]
+        if len(y) > 0:
+            x += (' '.join(y) + '  // ' + format(i*8, fa) + '\n').encode()
+    return x
+
+
+def convert_to_mif(data, depth=32, width=8):
+    x = f'''-- auto-generated memory initialisation file
+DEPTH = {depth};
+WIDTH = {width};
+ADDRESS_RADIX = HEX;
+DATA_RADIX = HEX;
+CONTENT
+BEGIN
+'''.encode()
+    addr_format = f'0{math.ceil(int(math.log2(len(data)))/4)}x'
+    a = [format(i, '02x') for i in data]
+    for i in range(int(len(a) / 8) + 1):
+        y = a[i * 8:(i + 1) * 8]
+        if len(y) > 0:
+            x += (format(i*8, addr_format) + ' : ' + ' '.join(y) + ';\n').encode()
+    x += b"END;"
+    return x

+ 149 - 0
tools/risc8asm.py

@@ -0,0 +1,149 @@
+import argparse
+import sys
+import math
+from os import path
+
+import asm_compiler as compiler
+
+asmc = compiler.Compiler(byte_order='big')
+asmc.add_reg('r0', 0)
+asmc.add_reg('r1', 1)
+asmc.add_reg('r2', 2)
+asmc.add_reg('r3', 3)
+
+
+class MoveInstr(compiler.Instruction):
+    def compile(self, operands):
+        regs = [0, 0]
+        imm = []
+        regs[0] = self.compiler.decode_reg(operands[0])
+        try:
+            regs[1] = self.compiler.decode_reg(operands[1])
+            self.imm_operands = 0
+        except compiler.CompilingError:
+            regs[1] = regs[0]
+            self.imm_operands = 1
+            imm = [self.compiler.decode_bytes(operands[1])]
+        instr = self._gen_instr(regs, imm)
+        return [instr] + imm
+
+
+asmc.add_instr(MoveInstr('MOVE ', 'b0000_????'))
+asmc.add_instr(compiler.Instruction('ADD  ', 'b0001_????'))
+asmc.add_instr(compiler.Instruction('SUB  ', 'b0010_????'))
+asmc.add_instr(compiler.Instruction('AND  ', 'b0011_????'))
+asmc.add_instr(compiler.Instruction('OR   ', 'b0100_????'))
+asmc.add_instr(compiler.Instruction('XOR  ', 'b0101_????'))
+asmc.add_instr(compiler.Instruction('MUL  ', 'b0110_????'))
+asmc.add_instr(compiler.Instruction('DIV  ', 'b0111_????'))
+asmc.add_instr(compiler.Instruction('BR   ', 'b1000_????', 2))
+asmc.add_instr(compiler.Instruction('SLL  ', 'b1001_??00'))
+asmc.add_instr(compiler.Instruction('SRL  ', 'b1001_??01'))
+asmc.add_instr(compiler.Instruction('SRA  ', 'b1001_??10'))
+asmc.add_instr(compiler.Instruction('SRAS ', 'b1001_??11'))
+asmc.add_instr(compiler.Instruction('LWHI ', 'b1010_??00', 3))
+asmc.add_instr(compiler.Instruction('SWHI ', 'b1010_??01'))
+asmc.add_instr(compiler.Instruction('LWLO ', 'b1010_??10', 3))
+asmc.add_instr(compiler.Instruction('SWLO ', 'b1010_??11', 3))
+asmc.add_instr(compiler.Instruction('INC  ', 'b1011_??00'))
+asmc.add_instr(compiler.Instruction('DEC  ', 'b1011_??01'))
+asmc.add_instr(compiler.Instruction('GETAH', 'b1011_??10'))
+asmc.add_instr(compiler.Instruction('GETIF', 'b1011_??11'))
+asmc.add_instr(compiler.Instruction('PUSH ', 'b1100_??00'))
+asmc.add_instr(compiler.Instruction('POP  ', 'b1100_??01'))
+asmc.add_instr(compiler.Instruction('COM  ', 'b1100_??10', 1))
+asmc.add_instr(compiler.Instruction('SETI ', 'b1100_??11'))
+asmc.add_instr(compiler.Instruction('BEQ  ', 'b1101_??00', 3))
+asmc.add_instr(compiler.Instruction('BGT  ', 'b1101_??01', 3))
+asmc.add_instr(compiler.Instruction('BGE  ', 'b1101_??10', 3))
+asmc.add_instr(compiler.Instruction('BZ   ', 'b1101_0011'))
+asmc.add_instr(compiler.Instruction('CALL ', 'b1111_0000', 2))
+asmc.add_instr(compiler.Instruction('RET  ', 'b1111_0001'))
+asmc.add_instr(compiler.Instruction('JUMP ', 'b1111_0010', 2))
+asmc.add_instr(compiler.Instruction('RETI ', 'b1111_0011'))
+asmc.add_instr(compiler.Instruction('CLC  ', 'b1111_0100'))
+asmc.add_instr(compiler.Instruction('SETC ', 'b1111_0101'))
+asmc.add_instr(compiler.Instruction('CLS  ', 'b1111_0110'))
+asmc.add_instr(compiler.Instruction('SETS ', 'b1111_0111'))
+asmc.add_instr(compiler.Instruction('SSETS', 'b1111_1000'))
+asmc.add_instr(compiler.Instruction('CLN  ', 'b1111_1001'))
+asmc.add_instr(compiler.Instruction('SETN ', 'b1111_1010'))
+asmc.add_instr(compiler.Instruction('SSETN', 'b1111_1011'))
+asmc.add_instr(compiler.Instruction('RJUMP', 'b1111_1100', 2))
+asmc.add_instr(compiler.Instruction('RBWI ', 'b1111_1101'))
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description='Assembly compiler', add_help=True)
+    parser.add_argument('file', help='Files to compile')
+    parser.add_argument('-t', '--output_type', choices=['bin', 'mem', 'binary', 'mif'], default='mem', help='Output type')
+    parser.add_argument('-S', '--slice', type=int, default=0, help='if defined, output to multiple sliced files')
+    parser.add_argument('-o', '--output', help='Output file')
+    parser.add_argument('-f', '--force', action='store_true', help='Force override output file')
+    parser.add_argument('-s', '--stdout', action='store_true', help='Print to stdout')
+    parser.add_argument('-l', '--size', type=int, default=0, help='if defined, fill rest of memory with 0x00')
+    args = parser.parse_args(sys.argv[1:])
+    if not path.isfile(args.file):
+        print(f'No file {args.file}!')
+        sys.exit(1)
+
+
+    output = args.output
+    if not output:
+        bname = args.file.rsplit('.', 1)
+        if args.output_type == 'mem':
+            ext = '.mem'
+        elif args.output_type == 'bin':
+            ext = '.bin'
+        elif args.output_type == 'mif':
+            ext = '.mif'
+        else:
+            ext = '.out'
+        output = bname[0] + ext
+    outputs = []
+
+    sformat = f'01d'
+    if args.slice > 0:
+        sformat = f'0{int(math.log10(args.slice)) + 1}d'
+        for i in range(0, args.slice):
+            bname = output.rsplit('.', 1)
+            outputs.append(f'{bname[0]}_{format(i, sformat)}.{bname[1]}')
+    else:
+        outputs = [output]
+    if not args.stdout and not args.force:
+        for output in outputs:
+            if path.isfile(output):
+                print(f'Output file already exists {output}!')
+                sys.exit(1)
+
+    with open(args.file, 'r') as f:
+        data = asmc.compile(args.file, f.readlines())
+    if data is not None:
+        if args.size > 0:
+            data = data + (args.size - len(data)) * bytearray(b'\x00')
+
+        for i, output in enumerate(outputs):
+
+            y = data[i::len(outputs)]
+            if args.output_type == 'binary':
+                x = compiler.convert_to_binary(y)
+            elif args.output_type == 'mem':
+                x = compiler.convert_to_mem(y)
+            elif args.output_type == 'mif':
+                x = compiler.convert_to_mif(y, depth=len(y))
+            else:
+                x = bytes(y)
+
+            op = 'Printing' if args.stdout else 'Saving'
+            print(f"{op} {args.output_type} data '{output}' [Size: {len(y)}B Slice: {format(i + 1, sformat)}/{len(outputs)}]")
+            if args.stdout:
+                print(x.decode())
+            else:
+                with open(output, 'wb') as of:
+                    of.write(x)
+
+        print(f"Total program size: {len(data)}B")
+    else:
+        print(f'Failed to compile {args.file}!')
+        sys.exit(1)
+    sys.exit(0)