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