There has been lots of interesting discussions about contended memory on this site and unfortunately it has caught my interest.
With MAME in its current state, I ran the Nirvana game engine and the display looked corrupt (as expected because this game engine relies on contended memory in order for the sprites to display correctly). Then I hacked around with the z80.cpp execute_run() function, periodically eating extra tstates every 100 tstates that went by (As that is kind of what happens when you have contended memory). When I ran the Nirvana engine again, I could see a few multi-color effects trying to do-their-thing (but only as the sprites went past a certain area of the screen). After that I was hooked!
Then I decided to have a go at implementing a complete contended memory solution based on the technical information from the World of Spectrum (WOS) website. I focused mainly on 'getting something working' rather than worrying about how my changes would effect other drivers/clones in MAME (To be honest, I didn't think I would get very far so I wasn't thinking ahead). Anyway, as time went on I cleaned up my hack more and more to the point where I now have things working well. I'm not sure if my implementation is a step in the right direction for mame, or just a step in a direction. Either way, its been an interesting learning experience and if you think that with a few/many tweaks it could find its way into mame then that's cool
About my implementation:
- The WOS information indicates which opcode groups are associated with which contended memory 'scripts' (For example, "LD A,I" and "LD I,A" both use the "pc:4,pc+1:5" script). In a similar way z80.cpp has tables containing relationships between opcodes and their uncontended cycle delays (cc_op, cc_cb etc.), I added tables containing relationships between opcodes and their script (There are 37 scripts which I defined CM01 to CM37. These are used in tables cm_op_contended, cm_cb_contended etc.).
- For each opcode processed, I keep a history of all the addresses that are read/written to.
- After the opcode has finished, I work out what script the opcode uses, run the script (Which processes all the read/write history), and adjust the tstate counter accordingly.
- Currently (For simplicity), the tstate counter adjustment is always carried out after the opcode has finished processing (I.e. After all the address read/writes for that opcode have been carried out). This could be improved so that the tstate counter is adjusted on each read/write. This would require a bit of a code change but I think it should be possible (Pre-determining the contended memory script prior to the opcode being run, and running parts of the script on each read/write). I might have a go at doing this next.
- I've changed spectrum_UpdateScreenBitmap() so the raster beam's pixel position is determined solely on the tstate counter. This function needs be called more regularly than it currently does (Currently, it is called 'once every 224 cycles' which looks a bit 'unstable'. I found 'once every 16 cycles' to look better but this does have a slight detrimental effect on performance).
- Currently only works with the "spectrum" and "spec128" driver.
Here are some screenshots of it working:screenshots
My diff is here (Its still a bit hacky at the moment. Please bear in mind its early days):diff
Just for completeness, I thought I'd mention that the ZX spectrum game engines capable of producing games that avoid colour clash (I've heard these engines described as providing 'Rainbow Graphics', 'Multicolor Graphics' and 'BiColor Graphics') are:-
ZXodus (Released in 2011)
BiFrost (Released in 2012)
Nirvana (Released in 2013)
These engines can be downloaded from various places on the web in '.TAP' format. The engines themselves, after you've loaded the TAP, run a little demo (So you can see the engine working without writing any code). I tried Nirvana and BiFrost out. I also played a game that uses the Nirvana engine called "SnakeShake" which is a pretty cool puzzle game but I am stuck on level 9.
OMG I've written absolutely loads... Well done if you've got this far and not fell asleep!
Thanks for reading!