ilo-vm/source/ilo.py

515 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()