"""
CALL Operation
Push the current program counter (i.e. the next instruction to be
executed) onto the stack, then set the program counter (i.e. jump) to
the value in the given argument (module or constant).
"""
from ..language_defs import (
INSTRUCTION_GROUPS,
SRC_REGISTERS,
DEST_REGISTERS,
MODULE_CONTROL,
ALU_CONTROL_FLAGS,
FLAGS,
instruction_byte_from_bitdefs,
)
from ..operation_utils import assemble_instruction, match_and_parse_line
from ..data_structures import (
get_arg_def_template, get_machine_code_byte_template
)
_NAME = "CALL"
[docs]def generate_microcode_templates():
"""
Generate microcode for all the CALL instructions.
Returns:
list(DataTemplate): DataTemplates for all the CALL instructions.
"""
data_templates = []
signatures = generate_signatures()
for signature in signatures:
templates = generate_operation_templates(signature)
data_templates.extend(templates)
return data_templates
[docs]def generate_signatures():
"""
Generate all the argument signatures for the CALL operation.
Returns:
list(list(dict)): All possible signatures, See
:func:`~.get_arg_def_template` for more information on an
argument definition dictionary.
"""
signatures = []
modules = ("ACC", "A", "B", "C",)
for module in modules:
arg_def = get_arg_def_template()
arg_def["value_type"] = "module_name"
arg_def["is_memory_location"] = False
arg_def["value"] = module
signatures.append([arg_def])
const_arg_def = get_arg_def_template()
const_arg_def["value_type"] = "constant"
const_arg_def["is_memory_location"] = False
signatures.append([const_arg_def])
return signatures
[docs]def generate_operation_templates(signature):
"""
Create the DataTemplates to define a CALL with the given signature.
Args:
signature (list(dict)): List of argument definitions that
specify which particular CALL operation to generate
templates for.
Returns:
list(DataTemplate) : Datatemplates that define this CALL.
"""
instruction_byte_bitdefs = generate_instruction_byte_bitdefs(signature)
flags_bitdefs = [FLAGS["ANY"]]
control_steps = generate_control_steps(signature)
return assemble_instruction(
instruction_byte_bitdefs, flags_bitdefs, control_steps
)
[docs]def generate_instruction_byte_bitdefs(signature):
"""
Generate bitdefs to specify the instruction byte for this signature.
Args:
signature (list(dict)): List of argument definitions that
specify which particular CALL operation to generate
the instruction byte bitdefs for.
Returns:
list(str): Bitdefs that make up the instruction_byte
"""
instruction_byte_bitdefs = []
instruction_byte_bitdefs.append(INSTRUCTION_GROUPS["LOAD"])
instruction_byte_bitdefs.append(DEST_REGISTERS["SP+/-"])
if signature[0]["value_type"] == "module_name":
instruction_byte_bitdefs.append(SRC_REGISTERS[signature[0]["value"]])
elif signature[0]["value_type"] == "constant":
instruction_byte_bitdefs.append(SRC_REGISTERS["CONST"])
return instruction_byte_bitdefs
[docs]def generate_control_steps(signature):
"""
Generate control steps for this signature.
Args:
signature (list(dict)): List of argument definitions that
specify which particular CALL operation to generate the
control steps for.
Returns:
list(list(str)): List of list of bitdefs that specify the
control steps.
"""
if signature[0]["value_type"] == "constant":
sp_minus1_into_alu_incr_pc = [
MODULE_CONTROL["SP"]["OUT"],
MODULE_CONTROL["ALU"]["A_IS_BUS"],
MODULE_CONTROL["ALU"]["STORE_RESULT"],
MODULE_CONTROL["PC"]["COUNT"],
]
sp_minus1_into_alu_incr_pc.extend(ALU_CONTROL_FLAGS["A_MINUS_1"])
alu_into_mar_and_sp = [
MODULE_CONTROL["ALU"]["OUT"],
MODULE_CONTROL["SP"]["IN"],
MODULE_CONTROL["MAR"]["IN"],
]
pc_into_data_ram_at_sp = [
MODULE_CONTROL["PC"]["OUT"],
MODULE_CONTROL["RAM"]["IN"],
MODULE_CONTROL["RAM"]["SEL_DATA_MEM"],
]
pc_minus1_into_alu = [
MODULE_CONTROL["PC"]["OUT"],
MODULE_CONTROL["ALU"]["A_IS_BUS"],
MODULE_CONTROL["ALU"]["STORE_RESULT"],
]
pc_minus1_into_alu.extend(ALU_CONTROL_FLAGS["A_MINUS_1"])
alu_into_mar = [
MODULE_CONTROL["ALU"]["OUT"],
MODULE_CONTROL["MAR"]["IN"],
]
constant_from_prog_to_pc = [
MODULE_CONTROL["RAM"]["OUT"],
MODULE_CONTROL["RAM"]["SEL_PROG_MEM"],
MODULE_CONTROL["PC"]["IN"],
]
return [
sp_minus1_into_alu_incr_pc,
alu_into_mar_and_sp,
pc_into_data_ram_at_sp,
pc_minus1_into_alu,
alu_into_mar,
constant_from_prog_to_pc,
]
elif signature[0]["value_type"] == "module_name":
sp_minus1_into_alu = [
MODULE_CONTROL["SP"]["OUT"],
MODULE_CONTROL["ALU"]["A_IS_BUS"],
MODULE_CONTROL["ALU"]["STORE_RESULT"],
]
sp_minus1_into_alu.extend(ALU_CONTROL_FLAGS["A_MINUS_1"])
alu_into_mar_and_sp = [
MODULE_CONTROL["ALU"]["OUT"],
MODULE_CONTROL["SP"]["IN"],
MODULE_CONTROL["MAR"]["IN"],
]
pc_onto_stack = [
MODULE_CONTROL["PC"]["OUT"],
MODULE_CONTROL["RAM"]["IN"],
MODULE_CONTROL["RAM"]["SEL_DATA_MEM"],
]
module_into_pc = [
MODULE_CONTROL[signature[0]["value"]]["OUT"],
MODULE_CONTROL["PC"]["IN"],
]
return [
sp_minus1_into_alu,
alu_into_mar_and_sp,
pc_onto_stack,
module_into_pc,
]
[docs]def parse_line(line):
"""
Parse a line of assembly code to create machine code byte templates.
If a line is not identifiably a CALL assembly line, return an empty
list instead.
Args:
line (str): Assembly line to be parsed.
Returns:
list(dict): List of machine code byte template dictionaries or
an empty list.
"""
match, signature = match_and_parse_line(
line, _NAME, generate_signatures()
)
if not match:
return []
instruction_byte = instruction_byte_from_bitdefs(
generate_instruction_byte_bitdefs(signature)
)
mc_bytes = []
mc_byte = get_machine_code_byte_template()
mc_byte["byte_type"] = "instruction"
mc_byte["bitstring"] = instruction_byte
mc_bytes.append(mc_byte)
if signature[0]["value_type"] == "constant":
const_byte = get_machine_code_byte_template()
const_byte["byte_type"] = "constant"
const_byte["constant"] = signature[0]["value"]
mc_bytes.append(const_byte)
return mc_bytes