aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2024-09-26 15:05:21 +0900
committerAstatin <astatin@redacted>2024-09-26 15:05:21 +0900
commit0b44780a7b142147d25f0f436159b876f95f17f7 (patch)
tree398412fdfb253e7ca6db2845e78877322dc84c7a
parentef861b3e9b9800259e011581e01a853d3a122bf4 (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--.gitignore1
-rwxr-xr-xgbasm419
-rw-r--r--go.mod2
-rw-r--r--test.gbasm4
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
diff --git a/gbasm b/gbasm
deleted file mode 100755
index 78b7715..0000000
--- a/gbasm
+++ /dev/null
@@ -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()
diff --git a/go.mod b/go.mod
index b12abb4..4f4aa91 100644
--- a/go.mod
+++ b/go.mod
@@ -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