예제 #1
0
파일: common.py 프로젝트: stepleton/tsasm
def _codegen_org(context: Context, op: Op) -> Tuple[Context, Op]:
  """ORG pseudoinstruction: set current output stream position."""
  # Try to parse our one argument. If successful, update our stream position.
  # Otherwise, leaving the op's `todo` unchanged means we'll try again later.
  op = op._replace(args=parse_args_if_able(
      _PARSE_OPTIONS, context, op, Type.ADDRESS))
  if all_args_parsed(op.args):
    op = op._replace(hex='', todo=None)
    context = context._replace(pos=op.args[0].integer)
  return context, op
예제 #2
0
파일: main.py 프로젝트: stepleton/tsasm
def _switch_arch(
    lineno: int,
    line: Text,
    context: Context,
    arch: Text,
) -> Context:
    """Switch the architecture we're generating code for."""
    try:
        module = importlib.import_module('.' + arch, 'tsasm.codegen')
        context = context._replace(arch=arch,
                                   codegen=getattr(module, 'get_codegen')(),
                                   encode_str=getattr(module, 'encode_str'))
    except (ModuleNotFoundError, AttributeError):
        raise Error(
            lineno, line,
            'Failed to load a code-generation library for architecture '
            '{!r}'.format(arch))
    return context
예제 #3
0
파일: main.py 프로젝트: stepleton/tsasm
def assemble(
    context: Context,
    input_file: TextIO,
    output_file: BinaryIO,
    listing_file: Optional[TextIO],
):
    """Assemble source code from a file.

  Args:
    context: an assembler context.
    input_file: handle for file containing input source code.
    output_file: handle for file receiving binary output.
    listing_file: optional handle for file receiving a text listing.

  Raises:
    Error: if any error is encountered.
  """
    # Load source code from the input.
    ops, lines = read_source(input_file)
    if not ops: raise Error(-1, '<EOF>', 'No code to compile in the input?')

    # Track where each op will commit its hex data to RAM. Entries are None when
    # we don't know that yet.
    addrs: List[Optional[int]] = [None] * len(ops)

    # Keep making passes through all of the ops until the number of pending
    # invocations of `asmpass_codegen` stops changing.
    num_ops_with_codegen_todos = None
    for pass_count in itertools.count(start=1):

        # At the beginning of the pass, reset the current output position to 0.
        context = context._replace(pos=0)

        # Perform a pass through the code. When catching errors, ValueErrors are
        # "normal" errors owing to bugs in user code; other types are "internal"
        # errors that are likely our fault.
        for i in range(len(ops)):
            # If the position of this op has already been calculated, that value is
            # authoritative. Otherwise, if we have new knowledge of this position,
            # and we're at or after code generation, save it.
            if addrs[i] is not None:
                context = context._replace(pos=addrs[i])
            elif context.pos is not None and ops[i].todo is not asmpass_lexer:
                addrs[i] = context.pos

            # If this op has a `todo`, execute it and apply some checks.
            if ops[i].todo is not None:
                try:
                    context, ops[i] = ops[i].todo(context, ops[i])
                    if ops[i].hex and len(ops[i].hex) % 2:
                        raise Error(ops[i].lineno, ops[i].line,
                                    'Extra nybble in generated hex.')
                except ValueError as error:
                    raise Error(ops[i].lineno, ops[i].line, str(error))
                except Exception as error:
                    raise Error(ops[i].lineno, ops[i].line,
                                'Internal error, sorry!\n  {}'.format(error))

        # With the pass complete, see if it's time to stop.
        ops_with_codegen_todos = tuple(op for op in ops
                                       if op.todo == asmpass_codegen)
        if num_ops_with_codegen_todos == len(ops_with_codegen_todos): break
        num_ops_with_codegen_todos = len(ops_with_codegen_todos)

    # See if compilation was successful.
    if ops_with_codegen_todos:
        raise Error(
            ops[-1].lineno + 1, '<EOF>',
            'After {} passes, {} statements still have unresolved labels or other '
            'issues preventing full assembly. These statements are:\n'
            '  {}\n'.format(
                pass_count, len(ops_with_codegen_todos),
                '\n  '.join('{:>5}: {}'.format(op.lineno, op.line)
                            for op in ops_with_codegen_todos)))

    # Construct a mapping from memory addresses to ops whose binary data will
    # start at those addresses. Complain if multiple ops that actually generate
    # binary data attempt to start in the same location.
    addr_to_op: Dict[int, Op] = {}
    for addr, op in zip(addrs, ops):
        maybe_old_op = addr_to_op.setdefault(addr, op)
        if maybe_old_op is not op and maybe_old_op.hex and op.hex:
            logging.warning(
                'At memory location $%X: replacing previously-generated code.\n'
                '   old - %5d: %s\n   new - %5d: %s', addr,
                maybe_old_op.lineno, maybe_old_op.line, op.lineno, op.line)

    # Write binary output.
    _emit_binary(output_file, addr_to_op)

    # Write listing.
    if listing_file: _emit_listing(listing_file, lines, addr_to_op)