ZASM
Made while drunk because zombie kept giving me shitty bytecode Kill me now
This commit is contained in:
commit
61db978eae
1 changed files with 155 additions and 0 deletions
155
zasm.py
Normal file
155
zasm.py
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
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,
|
||||||
|
|
||||||
|
'je': 0x08,
|
||||||
|
}
|
||||||
|
|
||||||
|
registers = {
|
||||||
|
'ip': 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(' ')
|
||||||
|
if 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.emit8(0)
|
||||||
|
elif chunks[2] in Things.registers:
|
||||||
|
self.emit8(Things.registers[chunks[2]])
|
||||||
|
elif chunks[2].startswith('#'):
|
||||||
|
self.emit8(int(chunks[2][1:]))
|
||||||
|
else:
|
||||||
|
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')
|
Loading…
Reference in a new issue