diff --git a/src/python/src/gbasm/arguments/ArgumentTypes.py b/src/python/src/gbasm/arguments/ArgumentTypes.py index 71ede4f..bd300d0 100644 --- a/src/python/src/gbasm/arguments/ArgumentTypes.py +++ b/src/python/src/gbasm/arguments/ArgumentTypes.py @@ -32,6 +32,9 @@ class Label(ArgumentType): return False return True + def parse(self, token) -> Arguments.Register8: + return Arguments.Label(token) + def get_name(self) -> str: return Label.NAME @@ -116,3 +119,20 @@ class Immediate16(ArgumentType): def get_name(self) -> str: return Immediate16.NAME + + +class Flag(ArgumentType): + + NAME = "Flag" + + def __init__(self): + pass + + def can_parse(self, token: str) -> bool: + return token in Arguments.Flag.FLAGS + + def parse(self, token) -> Arguments.Immediate16: + return Arguments.Flag(token) + + def get_name(self) -> str: + return Flag.NAME diff --git a/src/python/src/gbasm/arguments/Arguments.py b/src/python/src/gbasm/arguments/Arguments.py index 1732f00..2479209 100644 --- a/src/python/src/gbasm/arguments/Arguments.py +++ b/src/python/src/gbasm/arguments/Arguments.py @@ -58,3 +58,15 @@ class Immediate16(Argument): def __init__(self, value: int): self.value = value + + +class Flag(Argument): + + NAME = "Immediate8" + + FLAGS = ["Z", "C", "NZ", "NC"] + + def __init__(self, value: str): + if value not in Flag.FLAGS: + raise ValueError("Unknown Flag: {}".format(value)) + self.value = value diff --git a/src/python/src/gbasm/gbasm.py b/src/python/src/gbasm/gbasm.py index da7235e..e844bc6 100755 --- a/src/python/src/gbasm/gbasm.py +++ b/src/python/src/gbasm/gbasm.py @@ -9,6 +9,7 @@ from .instructions.inc import Inc from .instructions.dec import Dec from .instructions.nop import Nop from .instructions.stop import Stop +from .instructions.jr import Jr from .arguments import ArgumentType, Argument from typing import Callable, Dict, List, Optional @@ -22,7 +23,8 @@ GB_INSTRUCTIONS = [ Nop(), Stop(), Inc(), - Dec() + Dec(), + Jr() ] def build_instruction_map() -> Dict[str, Instruction]: @@ -57,13 +59,14 @@ def parse_line_size(instruction: Instruction, raise ValueError("Failed to parse line.") def parse_line_bytes(instruction: Instruction, - arguments: List[str], - label_resolver: Callable[[str], int]) -> bytes: + arguments: List[str], + instruction_addr: int, + label_resolver: Callable[[str], int]) -> bytes: for argtype_list in instruction.argument_specs: args = try_parse_arguments(arguments, argtype_list) if args is not None: - return instruction.to_bytes(args, label_resolver) + return instruction.to_bytes(args, instruction_addr, label_resolver) raise ValueError("Failed to parse line.") @@ -76,8 +79,6 @@ def assemble(lines: str) -> bytes: instruction_map = build_instruction_map() logger.debug("Instruction map: %s", instruction_map) - byte_offset = 0 - instruction_count = 0 labels = {} # type: Dict[str, int] program = bytes() @@ -87,6 +88,7 @@ def assemble(lines: str) -> bytes: for step in ["SIZE", "CONTENT"]: logger.debug("Starting step: %s", step) + byte_offset = 0 for line_num, line in enumerate(lines): # Remove comments @@ -115,17 +117,16 @@ def assemble(lines: str) -> bytes: raise KeyError("Unknown instruction \"%s\" on line %s", instruction_name, line_num) - if step == 'SIZE': - byte_offset += parse_line_size(instruction, args) - instruction_count += 1 if step == 'CONTENT': try: - program += parse_line_bytes(instruction, args, label_resolver) + program += parse_line_bytes(instruction, args, byte_offset, label_resolver) except ValueError: raise ValueError("Failed to parse line %s,\n%s", line_num, line) + + byte_offset += parse_line_size(instruction, args) + if step == 'SIZE': - logger.info("Program size: %s bytes, %s instructions", - byte_offset, instruction_count) + logger.info("Program size: %s bytes", byte_offset) logger.debug("Found labels: %s", labels) return program diff --git a/src/python/src/gbasm/instructions/Instruction.py b/src/python/src/gbasm/instructions/Instruction.py index 7e33601..c70af79 100644 --- a/src/python/src/gbasm/instructions/Instruction.py +++ b/src/python/src/gbasm/instructions/Instruction.py @@ -10,6 +10,8 @@ class Instruction(object): def num_bytes(self, arguments) -> int: raise NotImplementedError() - def to_bytes(arguments: List[Argument], + def to_bytes(self, + arguments: List[Argument], + instruction_addr: int, label_resolver: Callable[[str], int]) -> bytes: raise NotImplementedError() diff --git a/src/python/src/gbasm/instructions/dec.py b/src/python/src/gbasm/instructions/dec.py index 6d8b5c8..08f8d18 100644 --- a/src/python/src/gbasm/instructions/dec.py +++ b/src/python/src/gbasm/instructions/dec.py @@ -13,7 +13,9 @@ class Dec(Instruction): def num_bytes(self, arguments) -> int: return 1 - def to_bytes(self, arguments: List[Argument], + def to_bytes(self, + arguments: List[Argument], + instruction_addr: int, label_resolver: Callable[[str], int]) -> bytes: if len(arguments) != 1: diff --git a/src/python/src/gbasm/instructions/inc.py b/src/python/src/gbasm/instructions/inc.py index 891775a..0c23815 100644 --- a/src/python/src/gbasm/instructions/inc.py +++ b/src/python/src/gbasm/instructions/inc.py @@ -13,7 +13,9 @@ class Inc(Instruction): def num_bytes(self, arguments) -> int: return 1 - def to_bytes(self, arguments: List[Argument], + def to_bytes(self, + arguments: List[Argument], + instruction_addr: int, label_resolver: Callable[[str], int]) -> bytes: if len(arguments) != 1: diff --git a/src/python/src/gbasm/instructions/jr.py b/src/python/src/gbasm/instructions/jr.py new file mode 100644 index 0000000..1927b02 --- /dev/null +++ b/src/python/src/gbasm/instructions/jr.py @@ -0,0 +1,60 @@ +from .Instruction import Instruction +from ..arguments.ArgumentTypes import Flag, Label +from ..arguments import Argument +from typing import Callable, List + + +class Jr(Instruction): + + INSTRUCTION_SIZE = 2 + + def __init__(self): + argtypes = [[Label()], [Flag(), Label()]] + super().__init__("JR", argtypes) + + def num_bytes(self, arguments) -> int: + return Jr.INSTRUCTION_SIZE + + def to_bytes(self, + arguments: List[Argument], + instruction_address: int, + label_resolver: Callable[[str], int]) -> bytes: + + out_bytes = bytearray() + + if len(arguments) == 1: + out_bytes.append(0x18) + dest_arg = arguments[0].value + + elif len(arguments) == 2: + flag_dict = { + "NZ": 0x20, + "NC": 0x30, + "Z": 0x28, + "C": 0x38 + } + flag = arguments[0].value + try: + out_bytes.append(flag_dict[flag]) + except KeyError: + logger.exception("Instruction JR does not accept flag %s", arg) + dest_arg = arguments[1].value + + else: + raise ValueError("Incorrect number of arguments") + + if isinstance(dest_arg, str): + label = dest_arg + dest_addr = label_resolver(label) + else: + dest_addr = dest_arg + + addr_offset = dest_addr - (instruction_address + Jr.INSTRUCTION_SIZE) + if addr_offset > 127 or addr_offset < -128: + raise ValueError("JR instruction cannot jump to {} ({}), from {}, {} bytes away" + .format(label, dest_addr, instruction_address, addr_offset)) + if addr_offset < 0: + addr_offset += 256 + + out_bytes.append(addr_offset) + return bytes(out_bytes) diff --git a/src/python/src/gbasm/instructions/nop.py b/src/python/src/gbasm/instructions/nop.py index 0a88330..d86dfa5 100644 --- a/src/python/src/gbasm/instructions/nop.py +++ b/src/python/src/gbasm/instructions/nop.py @@ -12,7 +12,9 @@ class Nop(Instruction): def num_bytes(self, arguments) -> int: return 1 - def to_bytes(self, arguments: List[Argument], + def to_bytes(self, + arguments: List[Argument], + instruction_addr: int, label_resolver: Callable[[str], int]) -> bytes: if len(arguments) != 0: diff --git a/src/python/src/gbasm/instructions/stop.py b/src/python/src/gbasm/instructions/stop.py index bff63c4..0780574 100644 --- a/src/python/src/gbasm/instructions/stop.py +++ b/src/python/src/gbasm/instructions/stop.py @@ -12,7 +12,9 @@ class Stop(Instruction): def num_bytes(self, arguments) -> int: return 1 - def to_bytes(self, arguments: List[Argument], + def to_bytes(self, + arguments: List[Argument], + instruction_addr: int, label_resolver: Callable[[str], int]) -> bytes: if len(arguments) != 0: diff --git a/src/python/test/cases/instructions/jr.yaml b/src/python/test/cases/instructions/jr.yaml new file mode 100644 index 0000000..c5aabe9 --- /dev/null +++ b/src/python/test/cases/instructions/jr.yaml @@ -0,0 +1,370 @@ +--- +name: jr_bkwd +program: | + label: + JR label +expected: + - 0x18 + - 0xFE +--- +name: jr_fwd +program: | + JR label + label: +expected: + - 0x18 + - 0x00 + +--- +name: jr_z_bkwd +program: | + label: + JR Z label +expected: + - 0x28 + - 0xFE +--- +name: jr_z_fwd +program: | + JR Z label + label: +expected: + - 0x28 + - 0x00 + +--- +name: jr_nz_bkwd +program: | + label: + JR NZ label +expected: + - 0x20 + - 0xFE +--- +name: jr_nz_fwd +program: | + JR NZ label + label: +expected: + - 0x20 + - 0x00 + +--- +name: jr_c_bkwd +program: | + label: + JR C label +expected: + - 0x38 + - 0xFE +--- +name: jr_c_fwd +program: | + JR C label + label: +expected: + - 0x38 + - 0x00 + +--- +name: jr_nc_bkwd +program: | + label: + JR NC label +expected: + - 0x30 + - 0xFE +--- +name: jr_nc_fwd +program: | + JR NC label + label: +expected: + - 0x30 + - 0x00 + +--- +# Jump backward by the maximum amount +name: jr_far_bkwd +program: | + far_label: + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + JR far_label +expected: [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x80 ] + +--- +# Jump forward by the maximum amount +name: jr_far_fwd +program: | + JR far_label + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + NOP + far_label: +expected: [ 0x18, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]