516 lines
14 KiB
Python
516 lines
14 KiB
Python
|
# **************************************************************
|
||
|
# 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()
|
||
|
|