From cb222cc6bbc6e09edae034cd4e8aaea07cf94dc9 Mon Sep 17 00:00:00 2001 From: Max Regan Date: Sat, 21 Dec 2019 20:52:18 -0800 Subject: [PATCH] Add a DEC, NOP, STOP, and basic format tests --- src/python/src/gbasm/gbasm.py | 12 ++- src/python/src/gbasm/instructions/dec.py | 49 ++++++++++++ src/python/src/gbasm/instructions/nop.py | 21 ++++++ src/python/src/gbasm/instructions/stop.py | 21 ++++++ src/python/test/cases/format.yaml | 20 +++++ src/python/test/cases/format/format.yaml | 19 +++++ src/python/test/cases/instructions/dec.yaml | 74 +++++++++++++++++++ .../test/cases/instructions/special.yaml | 12 +++ src/python/test/test_assemble.py | 32 +++++--- 9 files changed, 247 insertions(+), 13 deletions(-) create mode 100644 src/python/src/gbasm/instructions/dec.py create mode 100644 src/python/src/gbasm/instructions/nop.py create mode 100644 src/python/src/gbasm/instructions/stop.py create mode 100644 src/python/test/cases/format.yaml create mode 100644 src/python/test/cases/format/format.yaml create mode 100644 src/python/test/cases/instructions/dec.yaml create mode 100644 src/python/test/cases/instructions/special.yaml diff --git a/src/python/src/gbasm/gbasm.py b/src/python/src/gbasm/gbasm.py index 7f2af3c..bc7aaf1 100755 --- a/src/python/src/gbasm/gbasm.py +++ b/src/python/src/gbasm/gbasm.py @@ -6,6 +6,9 @@ import sys from .instructions import Instruction from .instructions.inc import Inc +from .instructions.dec import Dec +from .instructions.nop import Nop +from .instructions.stop import Stop from .arguments import ArgumentType, Argument from typing import Callable, Dict, List, Optional @@ -16,7 +19,10 @@ COMMENT_CHAR = '#' LABEL_SUFFIX = ':' GB_INSTRUCTIONS = [ - Inc() + Nop(), + Stop(), + Inc(), + Dec() ] def build_instruction_map() -> Dict[str, Instruction]: @@ -88,8 +94,8 @@ def assemble(lines: str) -> bytes: # Tokenize tokens = line.split() - logging.info("Line:", line) - logging.info("Tokens:", tokens) + logging.info("Line: {}".format(line)) + logging.info("Tokens: {}".format(tokens)) if len(tokens) == 0: continue diff --git a/src/python/src/gbasm/instructions/dec.py b/src/python/src/gbasm/instructions/dec.py new file mode 100644 index 0000000..6d8b5c8 --- /dev/null +++ b/src/python/src/gbasm/instructions/dec.py @@ -0,0 +1,49 @@ +from .Instruction import Instruction +from ..arguments.ArgumentTypes import Register8, Register16 +from ..arguments import Argument +from typing import Callable, List + + +class Dec(Instruction): + + def __init__(self): + argtypes = [[Register8()], [Register16()]] + super().__init__("DEC", argtypes) + + def num_bytes(self, arguments) -> int: + return 1 + + def to_bytes(self, arguments: List[Argument], + label_resolver: Callable[[str], int]) -> bytes: + + if len(arguments) != 1: + raise ValueError("Incorrect number of arguments") + + value = arguments[0].value + + if value == "BC": + return bytes([0x0B]) + if value == "DE": + return bytes([0x1B]) + if value == "HL": + return bytes([0x2B]) + if value == "SP": + return bytes([0x3B]) + if value == "A": + return bytes([0x3D]) + if value == "B": + return bytes([0x05]) + if value == "C": + return bytes([0x0D]) + if value == "D": + return bytes([0x15]) + if value == "E": + return bytes([0x1D]) + if value == "H": + return bytes([0x25]) + if value == "L": + return bytes([0x2D]) + if value == "(HL)": + return bytes([0x35]) + + raise ValueError("Unknown value: {}".format(value)) diff --git a/src/python/src/gbasm/instructions/nop.py b/src/python/src/gbasm/instructions/nop.py new file mode 100644 index 0000000..0a88330 --- /dev/null +++ b/src/python/src/gbasm/instructions/nop.py @@ -0,0 +1,21 @@ +from .Instruction import Instruction +from ..arguments import Argument +from typing import Callable, List + + +class Nop(Instruction): + + def __init__(self): + argtypes = [[]] + super().__init__("NOP", argtypes) + + def num_bytes(self, arguments) -> int: + return 1 + + def to_bytes(self, arguments: List[Argument], + label_resolver: Callable[[str], int]) -> bytes: + + if len(arguments) != 0: + raise ValueError("Incorrect number of arguments") + + return bytes([0x00]) diff --git a/src/python/src/gbasm/instructions/stop.py b/src/python/src/gbasm/instructions/stop.py new file mode 100644 index 0000000..bff63c4 --- /dev/null +++ b/src/python/src/gbasm/instructions/stop.py @@ -0,0 +1,21 @@ +from .Instruction import Instruction +from ..arguments import Argument +from typing import Callable, List + + +class Stop(Instruction): + + def __init__(self): + argtypes = [[]] + super().__init__("STOP", argtypes) + + def num_bytes(self, arguments) -> int: + return 1 + + def to_bytes(self, arguments: List[Argument], + label_resolver: Callable[[str], int]) -> bytes: + + if len(arguments) != 0: + raise ValueError("Incorrect number of arguments") + + return bytes([0x10]) diff --git a/src/python/test/cases/format.yaml b/src/python/test/cases/format.yaml new file mode 100644 index 0000000..d1049a9 --- /dev/null +++ b/src/python/test/cases/format.yaml @@ -0,0 +1,20 @@ +--- +name: two_nop +program: | + NOP + NOP +expected: + - 0x00 + - 0x00 +--- +name: unused_labels +program: | + start: + NOP + middle: + NOP + end: +expected: + - 0x00 + - 0x00 +--- diff --git a/src/python/test/cases/format/format.yaml b/src/python/test/cases/format/format.yaml new file mode 100644 index 0000000..0d5777f --- /dev/null +++ b/src/python/test/cases/format/format.yaml @@ -0,0 +1,19 @@ +--- +name: two_nop +program: | + NOP + NOP +expected: + - 0x00 + - 0x00 +--- +name: unused_labels +program: | + start: + NOP + middle: + NOP + end: +expected: + - 0x00 + - 0x00 diff --git a/src/python/test/cases/instructions/dec.yaml b/src/python/test/cases/instructions/dec.yaml new file mode 100644 index 0000000..fe99a23 --- /dev/null +++ b/src/python/test/cases/instructions/dec.yaml @@ -0,0 +1,74 @@ +--- +name: dec_a +program: | + DEC A +expected: + - 0x3D + +--- +name: dec_b +program: | + DEC B +expected: + - 0x05 +--- +name: dec_c +program: | + DEC C +expected: + - 0x0D +--- +name: dec_d +program: | + DEC D +expected: + - 0x15 +--- +name: dec_e +program: | + DEC E +expected: + - 0x1D +--- +name: dec_h +program: | + DEC H +expected: + - 0x25 +--- +name: dec_l +program: | + DEC L +expected: + - 0x2D +--- +name: dec_(hl) +program: | + DEC (HL) +expected: + - 0x35 + +--- +name: dec_bc +program: | + DEC BC +expected: + - 0x0B +--- +name: dec_de +program: | + DEC DE +expected: + - 0x1B +--- +name: dec_hl +program: | + DEC HL +expected: + - 0x2B +--- +name: dec_sp +program: | + DEC SP +expected: + - 0x3B diff --git a/src/python/test/cases/instructions/special.yaml b/src/python/test/cases/instructions/special.yaml new file mode 100644 index 0000000..0651f7a --- /dev/null +++ b/src/python/test/cases/instructions/special.yaml @@ -0,0 +1,12 @@ +--- +name: nop +program: | + NOP +expected: + - 0x00 +--- +name: stop +program: | + STOP +expected: + - 0x10 diff --git a/src/python/test/test_assemble.py b/src/python/test/test_assemble.py index b1d17d3..faf1551 100644 --- a/src/python/test/test_assemble.py +++ b/src/python/test/test_assemble.py @@ -6,8 +6,10 @@ import yaml import pytest import logging +logging.basicConfig(format="") logging.getLogger().setLevel(logging.INFO) -logging.basicConfig() + +logger = logging.getLogger(__name__) class AssembleCase(object): @@ -17,28 +19,38 @@ class AssembleCase(object): self.expected = expected -def find_case_files(): +def find_case_files(subdir: str): test_root = Path(os.path.dirname(os.path.abspath(__file__))) - case_root = test_root / "cases" / "instructions" + case_root = test_root / "cases" / subdir return case_root.glob("**/*.yaml") -def get_test_cases(): +def get_test_cases(subdir: str): cases = [] - files = find_case_files() + files = find_case_files(subdir) for f in files: index = 0 with open(str(f), "r") as yaml_file: test_descs = yaml.safe_load_all(yaml_file) for desc in test_descs: - case = AssembleCase(desc['name'], desc['program'], bytes(desc['expected'])) + try: + case = AssembleCase(desc['name'], desc['program'], bytes(desc['expected'])) + except TypeError: + logger.exception("Failed to parse yaml: {}".format(desc)) cases.append(case) return cases -cases = get_test_cases() -print(cases) - -@pytest.mark.parametrize("case", cases, ids=[case.name for case in cases]) +instruction_cases = get_test_cases("instructions") +@pytest.mark.parametrize("case", instruction_cases, + ids=[case.name for case in instruction_cases]) def test_assemble_instruction(case): lines = case.program.split("\n") assembled = assemble(lines) assert assembled == case.expected + +format_cases = get_test_cases("format") +@pytest.mark.parametrize("case", format_cases, + ids=[case.name for case in format_cases]) +def test_format_instruction(case): + lines = case.program.split("\n") + assembled = assemble(lines) + assert assembled == case.expected