Home Page
Hi All!

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 smile

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:

My diff is here (Its still a bit hacky at the moment. Please bear in mind its early days):

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!

Hi again!

For anyone who is interested, I just thought I'd post my progress so far.

Here is my second attempt...
diff 2nd attempt

In this version:
- I've got rid of all my global variables
- The screen is updated using a callback (CPU usage is much lower now as I'm not thrashing the scanline timer)
- I've finished populating all my cc_**_contended[] tables.
- Fixed a bug with my logic when working out the amount of contention delay to apply based on the tstate. To elaborate - There was a bug with my cm_get_ula_sequence_delay() function that decides if the delay should be 6,5,4,3,2,1,0 or 0 based on tstate. Basically, even line numbers would have the correct sequence, odd line numbers wouldn't. I'm actually quite surprised that even with this bug, the visual results were still ok.
- Implemented a more accurate contention delay table. Whilst scratching my head looking into my cm_get_ula_sequence_delay bug, I discovered that the SinclairFAQWiki page appears to have an improved version of the contended memory script table (That takes into account the IR register for certain opcodes) compared to the table on the WorldOfSpectrum site.

I've now run the same game Nirvana game (SnakeEscape) on mame along side emuzwin and can confirm that cycle for cycle, for all operations done in a complete frame, both emulators synchronize perfectly for 48K and 128k models (So I'm happy that my contended memory tables are correct).

This is however still a small problem (So I will be making a third attempt at this). The problem is that when running the Nirvana engine the first 8 to 16 pixels of every line look corrupt. I'll try to explain why with an example. Running SnakeEscape using the spec128 driver, the first screen pixel on the 8th row happens at tstate 16185 (Calculated by 14631+(228*8)). The first screen pixel on the 9th row happens at tstate 16413 (Calculated by 14631+(228*9)). The colour attribute at both these times should be 0x46 (which is Yellow brush, White Paper). My debugging below shows that opcode 0x31 is being processed between tstates 16181 and 16191 (I.e. around the time the raster starts displaying the pixels for row 8). Once the opcode has finished, the screen updates to the new tstate, and the attribute data 0x46 is used for the first 8 pixels on row 8 (All good so far)...

execute_run - TState=16181, LastOpcodeTstates=11 PC=DDB4
MEM TState=16181 ULA=0 - Addr=DDB4 Val=31 (C:0, N:4)
MEM TState=16185 ULA=6 - Addr=DDB5 Val=3E (C:0, N:3)
MEM TState=16188 ULA=3 - Addr=DDB6 Val=58 (C:0, N:3)
spectrum_UpdateScreenBitmap - y=8 x=0 attr=46 (ink=e pap=8)
spectrum_UpdateScreenBitmap - y=8 x=8 attr=7 (ink=7 pap=0)
execute_run - TState=16191, LastOpcodeTstates=10 PC=DDB7

Now the problem is when we come to the next row of pixels down. Opcode 0x22 is being processed between tstates 16405 and 16430 (I.e. around the time the raster starts displaying the pixels for row 9). Here, Opcode 0x22 ("LD (5820H),7BH") is trying to change the attribute at the start of row 9 from 0x46 to 0x7B. On a real spec128, the attribute 0x46 gets displayed because, at tstate 16413, the opcode hasn't had time to complete its job of writing 0x7B to address 5820H. On mame currently, because I update the screen at the end of the operation, 0x7B get displayed. So even though operation timing maybe perfect, it is not perfectly synchronized to the raster...
execute_run - TState=16405, LastOpcodeTstates=7 PC=DDE5
MEM TState=16405 ULA=0 - Addr=DDE5 Val=22 (C:0, N:4)
MEM TState=16409 ULA=0 - Addr=DDE6 Val=20 (C:0, N:3)
MEM TState=16412 ULA=0 - Addr=DDE7 Val=58 (C:0, N:3)
MEM TState=16415 ULA=4 - Addr=5820 Val=7B (C:4, N:3)
MEM TState=16422 ULA=5 - Addr=5821 Val=07 (C:5, N:3)
spectrum_UpdateScreenBitmap - y=9 x=0 attr=7b (ink=b pap=f)
spectrum_UpdateScreenBitmap - y=9 x=8 attr=7 (ink=7 pap=0)
spectrum_UpdateScreenBitmap - y=9 x=16 attr=7 (ink=7 pap=0)
spectrum_UpdateScreenBitmap - y=9 x=24 attr=7 (ink=7 pap=0)
spectrum_UpdateScreenBitmap - y=9 x=32 attr=7 (ink=7 pap=0)
execute_run - TState=16430, LastOpcodeTstates=25 PC=DDE8

The only way round this is to process the contended memory script (and update the screen display) whilst the opcode is happening, rather than after the opcode has finished. So its onto my third attempt then!

OMG I've written loads again! Thanks for reading and hope its been of interest!

BTW. I can now get past level 9 of SnakeEscape smile
Hi again!

Here is my third attempt (And probably the last as I think everything is finished):
diff 3rd attempt

In this version:
  • Implemented 2 contended memory script tables to support all major ZX Spectrum models (The 'Sinclair' script table for 48K/128K/+2. The 'Amstrad' script table for +2A/+3).
  • Each contended memory script is run during the opcode's execution, rather than after the opcode has finished.
  • Added a macro (MCFG_Z80_CFG_CONTENDED_MEMORY) allowing you to configure how the z80 device should contend memory. If you don't call this macro, then the z80 will not contended memory and will rely on the cc_op[]/cc_ed[] etc.. tables for eating cycles (Thus, will work the old way).
  • The ZX spectrum clones (Timex, ATM, Scorpion, Pentagon - and anything else that inherits the ZX Spectrum machine configuration) all work as they did before (I.e. without contended memory and without using the raster callback).
  • Floating bus support now working.
  • Reduced the amount of times the screen/border bitmaps are updated. Rather than render to the new raster beam position every time tstates are eaten. I realised that it was only necessary to render to the new raster beam position:
    • Just before memory is written to that could effect screen colour attributes
    • Just before memory is written to that could effect border color
    • Always at the end of each scanline (To fix flickering/missing graphics issues that the SCANLINE timer previously fixed (Discussed in the 'firefly' thread)).
  • Tidy up of all my code.

Regarding CPU usage. According to top, without my contented memory changes Amaurote runs at 22% CPU. With my contented memory changes it runs at 26%.

In terms of games/demos, this now means the following:
  • Nirvana (SnakeEscape/DreamWalker/Elstompo/MultiDude/Stormfinch/Sunbucket) MultiColour games look perfect on 48K and 128K Spectrums.
  • Shock Megademo looks perfect on 48K and 128K Spectrums. Note - You must use the '.TAP' file from WOS, don't use (like I did) the '.Z80' dump floating around on the net as it does not display correctly on 48K or 128K spectrums (The dodgy Z80 dump doesn't look right on Emuzwin or Fuse either).
  • Aquaplane - Horizon/Border position is correct (Contended Memory)
  • Darkstar - Hi-Score table border pattern position is correct (Contended Memory)
  • Sidewize - Now fully playable at the correct speed. It used to freeze just as you started the game (Floating Bus).
  • Zynaps - MultiColour text is correct. Speed is now perfect. Previously game play would speed up and slow down (Floating Bus).
  • Arkanoid - Original release, now fully playable. It used to freeze just as you started the game (Floating Bus).
  • Cobra - Original release, now fully playable at the correct speed (Floating Bus).
  • Short Circuit - Original release, Number 5 no longer flickers (Floating Bus).
  • Uridium - MultiColour text is correct.

Here is a selection of screen shots of the games/demos that I've tested:

Thinking about whether or not I'd like to see this committed to mame and I'm really not sure. I've tried hard not to break anything and make modifications that fit in with mame 'as is'. I've done this but at the expense of making the Z80 CPU code more complex which has had a slight detrimental effect on MAME's CPU performance. Can the cost of my modifications be forgiven given based on the titles that now work? - In the end its all down to you mamedev experts to decide. Either way, I totally understand if don't decide to go with it, so no pressure smile

As I said in an earlier post, making/seeing this stuff work is what it has been about for me, take from it what you will and hope it helps smile
just fwiw, this was discussed a bit in the shoutbox

however, from a MAME point of view it's still a bit 'backwards' and relying far too heavily on system specific stuff in a CPU core, rather than improving MAME / the CPU core as a whole

doing it properly will have a much more significant performance impact, so don't worry too much about that

since it will likely require a z80 rewrite, and the ability to slow down the CPU on the fly etc. a proper solution is likely some way off, so personally I'd quite like to see something like this as a temporary measure, but IMHO to stand any chance of being accepted you'll probably need to make some C++ derived 'specz80' CPU type with and use virtual functions + overrides to keep all the speccy specific code as far out of the actual Z80 core as possible.

that said, there's a fair chance the devs just aren't going to accept this kind of solution at all, that would not be my say.
I'd accept it with the forked Z80, and OG sounded somewhat positive about that too. I guess the question is if it would bother Vas.
I guess I'd tolerate a forked Z80 in the short term provided it isn't completely gross. A more granular Z80 is substantially more complicated than some other CPUs.
Hi all & thanks for the responses!

I'm a bit confused by the term 'forking the Z80'. Do you mean create a new repository branch? Or do you mean create a copy of the Z80.cpp (Calling it, say, specz80.cpp, where I'd put the contended memory modifications) and have the zx spectrum driver family use that? Or do you mean (With regards to what Haze mentioned) deriving a specz80_device class from the z80_device class?
Derive specz80_device from the main Z80 and put the Spectrum mods in there (and of course have the Spectrum driver use it).
Ok cool thanks I understand now smile I'll give it my best shot, back soon!
Just wondering, would these changes not improve compatibility when applied to systems like the Amstrad CPC?
Hell no. This is a set of extremely specialized workarounds for how the Spectrum ULA works. The Amstrad's graphics work more like PC CGA.
Hi again!

Here is my 4th attempt:
diff 4th attempt

specz80_device is now derived from the main z80_device class. I must admit that with specz80_device containing all my mods things do seem to look a lot neater. Hope it is what you had in mind smile
It's definitely better. Would it be possible to actually put the specz80_device into a separate file? It has a lot of long comments, which is good for explaining how it works but kind of a pain for people just looking at the Z80.
No worries, was thinking the same thing, back soon smile
nice progress.
there is one more good test case - Qarx game http://www.worldofspectrum.org/infoseekid.cgi?id=0003960
it's high score screen have nice text scroll on border and multicolor effects
MetalliC - I've tried Qarx and everything looks good when viewing the hi-scores except for some strange artifacts at very top left of the border (See Qarx screenshot (Blue arrow pointing to the problem)). I'm confused as to why I am seeing these artifacts as I thought I had the border timings sorted. I've just gone into my loft and dug out my old 48K+ and +2 as I’d like to see how various games/demos/ULATestPrograms run on original hardware in comparison to mame (I’ve found screenshots of other Speccy Emulators running ULA test software on the web, but they all seem to give slightly conflicting results, so I’m not sure what I should trust). I shall post my screenshots and findings smile
Wow, after 20years+ they worked first time! I ran many different Games/Demos/TestTools on both machines. Here are my screenshots:

ZX 48+ testing

ZX 128+2 testing

- On my 48k when running DarkStar, I repeatedly toggled between the menu and hiscore table. The border pattern was mostly in the same place where the yellow border section would begin/align just after the 'k' of the "Dark Star" title. Except for about 1 in 10 times where The yellow border section would begin/align just after the first 'a' of the "Dark Star" title. On my 128k, although the border pattern is naturally skewed, it is always in the same place regardless of how many times I toggle between menu and hiscores. Screenshots of all these scenarios can be found in the albums above.

- Qarx hiscore screen looks perfect on my 48k (No strange artifacts at the very left side of the border). However, the border width of my actual 48k is noticeably thinner than mame's. Just a hunch at the moment, but I suspect that mame is showing more (too much) border than was actually possible back in the day and that is why I am seeing the strange artifacts. Searching on the web for other peoples screenshots of actual spectrums running and their border width looks the same as mine (So I do not think its just my 48k that has an unusually thin border). Also, it appears mame shows more border than the other spectrum emulators I've tried (Emuzwin/fuse), which is interesting.

- The Contended memory / floating bus test problems I found here Speccy tests
btime.tap (border time test) and stime.tap (screen time test) draw a small red line either in the border section or the screen section of the display. Where it is drawn depends on when the opcode writes (that set the red border/screen colour on and then off) occur. Using the 'q' and 'a' keys, you are able to adjust the timing of when the opcode writes occur, thus effect where the red line appears. I took plenty of screenshots when running btime and stime for various tstates.

Next step is to make sure these test results tally up with my mame mods smile
Those border and screen time tests look interesting. Thanks for the pictures
After fixing a few bugs (IO Contention wasn't working, and my floating bus timing and border positioning were a few tstates out) all the tests when running mame give the same results as my actual spectrums.

For completeness, here are some screenshots of the tests running on mame:
mame spec48 tests

mame spec128 tests

I'll post my latest diff shortly smile
I'd just did a quick 48k border comparison that gives some idea how much larger mame's border is compared by my actual 48k+. It does imply those Qarx border artifacts would just never been seen on actual hardware. Its not really a massive problem as you can use mame's slider controls (Screen Horiz/Vert stretch/Position) to achieve the desired look.

spec48k border comparison

One thing with border size is that most TVs overscan and don't show the entire border. On a decent composite monitor like a Commodore 1084/1084S you can adjust it so the entire visible area shows.
Yes indeed. Guessing that back in the day, most people programmed their Spectrums using TVs so they would never know/care about the extremes of the border. I guess I'm just trying to work out if this Qarx artifact thing is due to a bug in something I've done, or is it due to Mame exposing border areas that typically weren't visible back in the day. After all my border tests and comparisons I've pretty much convinced myself that its the latter so that's cool smile
RB - Splitting up my mods into another file seems a bit more tricky than I anticipated :p My aim was to put all my mods in specz80.h and specz80.cpp. In doing so I had to create another file (I called it z80_common.h) to move some defines from z80.cpp so that specz80.cpp could see them (At first I put all the defines into z80.h but that meant some other drivers failed to compile due to conflicting defines). Also, some of my specz80.cpp member functions failed to compile because they called z80_device member functions that were defined as 'inline' in z80.cpp. I got around this by removing the inline keyword from various z80_device member functions (but I'm not sure if this was the best approach?). Anyways, here is what I've done so far (and it all compiles/works) so see what you think, I don't mind going back to the drawing board again smile

attempt take 5

Also, I just want mention that I don't think I've quite nailed my implementation for the +2a/+3 models. Specifically, there is a demo called 'Mescaline' that MetalliC suggested I try a while back. There are a few builds of this demo (one for the 128K/+2 models, and one for the +2a/+3 models). The 128K/+2 build looks good on mame, but the +2a/+3 build doesn't. So I probably need to take a second look at this.
Can you please create a branch on github for this so people can see it easily and comment on stuff in context?
No worries, back soon smile
I think I've found the problem on the +2a/+3. I believe the specpls3 interrupt is firing later than it should. This causes my tstate counter drift out-of-sync for each frame that goes by (Which confuses the video drawing routines as they rely on an accurate tstate counter).
The spectrum_128 config accurately sets up the cpu clock speed and interrupt time like this:
MCFG_DEVICE_ADD("maincpu", Z80, X1_128_SINCLAIR / 5)
MCFG_SCREEN_RAW_PARAMS(X1_128_SINCLAIR / 2.5f, 456, 0, 352, 311, 0, 296)

The spectrum_plus3 config inherits from the spectrum_128 config and then modifies the interrupt time:

I think the spectrum_plus3 config should leave the interrupt time as is. When I remove the line "MCFG_SCREEN_REFRESH_RATE(50.01)" my contended memory mods work fine on the +2a/+3 (and the Mescaline Synesthesia demo looks the same as it does when running on my actual +2).

I've now committed all my multicolour mods and this fix to my github branch here:-

Just wanted to add some waffle about the Mescaline Synesthesia demo:

To display more colours than is possible on the Spectrum, this demo rapidly swaps screens each frame in an attempt to mix colours (I.e. a pixel might be yellow on one screen, red on another, thus giving the impression of an orange pixel). The I've run the demo (original and 'final cut' version) on my actual +2, and can confirm the effect does not work amazingly. There is quite a bit of flicker during the raster sections and during the still images. The still images also appear to have lines on them, I've added a screencaps into my ZX 128+2 testing album. I've found this youtube clip of the demo running "perfectly". If you read the comments it appears the demo only looks perfect if you run it on the Pentagon. Note, this website says "For Pentagon machines, a hardware modification is available which directly combines the two alternate screen areas into the video signal, thus eliminating the flicker associated with this method". I'm pretty sure after reading some of the youtube comments, the demo was originally written for the Pentagon and it relied on this hardware modification.

Anyways, I've now tested the Mescaline Synesthesia on Mame running the spec128 and specpls3 driver and it looks just the same as when it runs on my actual +2. So that is accurate/good enough for me smile
Have you tried running that demo on a CRT, just in case that it may blend the odd and even frames better?
Unfortunately, I don't have a CRT to try it out (I'm currently using a 42" plasma (Aerial input)). I did read a comments here http://www.pouet.net/prod.php?which=54207 suggesting it is still not perfect even with the CRT (But I have a tendency not to trust everything I read on the web).
Hi There! I've tried the Mescaline Synesthesia demo when connecting my ZX Spectrum +2 to a old 14" CRT TV. I observed same amount of flicker during the raster sections and during the still images. The still images also appear to have lines on them. I took some pics and have added them to my ZX 128+2 Testing album.

In summary, I've now run this demo on the following setups:
- My Contended Memory modified version of MAME with an 27" LCD screen.
- An actual ZX spectrum 128k+2 with a 42" Plasma screen screen (Aerial input).
- An actual ZX spectrum 128k+2 with a 14" CRT TV (Aerial input).
and the Mescaline Synesthesia demo runs/looks the same regardless of the setup. So from MAME's point of view, I think it is emulating this demo perfectly (and I think this demo, despite being ported from the Pentagon to the ZX Spectrum, will never look as good on any Spectrum as it does on the Pentagon). Case closed smile

Also, just wondered if anyone has had a chance to try out my contended memory modifications I committed to my github branch a while back?

Many thanks! smile
I'd like to do some experiments with that memory contention stuff, but I have problems parsing th thread. So let's ask the experts.

To test if a memory contention implementation works for the spectrum, what should I use? What driver, what software, what commands to type? And then, what should it look like?

This is the best technical description I've seen: https://faqwiki.zxnet.co.uk/wiki/Contended_memory
Hi OG! Unfortunately, there isn't an all singing all dancing memory contention self test that I know of. It very much a "Run this Game/Demo/TestTool and check that the things you see on screen when running a real Spectrum are the same as when you are running a spectrum emulator" affair. Also, just having a perfect memory contention implementation does not mean all multi colour games & demos will look perfect. You need perfect ULA emulation too (So that the screen raster is perfectly sychronised with the Z80).

There are a number of ULA test tools that give an insight into the ULA timings of the Spectrum model that it is running on. I've ran all the tests I could find on my actual 48K and 128K+2 (I've posted links to galleries containing screenshots of my results), and have checked these results match perfectly with mame.
The tests can be found here:

If you wanted a more enjoyable place to start, try loading the Nirvana+Engine demo on an 48k, 128K or +2A/+3. The tap can be found here:

If contended memory and ULA timings are correct, then the multi colour graphics will display correctly (I.e. When the demo runs you should see more than 2 colours occuyping an 8x8 pixel block, without any flicker or colour clash) regardless of what spectrum model you run it on.

Hope this helps smile
I've managed to get my hands on a Spectrum +3 from an old friend of mine (Just had to re-solder a dry joint on the audio/tape socket and it seems to be able to load stuff now). Currently, the +2A/+3 ULA timing settings used for my mame implementation are based on the results that the Fuse emulator gives. Now I have an actual +3 I can double check if my +2A/+3 settings are correct. Soon I'll create another photo album containing screenshots of the +3 running floatspy, btime, stime, ulatest3 (Like I did for my Spectrum 48K and +2) smile
Hi! Here are the screenshots of various tests tools running on my actual Spectrum +3. I can confirm that my mame implementation (when running the 'specpls3' model) displays exactly the same test results as the actual Spectrum +3, *except* for the "Test: Contended NOP" test. According to my actual Spectrum +3, during the Contended NOP test, TState 14490 takes '5' cycles. This differs from my mame implementation (and the Fuse emulator) which both indicate this as being '4' cycles. Which is quite interesting...

ZX 128+3 testing (Actual)

Mame/Fuse running Contended NOP test

© Forums