Exemplo n.º 1
0
def _codegen_jmp(context: Context, op: Op) -> Tuple[Context, Op]:
    """JMP pseudoinstruction: several underlying variants."""
    op = op._replace(args=parse_args_if_able(
        _PARSE_OPTIONS, context, op, Type.ADDRESS | Type.DEREF_REGISTER
        | Type.DEREF_ADDRESS))
    # Since this pseudoinstruction can produce code of different lengths, we
    # handle updating pos when "not all_args_parsed" in a special way.
    if not all_args_parsed(op.args):
        advance = 4 if op.args[0].argtype & Type.ADDRESS else 2
        return context.advance_by_bytes(advance), op

    # We are branching to an address literal.
    if op.args[0].argtype & Type.ADDRESS:
        _jmpdestcheck(op.args[0])
        op = op._replace(todo=None,
                         hex='D001{:04X}'.format(op.args[0].integer))

    # We are branching to an address stored at a memory location in a register.
    # (To branch to an address inside a register, use RET).
    elif op.args[0].argtype & Type.DEREF_REGISTER:
        _regderefcheck(op.args[0], postcrem_from=0, postcrem_to=0)
        op = op._replace(todo=None, hex='D0{:X}8'.format(op.args[0].integer))

    # We are branching to an address stored at a low memory location.
    else:
        _lowwordaddrcheck(op.args[0])
        op = op._replace(todo=None,
                         hex='20{:02X}'.format(op.args[0].integer // 2))

    return context.advance(op.hex), op
Exemplo n.º 2
0
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
Exemplo n.º 3
0
 def codegen_immed_to_reg(context: Context, op: Op) -> Tuple[Context, Op]:
     op = op._replace(args=parse_args_if_able(_PARSE_OPTIONS, context, op,
                                              Type.REGISTER, Type.NUMBER))
     if all_args_parsed(op.args):
         _regcheck(op.args[0])
         _bytecheck(op.args[1])
         digits = (nybble, op.args[0].integer, op.args[1].integer % 256)
         op = op._replace(todo=None, hex='{:X}{:X}{:02X}'.format(*digits))
     # We can still update pos whether we've parsed all args or not.
     return context.advance_by_bytes(2), op
Exemplo n.º 4
0
 def codegen_onereg(context: Context, op: Op) -> Tuple[Context, Op]:
     # Both register arguments to this opcode should be parseable.
     op = op._replace(args=parse_args_if_able(_PARSE_OPTIONS, context, op,
                                              Type.REGISTER))
     if all_args_parsed(op.args):
         _regcheck(*op.args)
         digits = (nybble_1, op.args[0].integer, nybble_2)
         op = op._replace(todo=None, hex=template.format(*digits))
     # We can still update pos whether we've parsed all args or not.
     return context.advance_by_bytes(2), op
Exemplo n.º 5
0
def _codegen_ctrl(context: Context, op: Op) -> Tuple[Context, Op]:
    """CTRL instruction."""
    op = op._replace(args=parse_args_if_able(_PARSE_OPTIONS, context, op,
                                             Type.ADDRESS, Type.NUMBER))
    if all_args_parsed(op.args):
        _devcheck(op.args[0])
        _bytecheck(op.args[1])
        digits = (op.args[0].integer, op.args[1].integer % 256)
        op = op._replace(todo=None, hex='1{:X}{:02X}'.format(*digits))
    # We can still update pos whether we've parsed all args or not.
    return context.advance_by_bytes(2), op
Exemplo n.º 6
0
def _codegen_putb(context: Context, op: Op) -> Tuple[Context, Op]:
    """PUTB instruction."""
    op = op._replace(args=parse_args_if_able(
        _PARSE_OPTIONS, context, op, Type.ADDRESS, Type.DEREF_REGISTER))
    if all_args_parsed(op.args):
        _devcheck(op.args[0])
        _regderefcheck(op.args[1], postcrem_from=-4, postcrem_to=4)
        modifier = _postcrement_to_modifier(op.args[1].postcrement)
        digits = (op.args[0].integer, op.args[1].integer, modifier)
        op = op._replace(todo=None, hex='4{:X}{:X}{:X}'.format(*digits))
    # We can still update pos whether we've parsed all args or not.
    return context.advance_by_bytes(2), op
Exemplo n.º 7
0
def _codegen_lwi(context: Context, op: Op) -> Tuple[Context, Op]:
    """LWI pseudoinstruction: MOVE RX, (RO)+; DW i."""
    op = op._replace(args=parse_args_if_able(_PARSE_OPTIONS, context, op,
                                             Type.REGISTER, Type.NUMBER))
    if all_args_parsed(op.args):
        _regcheck(op.args[0])
        if not -32767 <= op.args[1].integer <= 65535:
            raise ValueError(
                'Halfword literal {} not in range -32768..65535'.format(
                    op.args[1].stripped))
        digits = (op.args[0].integer, op.args[1].integer % 65536)
        op = op._replace(todo=None, hex='D{:X}01{:04X}'.format(*digits))
    # We can still update pos whether we've parsed all args or not.
    return context.advance_by_bytes(4), op
Exemplo n.º 8
0
def _codegen_call(context: Context, op: Op) -> Tuple[Context, Op]:
    """CALL pseudoinstruction: several underlying variants."""
    op = op._replace(args=parse_args_if_able(
        _PARSE_OPTIONS, context, op, Type.ADDRESS | Type.REGISTER
        | Type.DEREF_REGISTER | Type.DEREF_ADDRESS, Type.REGISTER))
    # Since this pseudoinstruction can produce code of different lengths, we
    # handle updating pos when "not all_args_parsed" in a special way.
    if not all_args_parsed(op.args):
        advance = 6 if op.args[0].argtype & Type.ADDRESS else 4
        return context.advance_by_bytes(advance), op

    # We are calling an address literal. Note that there is a way to do this in
    # two halfwords: for that, use the RCALL pseudoinstruction.
    if op.args[0].argtype & Type.ADDRESS:
        _jmpdestcheck(op.args[0])
        _regcheck(op.args[1])
        digits_a = (op.args[1].integer, op.args[1].integer, op.args[0].integer)
        op = op._replace(todo=None,
                         hex='0{:X}03D0{:X}1{:04X}'.format(*digits_a))

    # We are calling an address stored inside a register.
    elif op.args[0].argtype & Type.REGISTER:
        _callregcheck(op.args[0], op.args[1])
        digits_r = (op.args[1].integer, op.args[0].integer)
        op = op._replace(todo=None, hex='0{:X}0300{:X}4'.format(*digits_r))

    # We are calling an address stored at a memory location in a register.
    elif op.args[0].argtype & Type.DEREF_REGISTER:
        _callregcheck(op.args[0], op.args[1])
        _regderefcheck(op.args[0], postcrem_from=-2, postcrem_to=2)  # Words.
        modifier = _postcrement_to_modifier(2 * op.args[0].postcrement)
        digits_d = (op.args[1].integer, op.args[0].integer, modifier)
        op = op._replace(todo=None, hex='0{:X}03D0{:X}{:X}'.format(*digits_d))

    # We are calling an address stored at a low memory location.
    else:
        _regcheck(op.args[1])
        _lowwordaddrcheck(op.args[0])
        assert op.opcode is not None  # mypy...
        if op.args[0].precrement or op.args[0].postcrement:
            raise ValueError(
                'No (in/de)crementation is allowed for address dereference arguments '
                'to {}'.format(op.opcode.upper()))
        digits = (op.args[1].integer, op.args[0].integer // 2)
        op = op._replace(todo=None, hex='0{:X}0320{:02X}'.format(*digits))

    return context.advance(op.hex), op
Exemplo n.º 9
0
def _codegen_move(context: Context, op: Op) -> Tuple[Context, Op]:
    """MOVE instruction."""
    op = op._replace(
        args=parse_args_if_able(  # Note fractional crements enabled.
            _PARSE_OPTIONS._replace(
                fractional_crements=True), context, op, Type.ADDRESS
            | Type.REGISTER | Type.DEREF_REGISTER, Type.ADDRESS | Type.REGISTER
            | Type.DEREF_REGISTER))
    if all_args_parsed(op.args):

        if not any(arg.argtype & Type.REGISTER for arg in op.args):
            raise ValueError(
                'At least one argument to MOVE must be a register')

        # This is a move between registers.
        elif op.args[0].argtype == op.args[1].argtype == Type.REGISTER:
            _regcheck(*op.args)
            digits_r = (op.args[0].integer, op.args[1].integer)
            op = op._replace(todo=None, hex='0{:X}{:X}4'.format(*digits_r))

        # This is a move from/to an address found at a specified memory location.
        elif any(arg.argtype == Type.ADDRESS for arg in op.args):
            nybble, argaddr, argreg = ((2, op.args[1],
                                        op.args[0]) if op.args[0].argtype
                                       & Type.REGISTER else
                                       (3, op.args[0], op.args[1]))
            _regcheck(argreg)
            _lowwordaddrcheck(argaddr)
            digits_a = (nybble, argreg.integer, argaddr.integer // 2)
            op = op._replace(todo=None, hex='{:X}{:X}{:02X}'.format(*digits_a))

        # This is a move from/to an address found in a register.
        else:
            nybble, argderef, argreg = ((5, op.args[0],
                                         op.args[1]) if op.args[0].argtype
                                        & Type.DEREF_REGISTER else
                                        (0xD, op.args[1], op.args[0]))
            _regcheck(argreg)
            _regderefcheck(argderef, postcrem_from=-2, postcrem_to=2)  # Words.
            modifier = _postcrement_to_modifier(2 * argderef.postcrement)
            digits_d = (nybble, op.args[1].integer, op.args[0].integer,
                        modifier)
            op = op._replace(todo=None,
                             hex='{:X}{:X}{:X}{:X}'.format(*digits_d))

    # We can still update pos whether we've parsed all args or not.
    return context.advance_by_bytes(2), op
Exemplo n.º 10
0
def _codegen_bra(context: Context, op: Op) -> Tuple[Context, Op]:
    """BRA pseudoinstruction: ADD/SUB R0,#<dist>."""
    op = op._replace(
        args=parse_args_if_able(_PARSE_OPTIONS, context, op, Type.ADDRESS))
    if all_args_parsed(op.args) and context.pos is not None:
        _jmpdestcheck(op.args[0])
        offset = _reljmpoffset(context, op.args[0])
        if offset == 0:
            logging.warning(
                'Line %d: A BRA of +2 bytes (so, an ordinary PC increment) is not '
                'supported by the usual relative jump techniques; generating a NOP '
                '(MOVE R0, R0) instead', op.lineno)
            op = op._replace(todo=None, hex='0004')
        else:
            digits = (0xA, offset - 1) if offset > 0 else (0xF, -offset - 1)
            op = op._replace(todo=None, hex='{:X}0{:02X}'.format(*digits))

    # We can still update pos whether we've parsed all args or not.
    return context.advance_by_bytes(2), op
Exemplo n.º 11
0
def _codegen_movb(context: Context, op: Op) -> Tuple[Context, Op]:
    """MOVB instruction."""
    op = op._replace(
        args=parse_args_if_able(_PARSE_OPTIONS, context, op, Type.REGISTER
                                | Type.DEREF_REGISTER, Type.REGISTER
                                | Type.DEREF_REGISTER))
    # Both arguments to this opcode should be parseable.
    if op.args[0].argtype == op.args[1].argtype:
        raise ValueError(
            'One MOVB argument should be a register, and the other should be a '
            'register dereference')
    if all_args_parsed(op.args):
        nybble, argderef, argreg = ((6, op.args[1],
                                     op.args[0]) if op.args[0].argtype
                                    & Type.REGISTER else
                                    (7, op.args[0], op.args[1]))
        _regcheck(argreg)
        _regderefcheck(argderef, postcrem_from=-4, postcrem_to=4)
        modifier = _postcrement_to_modifier(argderef.postcrement)
        digits = (nybble, argreg.integer, argderef.integer, modifier)
        op = op._replace(todo=None, hex='{:X}{:X}{:X}{:X}'.format(*digits))
    # We can still update pos whether we've parsed all args or not.
    return context.advance_by_bytes(2), op
Exemplo n.º 12
0
def _codegen_rcall(context: Context, op: Op) -> Tuple[Context, Op]:
    """RCALL (R=relocatable) pseudoinstruction: INC2 Rx,R0; BRA <addr>."""
    op = op._replace(args=parse_args_if_able(_PARSE_OPTIONS, context, op,
                                             Type.ADDRESS, Type.REGISTER))
    assert op.args is not None
    if all_args_parsed(op.args) and context.pos is not None:
        _jmpdestcheck(op.args[0])
        _regcheck(op.args[1])
        offset = _reljmpoffset(context, op.args[0])
        if offset == 0:
            logging.warning(
                'Line %d: A +2-byte RCALL (so, an ordinary PC increment) is not '
                'supported by the usual relative jump techniques; generating a NOP '
                '(MOVE R0, R0) instead', op.lineno)
            op = op._replace(todo=None,
                             hex='0{:X}030004'.format(op.args[1].integer))
        else:
            digits = ((op.args[1].integer, 0xA, offset - 1) if offset > 0 else
                      (op.args[1].integer, 0xF, -offset - 1))
            op = op._replace(todo=None,
                             hex='0{:X}03{:X}0{:02X}'.format(*digits))

    # We can still update pos whether we've parsed all args or not.
    return context.advance_by_bytes(4), op
Exemplo n.º 13
0
    def codegen_add_or_sub(context: Context, op: Op) -> Tuple[Context, Op]:
        op = op._replace(args=parse_args_if_able(_PARSE_OPTIONS, context, op,
                                                 Type.REGISTER, Type.NUMBER
                                                 | Type.REGISTER))
        if all_args_parsed(op.args):
            _regcheck(op.args[0])

            # Adding/subtracting an immediate value to/from a register.
            if op.args[1].argtype & Type.NUMBER:
                if not 0 <= op.args[1].integer <= 256:
                    raise ValueError('Literal {!r} not in range 0..256'.format(
                        op.args[1].stripped))
                elif op.args[1].integer == 0:
                    assert op.opcode is not None  # mypy...
                    logging.warning(
                        'Line %d: A #0 literal argument to %s is not supported by the %s '
                        'instruction; generating a NOP (MOVE R0, R0) instead',
                        op.lineno, op.opcode.upper(), op.opcode.upper())
                    op = op._replace(todo=None, hex='0004')
                else:
                    digits = (0xA if add_or_sub == 'add' else 0xF,
                              op.args[0].integer,
                              (op.args[1].integer - 1) % 256)
                    op = op._replace(todo=None,
                                     hex='{:X}{:X}{:02X}'.format(*digits))

            # Adding/subtracting the LSB of one register to/from another register.
            else:
                _regcheck(op.args[1])
                digits = (op.args[0].integer, op.args[1].integer,
                          8 if add_or_sub == 'add' else 9)
                op = op._replace(todo=None,
                                 hex='0{:X}{:X}{:X}'.format(*digits))

        # We can still update pos whether we've parsed all args or not.
        return context.advance_by_bytes(2), op
Exemplo n.º 14
0
def _codegen_nop(context: Context, op: Op) -> Tuple[Context, Op]:
    """NOP pseudoinstruction: MOVE R0, R0."""
    # This opcode takes no arguments. We still parse to make sure there are none.
    op = op._replace(args=parse_args_if_able(_PARSE_OPTIONS, context, op))
    op = op._replace(todo=None, hex='0004')
    return context.advance(op.hex), op