zombie-assembler/zasm.py
Quantum 2001c4fc12
Fixed shit, added other shit
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
2021-11-06 17:43:12 -04:00

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')