Add support for JR command

This commit is contained in:
2019-12-28 16:54:23 -08:00
parent 2595e3af8c
commit 7ff5021236
10 changed files with 490 additions and 17 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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]:
@@ -58,12 +60,13 @@ def parse_line_size(instruction: Instruction,
def parse_line_bytes(instruction: Instruction,
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

View File

@@ -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()

View File

@@ -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:

View File

@@ -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:

View File

@@ -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)

View File

@@ -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:

View File

@@ -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:

View File

@@ -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, ]