Add support for JR command
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
60
src/python/src/gbasm/instructions/jr.py
Normal file
60
src/python/src/gbasm/instructions/jr.py
Normal 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)
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
370
src/python/test/cases/instructions/jr.yaml
Normal file
370
src/python/test/cases/instructions/jr.yaml
Normal 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, ]
|
||||
Reference in New Issue
Block a user