Writing an emulator in python - implementation questions (for performance)
pavlovevidence at gmail.com
Thu Nov 12 14:58:45 CET 2009
On Nov 12, 4:35 am, Santiago Romero <srom... at gmail.com> wrote:
> I'm trying to port (just for fun), my old Sinclair Spectrum emulator,
> ASpectrum, from C to Python + pygame.
The answer to your question is, "Use numpy". More details below.
> Should I start writing all the code with a Z80CPU object and if
> performance is low, just remove the "object" layer and declare it as
Yes, but I'd suggest to index a numpy array rather than structures.
> How can I implement this in Python, I mean, define a 16 byte variable
> so that high and low bytes can be accessed separately and changing W,
> H or L affects the entire variable? I would like to avoid doing BIT
> masks to get or change HIGH or LOW parts of a variable and let the
> compiled code to do it by itself.
You can do clever memory slicing like this with numpy. For instance:
breg = numpy.zeros((16,),numpy.uint8)
wreg = numpy.ndarray((8,),numpy.uint16,breg)
This causes breg and wreg to share the same 16 bytes of memory. You
can define constants to access specific registers:
R1L = 1
R1H = 2
R1 = 1
breg[R1H] = 2
> Is python's array module the best (and fastest) implementation to
> "emulate" the memory?
I'd use numpy for this as well. (I doubt the Z80 had a 16-bit bus,
but if it did you could use the same memory sharing trick I showed you
with the registers to simulate word reads and writes.)
Note that, when performing operations on single values, neither numpy
nor array module are necessarily a lot faster than Python lists, might
even be slower. But they use a LOT less memory, which is important
for largish arrays.
> The Sinclair Spectrum 8 bit computer can address 64KB of memory and
> that memory is based on 16KB pages (so it can see 4 pages
> simultaneously, where page 0 is always ROM). Programs can change
> "pages" to point to aditional 16KB pages in 128KB memory models.
> I don't know how to emulate paging in python...
numpy again. This would mean you'd have to fiddle with addresses a
bit, but that shouldn't be too big a deal. Create the physical
mem = numpy.zeros((128*1024,),numpy.uint8)
Then create the pages. (This is a regular Python list containing
numpy slices. numpy slices share memory so there is no copying of
page = [mem[0:16*1024],
To access the byte at address 42432, you'd have use bit operations to
get a page number and index (2 and 9664 in this case), then you can
access the memory like this:
page = 33
p = page
To swap a page, reassign the slice of main memory,
page = mem[96*1024:112*1024]
Now, accessing address 42432 will access a byte from a different page.
If you don't want to fiddle with indirect pages and would just rather
copy memory around when a page swap occurs, you can do that, too.
(Assigning to a slice copies the data rather than shares.) I don't
know if it's as fast as memset but it should be pretty quick.
Hope these brief suggestions help. If you don't want third party
libraries, then numpy will be of no use. But I guess if you're using
pygame third party modules are ok. So get numpy, it'll make things a
lot easier. It can be a bit daunting to learn, though.
More information about the Python-list