def _compile(self) -> Memory: """ Fills .bytecode field of IRElements in self._intermediate, returns memory block with whole source bytecode """ curr_ip = 0 # instruction pointer value at the point of running current op mem = ExtendableMemory(self._char_bit) for line in self._intermediate: try: logging.debug('compile: %s', line.source) if not line.statement: continue prev_ip = curr_ip if isinstance(line.statement, Data): for value in line.statement.values: mem.append(self._resolve_expression(value), line.statement.datatype, Endianness.Big) elif isinstance(line.statement, Instruction): op = line.statement.operation curr_ip = len(mem) + op.size_bytes mem.append(op.opcode, DataType.from_fmt('b'), Endianness.Little) if len(line.statement.args.arguments) != len(op.arg_def): raise SyntaxError( 'invalid number of arguments for operation %s: expected %d, got %d' % (op.mnemonic, len(op.arg_def), len(line.statement.args.arguments))) for idx, arg in enumerate(line.statement.args.arguments): arg_datatype = DataType.from_fmt(op.arg_def[idx]) if isinstance(arg, Register): mem.append(arg.value, arg_datatype, op.args_endianness) else: val = self._resolve_expression(arg) mem.append(self._resolve_expression(arg), arg_datatype, op.args_endianness) else: raise AssertionError('unhandled IR type: %s' % type(line.statement).__name__) curr_ip = len(mem) line.bytecode[:] = mem[prev_ip:] except Exception as err: raise Exception('could not assemble line: %s' % (line.source, )) from err return mem
def _resolve_expression(self, expr: Expression, already_resolved: Set[str] = None) -> int: logging.debug('%s = ?' % (expr, )) if not already_resolved: already_resolved = set() if isinstance(expr, NumericExpression): logging.debug('NumericExpression: %d' % expr.value) return expr.value elif isinstance(expr, CharacterExpression): logging.debug('CharacterExpression: %d' % ord(expr.value)) return ord(expr.value) elif isinstance(expr, ConstantExpression): if expr.name in already_resolved: raise ValueError('circular constant definition: %s' % expr.name) val = self._constants[expr.name] if not isinstance(val, int): val = self._resolve_expression( self._constants[expr.name], already_resolved | set(expr.name)) self._constants[expr.name] = val else: logging.debug('alredy resolved: %s = %s' % (expr.name, val)) logging.debug('ConstantExpression %s' % (val, )) return val elif isinstance(expr, UnaryExpression): logging.debug('UnaryExpression: %s' % (expr, )) if expr.operator == 'sizeof': assert isinstance(expr.operand, ConstantExpression) try: return DataType.from_fmt(expr.operand.name).size_bytes except KeyError: return CPU.OPERATIONS_BY_MNEMONIC[ expr.operand.name].size_bytes elif expr.operator == 'alignof': assert isinstance(expr.operand, ConstantExpression) return DataType.from_fmt(expr.operand.name).alignment else: return eval(expr.operator + str(self._resolve_expression(expr.operand))) elif isinstance(expr, BinaryExpression): logging.debug('BinaryExpression: %s' % (expr, )) return eval( str(self._resolve_expression(expr.lhs)) + expr.operator.replace('/', '//') # TODO: hack for integer division + str(self._resolve_expression(expr.rhs))) else: raise AssertionError('unknown expression type: %r' % (expr, ))
class Data(NamedTuple, Statement): """ db/a/w ARGUMENT_LIST """ datatype: DataType values: ArgumentList DATATYPES = { 'db': DataType.from_fmt('b'), 'da': DataType.from_fmt('a'), 'dw': DataType.from_fmt('w'), } @staticmethod def build(tokens: List[str]): return Data(Data.DATATYPES[tokens[0]], ExpressionList.build(tokens[1:]))
def pop(cpu: 'CPU', reg: int): """ pop - POP value from data stack into register reg = word ptr $RAM[SP] SP += sizeof_word """ cpu.registers[Register(reg)] = cpu.ram.get_fmt('w', cpu.registers.SP) cpu.registers.SP += DataType.from_fmt('w').size_bytes
def push(cpu: 'CPU', reg: int): """ push - PUSH register onto data stack SP -= sizeof_word word ptr $RAM[SP] = reg """ cpu.registers.SP -= DataType.from_fmt('w').size_bytes cpu.ram.set_fmt('w', cpu.registers.SP, cpu.registers[Register(reg)])
def ret(cpu: 'CPU'): """ ret - RETurn from subroutine IP = addr ptr $CALL_STACK[RP] RP += sizeof_addr """ addr_size = DataType.calcsize('a') cpu.registers.IP = cpu.call_stack.get_fmt('a', cpu.registers.RP) cpu.registers.RP += addr_size
def __str__(self): return ('--- REGISTERS ---\n' '%s\n' '--- PROGRAM ---\n' '%s\n' '--- RAM ---\n' '%s\n' '--- CALL_STACK ---\n' '%s\n' % (self.registers, self.program, self.ram, self.call_stack.make_dump(DataType.from_fmt('a').alignment)))
def call_r(cpu: 'CPU', reg: int): """ call.r addr - CALL subroutine, Register RP -= sizeof_addr addr ptr $CALL_STACK[RP] = IP IP = reg """ addr_size = DataType.calcsize('a') cpu.registers.RP -= addr_size cpu.call_stack.set_fmt('a', cpu.registers.RP, cpu.registers.IP) cpu.registers.IP = cpu.registers[Register(reg)]
def call(cpu: 'CPU', addr: int): """ call addr - CALL subroutine RP -= sizeof_addr addr ptr $CALL_STACK[RP] = IP IP = addr """ addr_size = DataType.calcsize('a') cpu.registers.RP -= addr_size cpu.call_stack.set_fmt('a', cpu.registers.RP, cpu.registers.IP) cpu.registers.IP = addr
def rand(cpu: 'CPU'): """ rand - put a random WORD in A A = random() """ num_bytes = DataType.calcsize('w') value = 0 for _ in range(num_bytes): value *= 2**cpu.ram.char_bit value += random.randint(0, 2**cpu.ram.char_bit) cpu.registers.A = value cpu._set_flags(cpu.registers.A)
def args_size(self) -> int: """ Size (in bytes) of this operation arguments, without the opcode """ return DataType.calcsize(self.arg_def)
'-A', '--addr-alignment', type=int, default=None, help='Address memory alignment. By default, equal to address size.') parser.add_argument( '-H', '--halt-after-instructions', type=int, default=None, help='Halts VM after executing specific number of instructions.') args = parser.parse_args() DataType._TYPES['w'] = DataType(name='w', size_bytes=args.word_size, alignment=(args.word_alignment or args.word_size)) DataType._TYPES['a'] = DataType(name='a', size_bytes=args.addr_size, alignment=(args.addr_alignment or args.addr_size)) with open(args.source[0]) as infile: asm = Assembler(char_bit=args.char_bit) if args.program_size is None: MEMORY_BLOCKS['program'] = asm.assemble_to_memory(infile.read()) else: MEMORY_BLOCKS['program'] = Memory(char_bit=args.char_bit, value=asm.assemble(infile.read()), size=args.program_size)