Add a DEC, NOP, STOP, and basic format tests
This commit is contained in:
@@ -6,6 +6,9 @@ import sys
|
|||||||
|
|
||||||
from .instructions import Instruction
|
from .instructions import Instruction
|
||||||
from .instructions.inc import Inc
|
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 .arguments import ArgumentType, Argument
|
||||||
|
|
||||||
from typing import Callable, Dict, List, Optional
|
from typing import Callable, Dict, List, Optional
|
||||||
@@ -16,7 +19,10 @@ COMMENT_CHAR = '#'
|
|||||||
LABEL_SUFFIX = ':'
|
LABEL_SUFFIX = ':'
|
||||||
|
|
||||||
GB_INSTRUCTIONS = [
|
GB_INSTRUCTIONS = [
|
||||||
Inc()
|
Nop(),
|
||||||
|
Stop(),
|
||||||
|
Inc(),
|
||||||
|
Dec()
|
||||||
]
|
]
|
||||||
|
|
||||||
def build_instruction_map() -> Dict[str, Instruction]:
|
def build_instruction_map() -> Dict[str, Instruction]:
|
||||||
@@ -88,8 +94,8 @@ def assemble(lines: str) -> bytes:
|
|||||||
|
|
||||||
# Tokenize
|
# Tokenize
|
||||||
tokens = line.split()
|
tokens = line.split()
|
||||||
logging.info("Line:", line)
|
logging.info("Line: {}".format(line))
|
||||||
logging.info("Tokens:", tokens)
|
logging.info("Tokens: {}".format(tokens))
|
||||||
|
|
||||||
if len(tokens) == 0:
|
if len(tokens) == 0:
|
||||||
continue
|
continue
|
||||||
|
|||||||
49
src/python/src/gbasm/instructions/dec.py
Normal file
49
src/python/src/gbasm/instructions/dec.py
Normal file
@@ -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))
|
||||||
21
src/python/src/gbasm/instructions/nop.py
Normal file
21
src/python/src/gbasm/instructions/nop.py
Normal file
@@ -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])
|
||||||
21
src/python/src/gbasm/instructions/stop.py
Normal file
21
src/python/src/gbasm/instructions/stop.py
Normal file
@@ -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])
|
||||||
20
src/python/test/cases/format.yaml
Normal file
20
src/python/test/cases/format.yaml
Normal file
@@ -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
|
||||||
|
---
|
||||||
19
src/python/test/cases/format/format.yaml
Normal file
19
src/python/test/cases/format/format.yaml
Normal file
@@ -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
|
||||||
74
src/python/test/cases/instructions/dec.yaml
Normal file
74
src/python/test/cases/instructions/dec.yaml
Normal file
@@ -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
|
||||||
12
src/python/test/cases/instructions/special.yaml
Normal file
12
src/python/test/cases/instructions/special.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
name: nop
|
||||||
|
program: |
|
||||||
|
NOP
|
||||||
|
expected:
|
||||||
|
- 0x00
|
||||||
|
---
|
||||||
|
name: stop
|
||||||
|
program: |
|
||||||
|
STOP
|
||||||
|
expected:
|
||||||
|
- 0x10
|
||||||
@@ -6,8 +6,10 @@ import yaml
|
|||||||
import pytest
|
import pytest
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(format="")
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
logging.basicConfig()
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class AssembleCase(object):
|
class AssembleCase(object):
|
||||||
|
|
||||||
@@ -17,28 +19,38 @@ class AssembleCase(object):
|
|||||||
self.expected = expected
|
self.expected = expected
|
||||||
|
|
||||||
|
|
||||||
def find_case_files():
|
def find_case_files(subdir: str):
|
||||||
test_root = Path(os.path.dirname(os.path.abspath(__file__)))
|
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")
|
return case_root.glob("**/*.yaml")
|
||||||
|
|
||||||
def get_test_cases():
|
def get_test_cases(subdir: str):
|
||||||
cases = []
|
cases = []
|
||||||
files = find_case_files()
|
files = find_case_files(subdir)
|
||||||
for f in files:
|
for f in files:
|
||||||
index = 0
|
index = 0
|
||||||
with open(str(f), "r") as yaml_file:
|
with open(str(f), "r") as yaml_file:
|
||||||
test_descs = yaml.safe_load_all(yaml_file)
|
test_descs = yaml.safe_load_all(yaml_file)
|
||||||
for desc in test_descs:
|
for desc in test_descs:
|
||||||
|
try:
|
||||||
case = AssembleCase(desc['name'], desc['program'], bytes(desc['expected']))
|
case = AssembleCase(desc['name'], desc['program'], bytes(desc['expected']))
|
||||||
|
except TypeError:
|
||||||
|
logger.exception("Failed to parse yaml: {}".format(desc))
|
||||||
cases.append(case)
|
cases.append(case)
|
||||||
return cases
|
return cases
|
||||||
|
|
||||||
cases = get_test_cases()
|
instruction_cases = get_test_cases("instructions")
|
||||||
print(cases)
|
@pytest.mark.parametrize("case", instruction_cases,
|
||||||
|
ids=[case.name for case in instruction_cases])
|
||||||
@pytest.mark.parametrize("case", cases, ids=[case.name for case in cases])
|
|
||||||
def test_assemble_instruction(case):
|
def test_assemble_instruction(case):
|
||||||
lines = case.program.split("\n")
|
lines = case.program.split("\n")
|
||||||
assembled = assemble(lines)
|
assembled = assemble(lines)
|
||||||
assert assembled == case.expected
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user