diff options
author | Astatin <[email protected]> | 2024-09-26 15:05:21 +0900 |
---|---|---|
committer | Astatin <astatin@redacted> | 2024-09-26 15:05:21 +0900 |
commit | 0b44780a7b142147d25f0f436159b876f95f17f7 (patch) | |
tree | 398412fdfb253e7ca6db2845e78877322dc84c7a | |
parent | ef861b3e9b9800259e011581e01a853d3a122bf4 (diff) |
So for some reason, I didn't change anything and it started working. I guess it's good then. Also cleaned up the tests files
-rw-r--r-- | .gitignore | 1 | ||||
-rwxr-xr-x | gbasm | 419 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | test.gbasm | 4 |
4 files changed, 2 insertions, 424 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8567fcd --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +gameboy-asm @@ -1,419 +0,0 @@ -#!/bin/python -import argparse -import string -from os.path import abspath, dirname - -def padTo(args, addr): - if (args[0] == 0): - raise ValueError(".PADTO with label or to 0x0000 is not allowed") - - if (addr > args[0]): - raise ValueError(".PADTO cannot pad to 0x{:04x} when starting from 0x{:04x}".format(args[0], addr)) - - return (args[0] - addr) * [0] - -labels = {} -constants = {} - -def absolute16bAddrToRelative8b(absolute_addr, base_addr): - print(absolute_addr, base_addr) - if absolute_addr == -1: - return 0 - new_addr = (absolute_addr - base_addr - 2) - if new_addr < -127 or new_addr > 128: - raise ValueError("JR is too far from its destination. Please use JP instead") - return new_addr & 0xff - -instructions = [ - { "opcode": "LD", "params": [ - { "type": ["r", "r"], "format": lambda args, _: [0b01000000 | (args[0] << 3) | args[1]] }, - { "type": ["r", "8b"], "format": lambda args, _: [0b00000110 | (args[0] << 3), args[1]] }, - { "type": ["r", "(HL)"], "format": lambda args, _: [0b01000110 | (args[0] << 3)] }, - { "type": ["(HL)", "r"], "format": lambda args, _: [0b01110000 | args[1]] }, - { "type": ["(HL)", "8b"], "format": lambda args, _: [0b00110110, args[1]] }, - { "type": ["A", "(BC)"], "format": lambda args, _: [0b00001010] }, - { "type": ["A", "(DE)"], "format": lambda args, _: [0b00011010] }, - { "type": ["(BC)", "A"], "format": lambda args, _: [0b00000010] }, - { "type": ["(DE)", "A"], "format": lambda args, _: [0b00010010] }, - { "type": ["A", "(nn)"], "format": lambda args, _: [0b11111010, args[1] & 0xff, args[1] >> 8] }, - { "type": ["(nn)", "A"], "format": lambda args, _: [0b11101010, args[0] & 0xff, args[0] >> 8] }, - { "type": ["A", "(C)"], "format": lambda args, _: [0b11110010] }, - { "type": ["(C)", "A"], "format": lambda args, _: [0b11100010] }, - { "type": ["A", "(n)"], "format": lambda args, _: [0b11110000, args[1]] }, - { "type": ["(n)", "A"], "format": lambda args, _: [0b11100000, args[0]] }, - { "type": ["A", "(HL-)"], "format": lambda args, _: [0b00111010] }, - { "type": ["(HL-)", "A"], "format": lambda args, _: [0b00110010] }, - { "type": ["A", "(HL+)"], "format": lambda args, _: [0b00101010] }, - { "type": ["(HL+)", "A"], "format": lambda args, _: [0b00100010] }, - { "type": ["rr", "16b"], "format": lambda args, _: [0b00000001 | (args[0] << 4), args[1] & 0xff, args[1] >> 8] }, - { "type": ["(nn)", "SP"], "format": lambda args, _: [0b00001000, args[0] & 0xff, args[0] >> 8] }, - { "type": ["SP", "HL"], "format": lambda args, _: [0b11111001] }, - { "type": ["HL", "8b"], "format": lambda args, _: [0b11111000, args[1]] }, - ]}, - { "opcode": "PUSH", "params": [ - { "type": ["rr"], "format": lambda args, _: [0b11000101 | (args[0] << 4)] }, - ]}, - { "opcode": "POP", "params": [ - { "type": ["rr"], "format": lambda args, _: [0b11000001 | (args[0] << 4)] }, - ]}, - { "opcode": "ADD", "params": [ - { "type": ["r"], "format": lambda args, _: [0b10000000 | (args[0])] }, - { "type": ["(HL)"], "format": lambda args, _: [0b10000110] }, - { "type": ["8b"], "format": lambda args, _: [0b11000110, args[0]] }, - { "type": ["SP", "8b"], "format": lambda args, _: [0b11101000, args[1]] }, - - # TODO: HL,rr (x9 ?) - ]}, - { "opcode": "ADC", "params": [ - { "type": ["r"], "format": lambda args, _: [0b10001000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b10001110] }, - { "type": ["8b"], "format": lambda args, _: [0b11001110, args[0]] }, - ]}, - { "opcode": "SUB", "params": [ - { "type": ["r"], "format": lambda args, _: [0b10010000 | (args[0])] }, - { "type": ["(HL)"], "format": lambda args, _: [0b10010110] }, - { "type": ["8b"], "format": lambda args, _: [0b11010110, args[0]] }, - ]}, - { "opcode": "SBC", "params": [ - { "type": ["r"], "format": lambda args, _: [0b10011000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b10011110] }, - { "type": ["8b"], "format": lambda args, _: [0b11011110, args[0]] }, - ]}, - { "opcode": "CP", "params": [ - { "type": ["r"], "format": lambda args, _: [0b10111000 | (args[0])] }, - { "type": ["(HL)"], "format": lambda args, _: [0b10111110] }, - { "type": ["8b"], "format": lambda args, _: [0b11111110, args[0]] }, - ]}, - { "opcode": "INC", "params": [ - { "type": ["r"], "format": lambda args, _: [0b00000100 | (args[0] << 3)] }, - { "type": ["(HL)"], "format": lambda args, _: [0b00110100] }, - - # THIS ONE IS SPECULATIVE - { "type": ["rr"], "format": lambda args, _: [0b00000011 | (args[0] << 4)] }, - ]}, - { "opcode": "DEC", "params": [ - { "type": ["r"], "format": lambda args, _: [0b00000101 | (args[0] << 3)] }, - { "type": ["(HL)"], "format": lambda args, _: [0b00110101] }, - - { "type": ["rr"], "format": lambda args, _: [0b00001011 | (args[0] << 4)] }, - ]}, - { "opcode": "AND", "params": [ - { "type": ["r"], "format": lambda args, _: [0b10100000 | (args[0])] }, - { "type": ["(HL)"], "format": lambda args, _: [0b10100110] }, - { "type": ["8b"], "format": lambda args, _: [0b11100110, args[0]] }, - ]}, - { "opcode": "OR", "params": [ - { "type": ["r"], "format": lambda args, _: [0b10110000 | (args[0])] }, - { "type": ["(HL)"], "format": lambda args, _: [0b10110110] }, - { "type": ["8b"], "format": lambda args, _: [0b11110110, args[0]] }, - ]}, - { "opcode": "XOR", "params": [ - { "type": ["r"], "format": lambda args, _: [0b10101000 | (args[0])] }, - { "type": ["(HL)"], "format": lambda args, _: [0b10101110] }, - { "type": ["8b"], "format": lambda args, _: [0b11101110, args[0]] }, - ]}, - { "opcode": "CCF", "params": [ - { "type": [], "format": lambda args, _: [0b00111111] }, - ]}, - { "opcode": "SCF", "params": [ - { "type": [], "format": lambda args, _: [0b00110111] }, - ]}, - { "opcode": "DAA", "params": [ - { "type": [], "format": lambda args, _: [0b00100111] }, - ]}, - { "opcode": "CPL", "params": [ - { "type": [], "format": lambda args, _: [0b00101111] }, - ]}, - { "opcode": "JP", "params": [ - { "type": ["16b"], "format": lambda args, _: [0b11000011, args[0] & 0xff, args[0] >> 8] }, - { "type": ["HL"], "format": lambda args, _: [0b11101001] }, - { "type": ["cc", "16b"], "format": lambda args, _: [0b11000010 | (args[0] << 3), args[1] & 0xff, args[1] >> 8] }, - { "type": ["C", "16b"], "format": lambda args, _: [0b11000010 | (3 << 3), args[1] & 0xff, args[1] >> 8] } - ]}, - { "opcode": "JR", "params": [ - { "type": ["8b"], "format": lambda args, _: [0b00011000, args[0]] }, - { "type": ["16b"], "format": lambda args, addr: [0b00011000, absolute16bAddrToRelative8b(args[0], addr)] }, - { "type": ["cc", "8b"], "format": lambda args, _: [0b00100000 | (args[0] << 3), args[1]] }, - { "type": ["C", "8b"], "format": lambda args, _: [0b00100000 | (3 << 3), args[1]] }, - { "type": ["cc", "16b"], "format": lambda args, addr: [0b00100000 | (args[0] << 3), absolute16bAddrToRelative8b(args[1], addr)] }, - { "type": ["C", "16b"], "format": lambda args, addr: [0b00100000 | (3 << 3), absolute16bAddrToRelative8b(args[1], addr)] } - ]}, - { "opcode": "CALL", "params": [ - { "type": ["16b"], "format": lambda args, _: [0b11001101, args[0] & 0xff, args[0] >> 8] }, - { "type": ["cc", "16b"], "format": lambda args, _: [0b11000100 | (args[0] << 3), args[1] & 0xff, args[1] >> 8] }, - { "type": ["C", "16b"], "format": lambda args, _: [0b11000100 | (3 << 3), args[1] & 0xff, args[1] >> 8] } - ]}, - { "opcode": "RET", "params": [ - { "type": [], "format": lambda args, _: [0b11001001] }, - { "type": ["cc"], "format": lambda args, _: [0b11000000 | (args[0] << 3)] }, - { "type": ["C"], "format": lambda args, _: [0b11000000 | (3 << 3)] } - ]}, - { "opcode": "RETI", "params": [ - { "type": [], "format": lambda args, _: [0b11011001] }, - ]}, - { "opcode": "RST", "params": [ - { "type": ["n"], "format": lambda args, _: [0b11000111 | (args[0] << 3)] }, - ]}, - { "opcode": "DI", "params": [ - { "type": [], "format": lambda args, _: [0b11110011] }, - ]}, - { "opcode": "EI", "params": [ - { "type": [], "format": lambda args, _: [0b11111011] }, - ]}, - { "opcode": "NOP", "params": [ - { "type": [], "format": lambda args, _: [0b00000000] }, - ]}, - { "opcode": "HALT", "params": [ - { "type": [], "format": lambda args, _: [0b01110110] }, - ]}, - { "opcode": "STOP", "params": [ - { "type": [], "format": lambda args, _: [0b00010000, 0b00000000] }, - ]}, - { "opcode": "RLCA", "params": [ - { "type": [], "format": lambda args, _: [0b00000111] }, - ]}, - { "opcode": "RLA", "params": [ - { "type": [], "format": lambda args, _: [0b00010111] }, - ]}, - { "opcode": "RRCA", "params": [ - { "type": [], "format": lambda args, _: [0b00001111] }, - ]}, - { "opcode": "RRA", "params": [ - { "type": [], "format": lambda args, _: [0b00011111] }, - ]}, - { "opcode": "BIT", "params": [ - { "type": ["n", "r"], "format": lambda args, _: [0b11001011, 0b01000000 | (args[0] << 3) | args[1]] }, - { "type": ["n", "(HL)"], "format": lambda args, _: [0b11001011, 0b01000110 | (args[0] << 3)] }, - ]}, - { "opcode": "SET", "params": [ - { "type": ["n", "r"], "format": lambda args, _: [0b11001011, 0b11000000 | (args[0] << 3) | args[1]] }, - { "type": ["n", "(HL)"], "format": lambda args, _: [0b11001011, 0b11000110 | (args[0] << 3)] }, - ]}, - { "opcode": "RES", "params": [ - { "type": ["n", "r"], "format": lambda args, _: [0b11001011, 0b10000000 | (args[0] << 3) | args[1]] }, - { "type": ["n", "(HL)"], "format": lambda args, _: [0b11001011, 0b10000110 | (args[0] << 3)] }, - ]}, - { "opcode": "RLC", "params": [ - { "type": ["r"], "format": lambda args, _: [0b11001011, 0b00000000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b11001011, 0b00000110] }, - ]}, - { "opcode": "RL", "params": [ - { "type": ["r"], "format": lambda args, _: [0b11001011, 0b00010000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b11001011, 0b00010110] }, - ]}, - { "opcode": "RRC", "params": [ - { "type": ["r"], "format": lambda args, _: [0b11001011, 0b00001000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b11001011, 0b00001110] }, - ]}, - { "opcode": "RR", "params": [ - { "type": ["r"], "format": lambda args, _: [0b11001011, 0b00011000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b11001011, 0b00011110] }, - ]}, - { "opcode": "SLA", "params": [ - { "type": ["r"], "format": lambda args, _: [0b11001011, 0b00100000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b11001011, 0b00100110] }, - ]}, - { "opcode": "SWAP", "params": [ - { "type": ["r"], "format": lambda args, _: [0b11001011, 0b00110000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b11001011, 0b00110110] }, - ]}, - { "opcode": "SRA", "params": [ - { "type": ["r"], "format": lambda args, _: [0b11001011, 0b00101000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b11001011, 0b00101110] }, - ]}, - { "opcode": "SRL", "params": [ - { "type": ["r"], "format": lambda args, _: [0b11001011, 0b00111000 | args[0]] }, - { "type": ["(HL)"], "format": lambda args, _: [0b11001011, 0b00111110] }, - ]}, - { "opcode": ".DB", "params": [ - { "type": ["*"], "format": lambda args, _: args }, - ]}, - { "opcode": ".PADTO", "params": [ - { "type": ["16b"], "format": lambda args, addr: padTo(args,addr) }, - ]}, -] - -registers = { - "A": 7, - "B": 0, - "C": 1, - "D": 2, # Maybe ? - "E": 3, - "H": 4, # Maybe ? - "L": 5, # Maybe ? - # (HL) is not a register but is associated with 6 - - "BC": 0, - "DE": 1, - "HL": 2, # Confirmed - "AF": 3, # TODO: Only for PUSH & POP - "SP": 3, # TODO: For everything except PUSH & POP -} - -conditions = { - # Not checked: - "NZ": 0, - "Z": 1, - "NC": 2, - "C": 3, -} - -class Param: - def __init__(self, value, labels): - self.type, self.value = self.get_type_value(value.upper().strip(), labels) - - def get_type_value(self, input, labels): - if input in ['A', 'B', 'C', 'D', 'E', 'H', 'L']: - return ['r', input], registers[input] - elif input in ['BC', 'DE', 'HL', 'SP', 'AF']: - return ['rr', input], registers[input] - elif len(input) == 4 and input[:2] == '0X': - return ['8b'], int(input[2:], 16) - elif len(input) == 6 and input[:2] == '0X': - return ['16b'], int(input[2:], 16) - elif len(input) == 8 and input[:3] == '(0X' and input[-1] == ')': - return ['(nn)'], int(input[3:-1], 16) - elif len(input) == 6 and input[:3] == '(0X' and input[-1] == ')': - return ['(n)'], int(input[3:-1], 16) - elif input in ['NZ', 'Z', 'NC', 'C']: - return ['cc', input], conditions[input] - elif input in ['0','1','2','3','4','5','6','7']: - return ['n'], int(input) - elif input in ["(HL)", "(BC)", "(DE)", "(C)", "(HL-)", "(HL+)"]: - return [input], 0 - elif input.startswith('='): - if labels is None: - return ['16b'], -1 - else: - return ['16b'], labels[input[1:]] - else: - raise ValueError("Invalid parameter ({})".format(input)) - - -class Instruction: - def __init__(self, value, labels): - splitted = value.split(' ') - self.opcode = splitted[0].upper().strip() - self.params = [Param(param, labels) for param in splitted[1:] if param.strip()] - - def get_instruction_format(self): - for instruction in instructions: - if self.opcode == instruction['opcode']: - for params in instruction['params']: - if len(params["type"]) == 1 and params["type"][0] == "*": - return params - if len(params["type"]) == len(self.params): - for i in range(len(params["type"])): - if params["type"][i] not in self.params[i].type: - break - else: - return params - return None - - def to_bytes(self, address): - instruction_format = self.get_instruction_format()['format'] - return instruction_format([param.value for param in self.params], address) - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("input_file", help="The input file in gbasm") - parser.add_argument("output_file", help="The output gb rom filename (e.g. basic.rom)") - args = parser.parse_args() - - lines, _ = preprocess(args.input_file, 0) - print("\n".join(lines)) - - program = assemble(lines) - - output = open(args.output_file, "wb") - output.write(program) - output.close() - -def isValidHexadecimal(s): - return all(c in string.hexdigits for c in s) and (len(s) == 2 or len(s) == 4) - - -def getConstant(name): - global constants - if name.startswith("$") and len(name) > 2: - if isValidHexadecimal(name[1:]): - return "0x" + name[1:] - else: - return getConstant(constants[name[1:].upper()]) - return name - - -def preprocess(input_file, offset): - f = open(input_file, "r") - relative_path = dirname(abspath(input_file)) - - global labels - global constants - starting_address = offset; - lines = [] - # Preprocess - for line in f: - line_without_comment = line.split(';')[0].strip() - - words = line_without_comment.split(' ') - - if len(words) == 2 and words[0].upper() == ".INCLUDE": - if not words[1].startswith('"') or not words[1].endswith('"'): - raise ValueError("Invalid parameter for .INCLUDE ({})".format(words[1])) - file = relative_path + "/" + words[1][1:-1] - included_lines, starting_address = preprocess(file, starting_address) - lines += included_lines - continue - - if len(words) == 3 and words[0].upper() == ".DEFINE": - if isValidHexadecimal(words[1]): - raise ValueError("\"{}\" is an invalid definition name since it could be mixed up with an hexadecimal value".format(words[1])) - - constants[words[1].upper()] = words[2] - print(constants) - continue - - if ':' in line_without_comment: - splitted = line_without_comment.split(':') - labelName = splitted[0].strip().upper() - if labelName in labels: - raise ValueError("Label \"{}\" is already defined".format(labelName)) - labels[labelName] = starting_address - line_without_comment = splitted[1].strip() - - if line_without_comment != '': - words = line_without_comment.replace(",", " ").replace("0xFF00+", "").split(" ") - - for i in range(0, len(words)): - if words[i].startswith("$") and len(words[i]) > 2: - words[i] = getConstant(words[i]) - - res = " ".join(words).replace("$", "0x") - lines.append(res) - instruction = Instruction(res, None) - print("Line: " + line) - print("Instruction: " + instruction.opcode + " " + str(instruction.params)) - print("Valid: " + str(instruction.get_instruction_format())) - print("Format: " + str(instruction)) - starting_address += len(instruction.to_bytes(starting_address)) - - print(labels) - - return lines, starting_address - -def assemble(lines): - program = [] - # Compile - for line in lines: - instruction = Instruction(line, labels) - print("Line: " + line) - print("Instruction: " + instruction.opcode + " " + str(instruction.params)) - print("Valid: " + str(instruction.get_instruction_format())) - print("Format: " + str(instruction)) - - program += instruction.to_bytes(len(program)) - return bytearray(program) - - -if __name__ == "__main__": - main() @@ -1,3 +1,3 @@ -module github.com/astatinchan/gamboy-asm +module github.com/astatinchan/gameboy-asm go 1.23.0 diff --git a/test.gbasm b/test.gbasm deleted file mode 100644 index 2e71378..0000000 --- a/test.gbasm +++ /dev/null @@ -1,4 +0,0 @@ -LD D, A -Test: - LD C, B - JP =Test |