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
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
def codegen_data(context: Context, op: Op) -> Tuple[Context, Op]: # Accumulate hex code here, and track whether we have its final value worked # out, or if we're still waiting on labels. hexparts = [] all_hex_ok = True # Align the data to match the data quantum, if directed. if align: if element_size != 1: if context.pos is None: raise ValueError( 'Unresolved labels above this line (or other factors) make it ' 'impossible to know how to align this data statement. Consider ' "an ORG statement to make this data's memory location explicit.") hexparts.append('00' * (context.pos % element_size)) # Generate data for each arg. Unlike nearly all other statements, we do most # of the parsing ourselves. for arg in op.args: # Is the argument a string? if arg.stripped.startswith('"') or arg.stripped.startswith("'"): hexparts.append(''.join( val.to_bytes(element_size, endianity).hex().upper() for val in parse_string(parse_options, context, arg.stripped))) # No, it must be a single integer value. else: # Does the argument look like a label? If so, try to resolve it and take # its value. If not, let's parse the argument as an integer value. if LABEL_RE.fullmatch(arg.stripped): all_hex_ok &= arg.stripped in context.labels val = context.labels[arg.stripped] if all_hex_ok else 0 else: val = parse_integer(parse_options, context, arg.stripped) # Now encode the value as hex. hexparts.append(val.to_bytes(element_size, endianity).hex().upper()) # Package the hex data from all the args and, if appropriate, mark our job # as complete. op = op._replace(todo=None if all_hex_ok else op.todo, hex=''.join(hexparts)) return context.advance(op.hex), op
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