def __init__(self): self.regs = CPURegisters() self.flags = UInt8() self.mem = MemoryController() self.iomap = IOMap() self.mem.add_map(0x0, MemoryMap(0x2000)) self.mem.add_map(0xa, self.iomap) self.cpu_hooks = {} self.devices = [] self.__opcodes = {} for name in dir(self.__class__): if name[:7] == 'opcode_': self.__opcodes.update({int(name[7:], 16): getattr(self, name)})
def setUp(self): self.u8 = UInt8() self.u8.toggle(0) self.u8.bit(6, True) self.mem = MemoryMap(0x2000) self.mem.write(65) self.mem.write16(1024) self.mem[0x100] = 65 self.mem.ptr = 0x0 self.mc = MemoryController() self.mc.add_map(0x0, self.mem) self.mc.write(65) self.mc.write16(1024) self.mc[0x100] = 65 self.mc.ptr = 0x0
class TestMemoryClass(unittest.TestCase): def setUp(self): self.u8 = UInt8() self.u8.toggle(0) self.u8.bit(6, True) self.mem = MemoryMap(0x2000) self.mem.write(65) self.mem.write16(1024) self.mem[0x100] = 65 self.mem.ptr = 0x0 self.mc = MemoryController() self.mc.add_map(0x0, self.mem) self.mc.write(65) self.mc.write16(1024) self.mc[0x100] = 65 self.mc.ptr = 0x0 def test_uint8(self): self.assertEqual(self.u8.value, 65) self.assertEqual(self.u8.b, 65) self.assertEqual(self.u8.c, 'A') self.assertEqual(self.u8.bit(0), True) self.assertEqual(self.u8.bit(1), False) self.assertEqual(self.u8.bit(2), False) self.assertEqual(self.u8.bit(3), False) self.assertEqual(self.u8.bit(4), False) self.assertEqual(self.u8.bit(5), False) self.assertEqual(self.u8.bit(6), True) self.assertEqual(self.u8.bit(7), False) def test_memorymap(self): self.mem.write_protect() self.assertEqual(len(self.mem), 0x2000) self.assertEqual(self.mem.ptr, 0x0) self.assertEqual(self.mem.read(), 65) self.assertEqual(self.mem.read16(), 1024) self.assertEqual(self.mem.ptr, 0x3) self.assertEqual(self.mem[0x100], 65) self.assertEqual(self.mem.ptr, 0x3) self.assertRaises(MemoryProtectionError, self.mem.write, [0x30, 70]) self.mem.read_protect() self.assertRaises(MemoryProtectionError, self.mem.read) def test_memorycontroller(self): self.assertEqual(len(self.mc), 0xFFFF) self.assertEqual(self.mc.ptr, 0x0) self.assertEqual(self.mc.fetch(), 65) self.assertEqual(self.mc.fetch16(), 1024) self.assertEqual(self.mc.ptr, 0x3) self.assertEqual(self.mc[0x100], 65) self.assertEqual(self.mc.ptr, 0x3)
def __init__(self): self.regs = CPURegisters() self.flags = UInt8() self.mem = MemoryController() self.iomap = IOMap() self.mem.add_map(0x0, MemoryMap(0x2000)) self.mem.add_map(0xa, self.iomap) self.cpu_hooks = {} self.devices = [] self.__opcodes = {} for name in dir(self.__class__): if name[:7] == 'opcode_': self.__opcodes.update({int(name[7:], 16):getattr(self, name)})
class CPU(object): """ This class is the core CPU/Virtual Machine class. It has most of the runtime that should be platform independent. This class does not contain any code that can touch the host operating environment, so it cannot load or save data. Depending on how or where you want the binary data/memory to be located in the host environment, let it be on disk, or in a database, you will need to subclass this and enable your specific environment's functionality. The other class below this CPU, should work on most operating systems to access standard disk and memory. """ def __init__(self): self.regs = CPURegisters() self.flags = UInt8() self.mem = MemoryController() self.iomap = IOMap() self.mem.add_map(0x0, MemoryMap(0x2000)) self.mem.add_map(0xa, self.iomap) self.cpu_hooks = {} self.devices = [] self.__opcodes = {} for name in dir(self.__class__): if name[:7] == 'opcode_': self.__opcodes.update({int(name[7:], 16): getattr(self, name)}) @property def var_map(self): return self.regs.registers def __getattr__(self, name): if name in self.regs.registers: return getattr(self.regs, name) raise AttributeError("%s isn't here." % name) def add_device(self, klass): hook = klass(self) self.devices.append(hook) for port in hook.ports: self.cpu_hooks.update({port: hook}) if hasattr(hook, 'io_address'): self.iomap.add_map(hook.io_address, hook) def clear_registers(self, persistent=[]): for reg in self.regs.registers: if reg not in persistent: getattr(self.regs, reg).value = 0 def push_registers(self, regs=None): if regs is None: regs = self.regs.pushable for reg in regs: self.mem.write16(self.ss + self.sp, getattr(self.regs, reg).b) self.sp.value += 2 def pop_registers(self, regs=None): if regs is None: regs = self.regs.pushable.reverse() for reg in regs: self.sp.value -= 2 getattr(self.regs, reg).value = self.mem.read16(self.ss + self.sp) def push_value(self, value): try: value = int(value) self.mem.write16(self.ss + self.sp, value) self.sp.value += 2 except: self.mem.ptr = self.ds self.mem.write(value + chr(0)) self.mem[self.ss + self.sp] = 0 self.sp.value += 2 def pop_value(self): if self.sp.value > 0: self.sp.value -= 2 return self.mem.read16(self.ss + self.sp) raise CPUException('Stack out of range.') def resolve(self, typ, value): if typ == 0: value = value.b elif typ == 4: value = self.mem.read(value) elif typ == 5: value = self.mem.read16(value) return value def get_value(self, resolve=True): b = self.fetch() typ = b >> 4 b = b & 0xf if typ == 0: value = getattr(self, self.var_map[b]) elif typ == 1: value = b elif typ in ( 2, 4, ): value = b | self.fetch() << 4 elif typ in ( 3, 5, ): value = b | self.fetch16() << 4 if resolve: return typ, self.resolve(typ, value) return typ, value def set_value(self, dst, src, valid=None): if valid is not None and dst[0] not in valid: raise CPUException( 'Attempted to place data in invalid location for specific operation.' ) typ, dst = dst if typ == 0: dst.value = src elif typ in ( 4, 5, ): if src < 256: self.mem[self.ds + dst] = src else: self.mem.write16(self.ds + dst, src) else: raise CPUException('Attempted to move data into immediate value.') def device_command(self, cmd): for device in self.devices: handler = getattr(device, cmd, None) if handler: handler() def start_devices(self): self.device_command('start') def stop_devices(self): self.device_command('stop') def device_cycle(self): self.device_command('cycle') def fetch(self): return self.mem.fetch() def fetch16(self): return self.mem.fetch16() def process(self): """ Processes a single bytecode. """ self.mem.ptr = self.cs + self.ip op = self.fetch() if self.__opcodes.has_key(op): if not self.__opcodes[op](): self.ip.value = self.mem.ptr - self.cs.b else: raise CPUException('Invalid OpCode detected: %s' % op) def opcode_0x0(self): pass # NOP def opcode_0x1(self): """ INT """ i = self.get_value()[1] self.ip.value = self.mem.ptr - self.cs self.push_registers(['cs', 'ip']) jmp = self.mem[i * 2 + self.int_table:i * 2 + self.int_table + 2] self.regs.cs.value = jmp self.ip.value = 0 return True def opcode_0x2(self): """ MOV """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, src) def opcode_0x3(self): """ IN """ src = self.get_value()[1] dst = self.get_value(False) if self.cpu_hooks.has_key(src): self.set_value(dst, self.cpu_hooks[src].input(src)) def opcode_0x4(self): """ OUT """ src = self.get_value()[1] dst = self.get_value()[1] if self.cpu_hooks.has_key(dst): self.cpu_hooks[dst].output(dst, src) def opcode_0x5(self): """ HLT """ self.running = False def opcode_0x6(self): """ JMP """ self.mem.ptr = self.cs.b + self.get_value()[1] def opcode_0x7(self): """ PUSH """ typ, src = self.get_value() if typ == 0: self.push_value(src) else: raise CPUException('Attempt to PUSH a non-register.') def opcode_0x8(self): """ POP """ dst = self.get_value(False) self.set_value(dst, self.pop_value(), [0]) def opcode_0x9(self): """ CALL """ jmp = self.cs.b + self.get_value()[1] self.ip.value = self.mem.ptr - self.cs.b self.push_registers(['cs', 'ip']) self.mem.ptr = jmp def opcode_0xa(self): """ INC """ typ, src = self.get_value(False) if typ == 0: src.value += 1 else: raise CPUException('Attempt to increment a non-register.') def opcode_0xb(self): """ DEC """ typ, src = self.get_value(False) if typ == 0: src.value -= 1 else: raise CPUException('Attempt to decrement a non-register.') def opcode_0xc(self): """ ADD """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, src + dst[1].b) def opcode_0xd(self): """ SUB """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, dst[1].b - src) def opcode_0xe(self): """ TEST """ src = self.get_value()[1] dst = self.get_value()[1] self.flags.bit(0, src == dst) def opcode_0xf(self): """ JE """ jmp = self.get_value()[1] if self.flags.bit(0): self.mem.ptr = self.cs.b + jmp def opcode_0x10(self): """ JNE """ jmp = self.get_value()[1] if not self.flags.bit(0): self.mem.ptr = self.cs.b + jmp def opcode_0x11(self): """ CMP """ src = self.get_value()[1] dst = self.get_value()[1] result = src - dst self.flags.bit(0, True if result == 0 else False) def opcode_0x12(self): """ MUL """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, dst[1].b * src) def opcode_0x13(self): """ DIV """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, dst[1].b / src) def opcode_0x14(self): """ PUSHF """ self.push_value(self.flags.b) def opcode_0x15(self): """ POPF """ self.flags.value = self.pop_value() def opcode_0x16(self): """ AND """ src = self.get_value()[1] dst = self.get_value(False) v = self.resolve(*dst) self.set_value(dst, v & src, [0]) def opcode_0x17(self): """ OR """ src = self.get_value()[1] dst = self.get_value(False) v = self.resolve(*dst) self.set_value(dst, v | src, [0]) def opcode_0x18(self): """ XOR """ src = self.get_value()[1] dst = self.get_value(False) v = self.resolve(*dst) self.set_value(dst, v ^ src, [0]) def opcode_0x19(self): """ NOT """ src = self.get_value()[1] dst = self.get_value(False) v = self.resolve(*dst) self.set_value(dst, v & ~src, [0]) def opcode_0x1a(self): """ RET """ self.pop_registers(['ip', 'cs']) return True def run(self, cs=0, persistent=[]): self.clear_registers(persistent) self.cs.value = cs self.mem.ptr = 0 self.int_table = len(self.mem) - 512 del persistent del cs self.running = True while self.running: if 'bp' in self.__dict__ and self.bp == self.mem.ptr: break self.device_cycle() self.process() self.stop_devices() return 0 def loadbin(self, filename, dest, compressed=False): if not compressed: bindata = open(filename, 'rb').read() else: bindata = zlib.decompress(open(filename, 'rb').read()) self.mem.writeblock(dest, bindata) self.mem.ptr = 0 def savebin(self, filename, src, size, compress=False): if not compress: open(filename, 'wb').write(self.mem.readblock(src, size)) else: open(filename, 'wb').write(zlib.compress(self.mem.readblock(src, size)))
class CPU(object): """ This class is the core CPU/Virtual Machine class. It has most of the runtime that should be platform independent. This class does not contain any code that can touch the host operating environment, so it cannot load or save data. Depending on how or where you want the binary data/memory to be located in the host environment, let it be on disk, or in a database, you will need to subclass this and enable your specific environment's functionality. The other class below this CPU, should work on most operating systems to access standard disk and memory. """ def __init__(self): self.regs = CPURegisters() self.flags = UInt8() self.mem = MemoryController() self.iomap = IOMap() self.mem.add_map(0x0, MemoryMap(0x2000)) self.mem.add_map(0xa, self.iomap) self.cpu_hooks = {} self.devices = [] self.__opcodes = {} for name in dir(self.__class__): if name[:7] == 'opcode_': self.__opcodes.update({int(name[7:], 16):getattr(self, name)}) @property def var_map(self): return self.regs.registers def __getattr__(self, name): if name in self.regs.registers: return getattr(self.regs, name) raise AttributeError("%s isn't here." % name) def add_device(self, klass): hook = klass(self) self.devices.append(hook) for port in hook.ports: self.cpu_hooks.update({port: hook}) if hasattr(hook, 'io_address'): self.iomap.add_map(hook.io_address, hook) def clear_registers(self, persistent=[]): for reg in self.regs.registers: if reg not in persistent: getattr(self.regs, reg).value = 0 def push_registers(self, regs=None): if regs is None: regs = self.regs.pushable for reg in regs: self.mem.write16(self.ss+self.sp, getattr(self.regs, reg).b) self.sp.value += 2 def pop_registers(self, regs=None): if regs is None: regs = self.regs.pushable.reverse() for reg in regs: self.sp.value -= 2 getattr(self.regs, reg).value = self.mem.read16(self.ss+self.sp) def push_value(self, value): try: value = int(value) self.mem.write16(self.ss+self.sp,value) self.sp.value += 2 except: self.mem.ptr = self.ds self.mem.write(value+chr(0)) self.mem[self.ss+self.sp] = 0 self.sp.value += 2 def pop_value(self): if self.sp.value > 0: self.sp.value -= 2 return self.mem.read16(self.ss+self.sp) raise CPUException('Stack out of range.') def resolve(self, typ, value): if typ == 0: value = value.b elif typ == 4: value = self.mem.read(value) elif typ == 5: value = self.mem.read16(value) return value def get_value(self, resolve=True): b = self.fetch() typ = b>>4 b = b&0xf if typ == 0: value = getattr(self, self.var_map[b]) elif typ == 1: value = b elif typ in (2,4,): value = b|self.fetch()<<4 elif typ in (3,5,): value = b|self.fetch16()<<4 if resolve: return typ, self.resolve(typ, value) return typ, value def set_value(self, dst, src, valid=None): if valid is not None and dst[0] not in valid: raise CPUException('Attempted to place data in invalid location for specific operation.') typ, dst = dst if typ == 0: dst.value = src elif typ in (4,5,): if src < 256: self.mem[self.ds+dst] = src else: self.mem.write16(self.ds+dst, src) else: raise CPUException('Attempted to move data into immediate value.') def device_command(self, cmd): for device in self.devices: handler = getattr(device, cmd, None) if handler: handler() def start_devices(self): self.device_command('start') def stop_devices(self): self.device_command('stop') def device_cycle(self): self.device_command('cycle') def fetch(self): return self.mem.fetch() def fetch16(self): return self.mem.fetch16() def process(self): """ Processes a single bytecode. """ self.mem.ptr = self.cs+self.ip op = self.fetch() if self.__opcodes.has_key(op): if not self.__opcodes[op](): self.ip.value = self.mem.ptr-self.cs.b else: raise CPUException('Invalid OpCode detected: %s' % op) def opcode_0x0(self): pass # NOP def opcode_0x1(self): """ INT """ i = self.get_value()[1] self.ip.value = self.mem.ptr-self.cs self.push_registers(['cs', 'ip']) jmp = self.mem[i*2+self.int_table:i*2+self.int_table+2] self.regs.cs.value = jmp self.ip.value = 0 return True def opcode_0x2(self): """ MOV """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, src) def opcode_0x3(self): """ IN """ src = self.get_value()[1] dst = self.get_value(False) if self.cpu_hooks.has_key(src): self.set_value(dst, self.cpu_hooks[src].input(src)) def opcode_0x4(self): """ OUT """ src = self.get_value()[1] dst = self.get_value()[1] if self.cpu_hooks.has_key(dst): self.cpu_hooks[dst].output(dst, src) def opcode_0x5(self): """ HLT """ self.running = False def opcode_0x6(self): """ JMP """ self.mem.ptr = self.cs.b+self.get_value()[1] def opcode_0x7(self): """ PUSH """ typ, src = self.get_value() if typ == 0: self.push_value(src) else: raise CPUException('Attempt to PUSH a non-register.') def opcode_0x8(self): """ POP """ dst = self.get_value(False) self.set_value(dst, self.pop_value(), [0]) def opcode_0x9(self): """ CALL """ jmp = self.cs.b+self.get_value()[1] self.ip.value = self.mem.ptr-self.cs.b self.push_registers(['cs', 'ip']) self.mem.ptr = jmp def opcode_0xa(self): """ INC """ typ, src = self.get_value(False) if typ == 0: src.value +=1 else: raise CPUException('Attempt to increment a non-register.') def opcode_0xb(self): """ DEC """ typ, src = self.get_value(False) if typ == 0: src.value -=1 else: raise CPUException('Attempt to decrement a non-register.') def opcode_0xc(self): """ ADD """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, src+dst[1].b) def opcode_0xd(self): """ SUB """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, dst[1].b-src) def opcode_0xe(self): """ TEST """ src = self.get_value()[1] dst = self.get_value()[1] self.flags.bit(0, src == dst) def opcode_0xf(self): """ JE """ jmp = self.get_value()[1] if self.flags.bit(0): self.mem.ptr = self.cs.b+jmp def opcode_0x10(self): """ JNE """ jmp = self.get_value()[1] if not self.flags.bit(0): self.mem.ptr = self.cs.b+jmp def opcode_0x11(self): """ CMP """ src = self.get_value()[1] dst = self.get_value()[1] result = src - dst self.flags.bit(0, True if result == 0 else False) def opcode_0x12(self): """ MUL """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, dst[1].b*src) def opcode_0x13(self): """ DIV """ src = self.get_value()[1] dst = self.get_value(False) self.set_value(dst, dst[1].b/src) def opcode_0x14(self): """ PUSHF """ self.push_value(self.flags.b) def opcode_0x15(self): """ POPF """ self.flags.value = self.pop_value() def opcode_0x16(self): """ AND """ src = self.get_value()[1] dst = self.get_value(False) v = self.resolve(*dst) self.set_value(dst, v & src, [0]) def opcode_0x17(self): """ OR """ src = self.get_value()[1] dst = self.get_value(False) v = self.resolve(*dst) self.set_value(dst, v | src, [0]) def opcode_0x18(self): """ XOR """ src = self.get_value()[1] dst = self.get_value(False) v = self.resolve(*dst) self.set_value(dst, v ^ src, [0]) def opcode_0x19(self): """ NOT """ src = self.get_value()[1] dst = self.get_value(False) v = self.resolve(*dst) self.set_value(dst, v & ~src, [0]) def opcode_0x1a(self): """ RET """ self.pop_registers(['ip', 'cs']) return True def run(self, cs=0, persistent=[]): self.clear_registers(persistent) self.cs.value = cs self.mem.ptr = 0 self.int_table = len(self.mem)-512 del persistent del cs self.running = True while self.running: if 'bp' in self.__dict__ and self.bp == self.mem.ptr: break self.device_cycle() self.process() self.stop_devices() return 0 def loadbin(self, filename, dest, compressed=False): if not compressed: bindata = open(filename, 'rb').read() else: bindata = zlib.decompress(open(filename, 'rb').read()) self.mem.writeblock(dest, bindata) self.mem.ptr = 0 def savebin(self, filename, src, size, compress=False): if not compress: open(filename, 'wb').write(self.mem.readblock(src, size)) else: open(filename, 'wb').write(zlib.compress(self.mem.readblock(src, size)))