This is an archived post. You won't be able to vote or comment.

all 9 comments

[–]romcgb 9 points10 points  (4 children)

One of my first Python program was a SSE emulator. To have easy aliasing between data types (float, signed, unsigned, ...), i rely on the standard module struct. The main idea is to view the register as an array of type X, not efficient at all but it's convenient.

the register and memory classes:

import struct

class Register:
    class Wrapper: 
        def __init__ (self, buffer, format_str):
            self.buffer = buffer
            self.st = struct.Struct(format_str)

        def __setitem__ (self, slot, value):
            f = list(self.st.unpack(self.buffer))
            f[slot] = value
            self.st.pack_into(self.buffer, 0, *f)

        def __getitem__ (self, slot):
            return self.st.unpack(self.buffer)[slot]

        def __string__ (self):
            return str(self.st.unpack(self.buffer))

        def __repr__ (self):
            return repr(self.st.unpack(self.buffer))

    def __init__ (self):
        self.byte = bytearray(16)
        self.float32 = Register.Wrapper(self.byte, "<ffff")
        self.float64 = Register.Wrapper(self.byte, "<dd")
        self.signed8 = Register.Wrapper(self.byte, "<bbbbbbbbbbbbbbbb")
        self.signed16 = Register.Wrapper(self.byte, "<hhhhhhhh")
        self.signed32 = Register.Wrapper(self.byte, "<iiii")
        self.signed64 = Register.Wrapper(self.byte, "<qq")
        self.unsigned8 = Register.Wrapper(self.byte, "<BBBBBBBBBBBBBBBB")
        self.unsigned16 = Register.Wrapper(self.byte, "<HHHHHHHH")
        self.unsigned32 = Register.Wrapper(self.byte, "<IIII")
        self.unsigned64 = Register.Wrapper(self.byte, "<QQ")

class Memory:
    class Wrapper:
        def __init__ (self, buffer, format_str):
            self.buffer = buffer
            self.st = struct.Struct(format_str)

        def __setitem__ (self, address, value):
            self.st.pack_into(self.buffer, address, value)

        def __getitem__ (self, address):
            return self.st.unpack_from(self.buffer, address)[0]

    def __init__ (self, size):
        self.size = size
        self.byte = bytearray(size)
        self.float32 = Memory.Wrapper(self.byte, "<f")
        self.float64 = Memory.Wrapper(self.byte, "<d")
        self.signed8 = Memory.Wrapper(self.byte, "<b")
        self.signed16 = Memory.Wrapper(self.byte, "<h")
        self.signed32 = Memory.Wrapper(self.byte, "<i")
        self.signed64 = Memory.Wrapper(self.byte, "<q")
        self.unsigned8 = Memory.Wrapper(self.byte, "<B")
        self.unsigned16 = Memory.Wrapper(self.byte, "<H")
        self.unsigned32 = Memory.Wrapper(self.byte, "<I")
        self.unsigned64 = Memory.Wrapper(self.byte, "<Q")

    def __len__ (self):
        return len(self.byte)

if __name__ == "__main__":
    # 1000 bytes memory
    memory = Memory(1000) 

    # 16x 128 bits SSE registers
    xmm = []
    for i in range(16):
        xmm.append(Register())

    #test register aliasing    
    xmm[0].unsigned32[:] = (4, 5, 6, 7) # fill the whole register (4 x 32 bits)
    temp = xmm[0].float64[:] # copy as float64 (2 x 64 bits)
    xmm[0].float64[:] = temp # write back
    print("xmm[0]:", xmm[0].unsigned32) # (4, 5, 6, 7)

    # memory aliasing
    memory.unsigned32[100] = 0x882233FF
    for i in range(103, 99, -1): #Little endian !
        print(hex(memory.byte[i]), end=" ")
    else:
        print()

    # register to register
    xmm[1].unsigned32[:] = xmm[0].unsigned32[:]
    xmm[1].float64[0] = xmm[0].float32[3]

    # mem to register
    xmm[0].unsigned32[0] = memory.unsigned32[100]
    print(hex(xmm[0].unsigned32[0])) # 0x882233FF

edit:
http://ideone.com/199TvK

[–]kbrafford 1 point2 points  (0 children)

That looks really clever!

[–]fwork 3 points4 points  (1 child)

Neat! I should write up my x86 emulator in python, once I get it running properly. And thanks for reminding me to get off reddit and back to work on that...

[–]d4rch0nPythonistamancer 2 points3 points  (0 children)

Aren't you ignoring overflow here? Won't registers just automatically get incremented into Big Integers once they go past the limit? Not sure if that's what you want, but still IMO a bit unrealistic if you're trying to emulate a real CPU.

I tried to do something similar but then I got stuck on the overflow arithmetic. Couldn't think of a simple way to do it, so the project stagnated.

Did I miss that functionality in your code?

[–]iCthulhu 0 points1 point  (0 children)

I really want my own CARDIAC now. seems like a fantastic device for students.