# ************************************************************** # crc's _ _ # (_) | ___ # | | |/ _ \ a tiny virtual computer # | | | (_) | 64kw RAM, 32-bit, Dual Stack, MISC # |_|_|\___/ ilo.py (c) charles childers # (c) arland childers # ************************************************************** #Ilo.py v2.2b #Changes: # Split debug modes: # 'instrs': Counts overall time, and counts each instruction/bundle. # 'timing': Just gives elapsed time. # 'cProfile': Using cProfile, measure time & time for all instructions+overhead. Does not count time for unoptimised bundles. # Added more optimised instruction bundles, focusing on DTC. # Optimised many instructions, removing uneeded vars. Added raw_lit to not limit it when it is not needed. # Faster block unpacking. The uncached blocks seem to only be ~1.7x slower instead of ~2x, now. # Sorted some of the instructions listing, in the hopes it may make an improvement. It did not seem to. # Added calls to limit the rom and blocks as they get put into memory. #--- CONFIG BLOCK_FILE = 'ilo.blocks' ROM_FILE = 'ilo.rom' BLOCK_CACHE = True DEBUG = False # False for nothing # 'timing' just gives elapsed time. Negligible time loss # 'Instrs' counts instructions & bundles, but ~2.3x slower # 'cProfile' for cProfile, but ~3.25x slower #--- ILO import os def limit(val): val &= 0xffffffff if val & 0x80000000: val -= 0x100000000 return val def div_mod(a, b): x = abs(a) y = abs(b) q, r = divmod(x, y) if a < 0 and b < 0: r *= -1 elif a > 0 and b < 0: q *= -1 elif a < 0 and b > 0: r *= -1 q *= -1 return q, r class Stack: def __init__(self, size): self.sp = 0 self.data = [0] * size self.max = size def lit(self, val): self.sp += 1 self.data[self.sp] = ((val & 0xffffffff)-0x100000000) if val & 0x80000000 else (val & 0xffffffff) def raw_lit(self, val): self.sp += 1 self.data[self.sp] = val def pop(self): self.sp -= 1 return self.data[self.sp + 1] def top(self): return self.data[self.sp] def second(self): return self.data[self.sp - 1] def depth(self): return self.sp class BlockTools_NoCache: def __init__(self, block_file, rom_file, data_stack, memory): self.BLOCK_FILE = block_file self.ROM_FILE = rom_file self.data_stack = data_stack self.memory = memory def block_unpack(self, block): return [(limit(block[i] | (block[i + 1] << 8) | (block[i + 2] << 16) | (block[i + 3] << 24))) for i in range(0, len(block), 4)] def block_pack(self, *values): return b''.join(val.to_bytes(4, 'little', signed=True) for val in values) def read_block(self): buffer = self.data_stack.pop() block = self.data_stack.pop() with open(self.BLOCK_FILE, 'r+b') as f: f.seek(4096 * block) self.memory[buffer: buffer + 1024] = list(self.block_unpack(f.read(4096))) def block_write(self): buffer = self.data_stack.pop() block = self.data_stack.pop() with open(self.BLOCK_FILE, 'r+b') as f: f.seek(4096 * block) f.write(self.block_pack(*self.memory[buffer: buffer + 1024])) def load_image(self): with open(self.ROM_FILE, 'rb') as f: raw_data = f.read(4 * 65536) self.memory[:] = [limit(int.from_bytes(raw_data[i:i + 4], 'little')) for i in range(0, len(raw_data), 4)] def save_image(self): with open(self.ROM_FILE, 'wb') as f: for value in self.memory[:65536]: f.write(value.to_bytes(4, 'little')) class BlockTools(BlockTools_NoCache): # caches blocks. def __init__(self, block_file, rom_file, data_stack, memory): self.BLOCK_FILE = block_file self.ROM_FILE = rom_file self.data_stack = data_stack self.memory = memory self.block_time = os.path.getmtime(block_file) self.blocks = [] def read_block(self): if self.block_time != os.path.getmtime(self.BLOCK_FILE): self.read_all_blocks() self.block_time = os.path.getmtime(self.BLOCK_FILE) buffer = self.data_stack.pop() self.memory[buffer: buffer + 1024] = self.blocks[self.data_stack.pop()] def read_all_blocks(self): with open(self.BLOCK_FILE, 'rb') as f: all_block_data = f.read() self.blocks = [list(self.block_unpack(all_block_data[i:i+4096])) for i in range(0, len(all_block_data), 4096)] return self.blocks class ilo: def __init__(self, block_file='ilo.blocks', rom_file='ilo.rom', cache_blocks=False): self.ip = 0 self.memory = [0] self.input_buffer = [] self.data = Stack(33) self.address = Stack(257) if cache_blocks: self.block_tools = BlockTools(block_file, rom_file, self.data, self.memory) else: self.block_tools = BlockTools_NoCache(block_file, rom_file, self.data, self.memory) self.instructions = { 1: self.i01, 4: self.i04, 11: self.i11, 6: self.i06, 2: self.i02, 5: self.i05, 18: self.i18, 2049: self.i2049, 2832: self.i2832, 10: self.i10, 68288770: self.i68288770, 8: self.i08, 17563906: self.i17563906, 7: self.i07, 524545: self.i524545, 3: self.i03, 459014: self.i459014, 9: self.i09, 12: self.i12, 13: self.i13, 14: self.i14, 15: self.i15, 16: self.i16, 17: self.i17, 19: self.i19, 20: self.i20, 21: self.i21, 22: self.i22, 23: self.i23, 24: self.i24, 25: self.i25, 26: self.i26, 27: self.i27, 28: self.i28, 29: self.i29, 1793: self.i1793, 459009: self.i459009, 67502597: self.i67502597, 100926722: self.i100926722, 302059522: self.i302059522, 2818: self.i2818, 219156993: self.i219156993, 33689603: self.i33689603, 167840769: self.i167840769, 525572: self.i525572, 134284806: self.i134284806 } def i00(self): # nop pass def i01(self): # lit self.ip += 1 self.data.lit(self.memory[self.ip]) def i02(self): # dup self.data.raw_lit(self.data.top()) def i03(self): # drop self.data.pop() def i04(self): # swap a = self.data.pop() b = self.data.pop() self.data.raw_lit(a) self.data.raw_lit(b) def i05(self): # push self.address.raw_lit(self.data.pop()) def i06(self): # pop self.data.raw_lit(self.address.pop()) def i07(self): # jump self.ip = self.data.pop() - 1 def i08(self): # call self.address.raw_lit(self.ip) self.ip = self.data.pop() - 1 def i09(self): # ccall target = self.data.pop() flag = self.data.pop() if flag != 0: self.address.raw_lit(self.ip) self.ip = target - 1 def i10(self): # cjump target = self.data.pop() flag = self.data.pop() if flag != 0: self.ip = target - 1 def i11(self): # return self.ip = self.address.pop() def i12(self): # equal self.data.raw_lit(-(self.data.pop() == self.data.pop())) def i13(self): # not equal self.data.raw_lit(-(self.data.pop() != self.data.pop())) def i14(self): # less than b = self.data.pop() a = self.data.pop() self.data.raw_lit(-(a < b)) def i15(self): # greater than b = self.data.pop() a = self.data.pop() self.data.raw_lit(-(a > b)) def i16(self): # fetch self.data.raw_lit(self.memory[self.data.pop()]) def i17(self): # store a = self.data.pop() self.memory[a] = self.data.pop() def i18(self): # add self.data.lit(self.data.pop() + self.data.pop()) def i19(self): # subtract b = self.data.pop() self.data.lit(self.data.pop() - b) def i20(self): # multiply self.data.lit(self.data.pop() * self.data.pop()) def i21(self): # divmod a = self.data.pop() a, b = div_mod(self.data.pop(), a) self.data.raw_lit(b) self.data.raw_lit(a) def i22(self): # and self.data.raw_lit(self.data.pop() & self.data.pop()) def i23(self): # or self.data.raw_lit(self.data.pop() | self.data.pop()) def i24(self): # xor self.data.raw_lit(self.data.pop() ^ self.data.pop()) def i25(self): # shift left b = self.data.pop() self.data.lit(self.data.pop() << b) def i26(self): # shift right b = self.data.pop() self.data.raw_lit(self.data.pop() >> b) def i27(self): # compare memory l = self.data.pop() dest = self.data.pop() src = self.data.pop() self.data.raw_lit(-(self.memory[dest: dest + l] == self.memory[src: src + l])) def i28(self): # copy memory l = self.data.pop() dest = self.data.pop() src = self.data.pop() self.memory[dest: dest + l] = self.memory[src: src + l] def i29(self): # io i = self.data.pop() if i == 0: print(chr(self.data.pop()), end='') elif i == 1: if not len(self.input_buffer): self.input_buffer = ['\n'] + list(input())[::-1] self.data.lit(ord(self.input_buffer.pop())) elif i == 2: self.block_tools.read_block() elif i == 3: self.block_tools.block_write() elif i == 4: self.block_tools.save_image() elif i == 5: self.block_tools.load_image() self.ip = -1 elif i == 6: self.ip = 65535 elif i == 7: self.data.lit(self.data.depth()) self.data.lit(self.address.depth()) def i2049(self): # lica.... self.address.lit(self.ip + 1) self.ip = self.memory[self.ip + 1] - 1 def i1793(self): # liju.... self.ip = self.memory[self.ip + 1] - 1 def i524545(self): # lilica.. self.data.lit(self.memory[self.ip + 1]) self.address.lit(self.ip + 2) self.ip = self.memory[self.ip + 2] - 1 def i459009(self): # liliju.. self.data.lit(self.memory[self.ip + 1]) self.ip = self.memory[self.ip + 2] - 1 def i67502597(self): # puduposw 'over' self.data.lit(self.data.second()) def i17563906(self): # dulieqli self.data.lit(-(self.memory[self.ip + 1] == self.data.top())) self.data.lit(self.memory[self.ip + 2]) self.ip += 2 def i100926722(self): # dupuswpo 'tuck' a = self.data.pop() b = self.data.pop() self.data.lit(a) self.data.lit(b) self.data.lit(a) def i302059522(self): # dufeliad self.data.lit(self.memory[self.data.top()] + self.memory[self.ip + 1]) self.ip += 1 def i2818(self): # dure.... self.data.raw_lit(self.data.top()) self.ip = self.address.pop() def i219156993(self): # liadfene self.ip += 1 self.data.lit(-(limit(self.memory[self.memory[self.ip] + self.data.pop()]) != self.data.pop())) def i33689603(self): # drfedudu self.data.pop() m = self.memory[self.data.pop()] self.data.lit(m) self.data.lit(m) self.data.lit(m) def i167840769(self): # lieqlicj self.ip += 2 if -(self.memory[self.ip-1] == self.data.pop()) != 0: self.ip = self.memory[self.ip] - 1 def i2832(self): # fere.... self.data.raw_lit(self.memory[self.data.pop()]) self.ip = self.address.pop() def i68288770(self): #duliadsw self.data.raw_lit(self.data.top()) self.ip += 1 self.data.lit(limit(self.memory[self.ip]) + self.data.pop()) a = self.data.pop() b = self.data.pop() self.data.raw_lit(a) self.data.raw_lit(b) def i525572(self): # swpuca.. a = self.data.pop() self.address.raw_lit(self.data.pop()) self.data.raw_lit(a) self.address.raw_lit(self.ip) self.ip = self.data.pop() - 1 def i459014(self): # poliju.. self.data.raw_lit(self.address.pop()) self.ip = self.memory[self.ip+1] - 1 def i134284806(self): # popolica self.data.raw_lit(self.address.pop()) self.data.raw_lit(self.address.pop()) self.ip += 1 self.address.lit(self.ip) self.ip = self.memory[self.ip] - 1 def print_opcode_bundle(self, opcode): print(opcode & 0xFF, end=' ') print((opcode >> 8) & 0xFF, end=' ') print((opcode >> 16) & 0xFF, end=' ') print((opcode >> 24) & 0xFF, end=': ') def process(self, c): if c in self.instructions: self.instructions[c]() def execute(self, start): self.ip = start while self.ip < 65536: m = self.memory[self.ip] if m in self.instructions: self.instructions[m]() else: self.process(m & 0xFF) self.process((m >> 8) & 0xFF) self.process((m >> 16) & 0xFF) self.process((m >> 24) & 0xFF) self.ip += 1 def debug_execute(self, start): data = {} self.ip = start while self.ip < 65536: m = self.memory[self.ip] if m in self.instructions: self.instructions[m]() else: self.process(m & 0xFF) self.process((m >> 8) & 0xFF) self.process((m >> 16) & 0xFF) self.process((m >> 24) & 0xFF) data[str(m & 0xFF)] = data.get(str(m & 0xFF), 0) + 1 data[str((m >> 8) & 0xFF)] = data.get(str((m >> 8) & 0xFF), 0) + 1 data[str((m >> 16) & 0xFF)] = data.get(str((m >> 16) & 0xFF), 0) + 1 data[str((m >> 24) & 0xFF)] = data.get(str((m >> 24) & 0xFF), 0) + 1 data[str(m)] = data.get(str(m), 0) + 1 self.ip += 1 return data def main(self, debug=False): self.block_tools.load_image() if BLOCK_CACHE: self.block_tools.read_all_blocks() if debug: return self.debug_execute(0) else: self.execute(0) def sort_dict(input_dict): sorted_items = sorted(input_dict.items(), key=lambda x: x[1], reverse=True) sorted_dict = dict(sorted_items) return sorted_dict if __name__ == '__main__': if DEBUG: if DEBUG.lower() == 'instrs': import time start_time = time.time() k = ilo(BLOCK_FILE, ROM_FILE, BLOCK_CACHE) data = k.main(True) end_time = time.time() print(str(sort_dict(data)).replace(',', '\n').strip('{}')) print('\nElapsed Time: ', end_time - start_time) elif DEBUG.lower() == 'timing': import time start_time = time.time() ilo(BLOCK_FILE, ROM_FILE, BLOCK_CACHE).main() end_time = time.time() print('\nElapsed Time: ', end_time - start_time) elif DEBUG.lower() == 'cprofile': import cProfile cProfile.run('ilo(BLOCK_FILE, ROM_FILE, BLOCK_CACHE).main()', sort='tottime') else: print('Unknown debug mode') else: ilo(BLOCK_FILE, ROM_FILE, BLOCK_CACHE).main()