Writing an emulator in python - implementation questions (for performance)

Carl Banks 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:
>  Hi.
>
>  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.



[snip]
>  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
> globals,

Yes, but I'd suggest to index a numpy array rather than structures.
See below.



[snip]
>  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
print wreg[R1]


[snip]
>  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.


[snip]
>  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
memory:

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
underlying data.)

page = [mem[0:16*1024],
        mem[16*1024:32*1024],
        mem[32*1024:48*1024],
        mem[48*1024:64*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[2][9664] = 33
p = page[3][99]

To swap a page, reassign the slice of main memory,

page[2] = 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.


Carl Banks



More information about the Python-list mailing list