Source code for SAPsim

__author__ = "Jesse Wei <jesse@cs.unc.edu>"

from pathlib import Path
from typing import Any
from SAPsim.utils.parser import parse_csv
import SAPsim.utils.helpers as helpers
import SAPsim.utils.globs as globs
import SAPsim.utils.execute as execute


[docs]def run(prog_path: str, **kwargs) -> None: r"""Run given .csv program. :param prog_path: .csv file in SAPsim format. :type prog_path: ``str`` :param \**kwargs: See below :Keyword Arguments: * *debug* (``bool``) -- * Whether to run in debug mode (True) or at full speed (False) * Default is full speed * *change* (``str``) -- * Comma-separated list of changes to RAM * Useful for debugging programs (edit a value without changing CSV) * Also useful for autograding programs (overwrite a reserved instruction/data value) * Format: <addr>:<base-10 value>,<addr>:<base-10 value>, ... * *format* (``str``) -- * Table format * Options: https://github.com/astanin/python-tabulate#table-format * *bits* (``int``) -- * Number of bits in unsigned registers * Default 8 :return: ``dict`` containing all final values in globs.py, used for autograding :rtype: ``dict`` """ path: Path = Path(prog_path) assert path.suffix == ".csv" helpers.setup_8bit() parse_csv(path) if "bits" in kwargs: assert int(kwargs["bits"]) > 1 globs.NUM_BITS_IN_REGISTERS = int(kwargs["bits"]) globs.MAX_UNSIGNED_VAL_IN_REGISTERS = 2**globs.NUM_BITS_IN_REGISTERS - 1 if "change" in kwargs: changes = kwargs["change"].split(",") for change in changes: if change.count(":") != 1: print( "Invalid syntax for --c option, correct format is <addr>:<base-10 value>,<addr>:<base-10 value>, ..." ) exit(1) colon_position = change.find(":") addr = int(change[:colon_position]) if addr not in globs.RAM: print( f"You can apply a change only to an address that's already mapped (not skipped). Address {addr} is not mapped." ) exit(1) value = int(change[colon_position + 1 :]) if value < 0 or value > globs.MAX_UNSIGNED_VAL_IN_REGISTERS: print( f"Invalid base-10 value for change: {value}. Negative or overflows registers." ) exit(1) globs.RAM[addr] = value if "format" in kwargs: globs.table_fmt = kwargs["format"] if "debug" in kwargs and kwargs["debug"]: print("Initial state of simulation.") helpers.print_RAM(dispPC=True) helpers.print_info() print("Debug mode: press Enter to execute next instruction ( > ).") input() while globs.EXECUTING: # Special case so that you don't have to press Enter twice to halt on a HLT instruction if ( globs.PC in globs.RAM and helpers.parse_opcode(globs.RAM[globs.PC]) == 0xF ): execute.execute_next() break execute.execute_next() helpers.print_RAM(dispPC=True) helpers.print_info() input() print("Program halted.") else: execute.execute_full_speed() helpers.print_RAM(dispPC=True) helpers.print_info() print("Program halted.")
[docs]def run_and_return_state(prog_path: str, **kwargs) -> dict[str, Any]: r"""Run given .csv program and return final state. Just a wrapper around ``run()`` that can be used for autograding. The rest of the docstring is identical to that of run(). :param prog_path: .csv file in SAPsim format. :type prog_path: ``str`` :param \**kwargs: See below :Keyword Arguments: * *debug* (``bool``) -- * Whether to run in debug mode (True) or at full speed (False) * *change* (``str``) -- * Comma-separated list of changes to RAM * Useful for debugging programs (edit a value without changing CSV) * Also useful for autograding programs (overwrite a reserved instruction/data value) * Format: <addr>:<base-10 value>,<addr>:<base-10 value>, ... * *format* (``str``) -- * Table format * Options: https://github.com/astanin/python-tabulate#table-format * *bits* (``int``) -- * Number of bits in unsigned registers :return: ``dict`` containing all final values in globs.py, used for autograding :rtype: ``dict`` """ run(prog_path, **kwargs) return helpers.get_state()