from ctypes import c_uint8, c_uint16, c_uint32 class Addressable: def __init__(self, start=0, end=0): self.start = start self.end = end def addr_to_pos(self, addr): if not (self.start <= addr < self.end): raise KeyError("Address out of space") return addr - self.start def merge_bytes (byte_iter): val = 0 for i,v in enumerate(byte_ouput): val += v.value << 8 * i return val def split_bytes (val, byte_count): return tuple( c_uint8(val >> 8 * i) for i in range(byte_count) ) class Memory32 (Addressable): """ This is a raw 32-bit word memory addressable at a byte level. Internally it is defined as c_uint8 list. Special functions are needed to access halfs (c_uint16) and words (c_uint32). """ def __init__(self, start=0, end=0, bigEndian=False): super().__init__(start, end) self.bigEndian = bigEndian self.lastChange = None # Pre-allocate or allocate or allocate per write? self.data = [None] * (end - start) def get_byte(self, addr): return self.data[ self.addr_to_pos(addr) ] def set_byte(self, addr, val): if not isinstance(val, c_uint8): if val > 0xFF: raise ValueError("Value is larger than a byte") self.data[ self.addr_to_pos(addr) ] = val if isinstance(val, c_uint8) else c_uint8(val) self.lastChange = range(addr, addr+1) def get_half(self,addr): byte_output = (get_byte(addr), get_byte(addr+1)) if self.bigEndian: byte_output = reversed(byte_output) return c_uint16( merge_bytes(byte_output) ) def set_half(self, addr, val): if not isinstance(val, c_uint16): if val > 0xFFFF: raise ValueError("Value is larger than a half") bytes = split_bytes(val, 2) if self.bigEndian: bytes = reversed(bytes) for i,v in enumerate(bytes): self.set_byte(addr+i, v) self.lastChange = range(addr,addr+2) def get_word(self, pos): byte_output = tuple(get_byte(addr+i) for i in range(4)) if self.bigEndian: byte_output = reversed(byte_output) return c_uint32( merge_bytes(byte_output) ) def set_word(self, addr, val): if not isinstance(val, c_uint32): if val > 0xFFFFFFFF: raise ValueError("Value is larger than a word") bytes = split_bytes(val, 4) if self.bigEndian: bytes = reversed(bytes) for i,v in enumerate(bytes): self.set_byte(addr+i, v) self.lastChange = range(addr,addr+4) def __str__(self): out = " " out += "-" * 50 out += "\n" for i,d in enumerate(self.data): if d is not None: addr = i+self.start out += "->" if addr in self.lastChange else " " out += f"| {addr:#20x} | {d.value:#5} | {d.value:#04x} | {d.value:#010b} |" out += "\n" out += " " out += "-" * 50 out += "\n" return out class Memory32RO (Memory32): def set_byte(self, addr, val): raise NotImplementedError("Trying to write in a read-only memory") def set_half(self, addr, val): raise NotImplementedError("Trying to write in a read-only memory") def set_word(self, addr, val): raise NotImplementedError("Trying to write in a read-only memory") class CodeMemory32 (Memory32RO): def __init__(self, start=0, end=0): super().__init__(writable=False,start=start,end=end) if __name__ == "__main__": m = Memory32(start=100,end=200) print(m.start, m.end) m.set_byte(100, 246) print(m)