Quantum
2001c4fc12
Raw data can be added with db db "Strings" db 123 32 12 32 db 0x12 0x24 0x54 db 0x12 123 32 0x23 Added mov, call, and ret instructions ip register renamed to pc values are now emitted as 16 bits values can now be labels
173 lines
5.3 KiB
Python
173 lines
5.3 KiB
Python
class Things:
|
|
opcodes = {
|
|
'nop': 0x00,
|
|
'hlt': 0x01,
|
|
'ld': 0x04,
|
|
'push': 0x05,
|
|
'pop': 0x06,
|
|
'jmp': 0x07,
|
|
'jz': 0x08,
|
|
'cmp': 0x09,
|
|
'inc': 0x0A,
|
|
'dec': 0x0B,
|
|
'addi': 0x0C,
|
|
'subi': 0x0D,
|
|
'add': 0x0E,
|
|
'sub': 0x0F,
|
|
'muli': 0x10,
|
|
'divi': 0x11,
|
|
'mul': 0x12,
|
|
'div': 0x13,
|
|
'test': 0x16,
|
|
'prt': 0x17,
|
|
'mov': 0x18,
|
|
'call': 0x19,
|
|
'ret': 0x1A,
|
|
|
|
'je': 0x08,
|
|
}
|
|
|
|
registers = {
|
|
'pc': 0x00,
|
|
'sp': 0x01,
|
|
'flags': 0x03,
|
|
'a': 0x04,
|
|
'b': 0x05,
|
|
'c': 0x06,
|
|
'd': 0x07,
|
|
'r8': 0x08,
|
|
'r9': 0x09,
|
|
'r10': 0x0A,
|
|
'r11': 0x0B,
|
|
'r12': 0x0C,
|
|
'r13': 0x0D,
|
|
'r14': 0x0E,
|
|
'r15': 0x0F,
|
|
'r16': 0x10,
|
|
}
|
|
|
|
class ZASM:
|
|
def __init__(self):
|
|
self._out = []
|
|
self._pc = 0
|
|
self._org = 0
|
|
self._labels = {}
|
|
self._to_be_resolved = {}
|
|
|
|
def emit8(self, value):
|
|
self._out.append(value & 0xFF)
|
|
self._pc += 1
|
|
|
|
def emit16(self, value):
|
|
self._out.append((value & 0xFF00) >> 8)
|
|
self._out.append(value & 0xFF)
|
|
self._pc += 2
|
|
|
|
def set8(self, addr, value):
|
|
self._out[addr - self._org] = bytes([value & 0xFF])
|
|
|
|
def set16(self, addr, value):
|
|
print(self._out)
|
|
self._out[addr - self._org] = (value & 0xFF00) >> 8
|
|
self._out[addr - self._org + 1] = value & 0xFF
|
|
|
|
def assemble(self, path):
|
|
with open(path, 'r') as f:
|
|
lines = f.readlines()
|
|
|
|
line_no = 0
|
|
for line in lines:
|
|
# Strip away bullshit
|
|
line = line.replace(',', '')
|
|
line = line.replace('\n', '')
|
|
|
|
# Increment line number
|
|
line_no += 1
|
|
|
|
# Ignore blank lines and comments
|
|
if line == '':
|
|
continue
|
|
if line.startswith('#'):
|
|
continue
|
|
|
|
# Is it a label?
|
|
if not line.startswith(' '):
|
|
if line.startswith('org'):
|
|
self._pc = int(line.split(' ')[1], 16)
|
|
self._org = self._pc
|
|
else:
|
|
self._labels[line.split(':')[0]] = self._pc
|
|
# It is code
|
|
else:
|
|
line = line[4:]
|
|
chunks = line.split(' ')
|
|
print(chunks)
|
|
if chunks[0] == 'db':
|
|
if chunks[1].startswith('"') and chunks[-1].endswith('"'):
|
|
for c in ' '.join(chunks[1:])[1:-1]:
|
|
self.emit8(ord(c))
|
|
self.emit8(0)
|
|
else:
|
|
for c in chunks[1:]:
|
|
if c.startswith('0x'):
|
|
self.emit8(int(c, 16))
|
|
else:
|
|
self.emit8(int(c))
|
|
continue
|
|
elif chunks[0] in Things.opcodes:
|
|
self.emit8(Things.opcodes[chunks[0]])
|
|
else:
|
|
print(f'Unknown opcode: {chunks[0]} at {line_no}: {line}')
|
|
break
|
|
|
|
# SUUUUPER dirty hack to do indirect addressing
|
|
#if chunks[1].startswith('[') and chunks[1].endswith(']'):
|
|
# self.set8(self._pc - 1, Things.opcodes[chunks[0]] - 2)
|
|
# chunk[1] = chunk[1][1:-1]
|
|
if len(chunks) == 1:
|
|
self.emit16(0)
|
|
elif chunks[1].startswith('#'):
|
|
self.emit16(int(chunks[1][1:]))
|
|
elif chunks[1] in Things.registers:
|
|
self.emit16(Things.registers[chunks[1]])
|
|
else:
|
|
self._to_be_resolved[self._pc] = (chunks[1], line_no, line)
|
|
self.emit16(0xDADA)
|
|
# print(f'Unknown register or invalid immediate: {chunks[1]!r}')
|
|
# break
|
|
|
|
# SUUUUPER dirty hack to do indirect addressing
|
|
#if chunks[2].startswith('[') and chunks[2].endswith(']'):
|
|
# self.set8(self._pc - 2, Things.opcodes[chunks[0]] - 2)
|
|
# chunk[2] = chunk[2][1:-1]
|
|
if len(chunks) <= 2:
|
|
self.emit16(0)
|
|
elif chunks[2] in Things.registers:
|
|
self.emit16(Things.registers[chunks[2]])
|
|
elif chunks[2].startswith('#'):
|
|
self.emit16(int(chunks[2][1:]))
|
|
else:
|
|
self._to_be_resolved[self._pc] = (chunks[2], line_no, line)
|
|
self.emit16(0xDADA)
|
|
# print(f'Invalid immediate: {chunks[2]} at {line_no}: {line}')
|
|
# break
|
|
|
|
for loc, (label, line_no, line) in self._to_be_resolved.items():
|
|
print(loc, label, line_no, line)
|
|
print(self._labels)
|
|
if label in self._labels.keys():
|
|
self.set16(loc, self._labels[label])
|
|
else:
|
|
print(f'Failed to resolve {label} at {line_no}: {line}')
|
|
break
|
|
|
|
def write(self, path):
|
|
with open(path, 'wb') as f:
|
|
f.write(bytes(self._out))
|
|
self._out = b''
|
|
|
|
if __name__ == '__main__':
|
|
asm = ZASM()
|
|
|
|
asm.assemble('test.zasm')
|
|
asm.write('test.bin')
|