Scaling hardware?

Ask anything your want about Mega/SegaCD programming.

Moderator: Mask of Destiny

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Tue May 08, 2012 4:28 pm

I would need to think about a neat way of emulating this behavior. For the record, I found back the original thread on Eidolon's Inn where you and Steve talked about this:

http://www.eidolons-inn.net/tiki-view_f ... entId=7404


Completely off-topic but I got some hard time emulating Snatcher. It hangs when I press start during the intro (the first displayed text message with blue background) and only after a very long time (something like 7000 frames or so when it should be quite immediate) the scene with Konami heardquarters is displayed, then it hangs forever on a black screen.
Have you (Stef or TascoDeluxe or anyone who worked on CD emulation) ever faced this issue ? I have trouble figuring what is causing this long pause between scenes and the lockup: MAIN-CPU seems to be running an infinite loop and only wakeup on VINT to refresh the screen with always the same setup while SUB-CPU main loop is only interrupted by CDD interrupt and only send TOC READ commands...


From what I've read on various forums, it seems like this game is VERY sensitive to the TOC so i faked audio tracks and got the intro running further (it indeed doesn't go further than the blue screen text if you only load an ISO without audio tracks) but as soon as I press START to skip the intro, same problems arise.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Tue May 08, 2012 9:13 pm

It's true this game is sensitive to TOC, you have to be sure your image is 100% correct.
I don't remember specific problem about this game (except TOC mistake which was making game hanging). I got it quickly working in Gens as far i remember... How is your general Sega CD compatibility except for this game ? Also how is your synchronization between main and sub 68000 cpu ? I remember i experienced many hanging and/or very long loading before adding the perfect synchro option (which basically reduce a lot the 68k timeslice execution).
Last edited by Stef on Wed May 09, 2012 11:19 am, edited 1 time in total.

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Wed May 09, 2012 11:08 am

I did not tested a lot of games yet, infact i'm trying to fix each game i try before going to the next one: so far, Sonic, Shining Force, Wonderdog, Double Switch & Popful Mail work without bugs. For the latter, I indeed had to improve CPU sync but didn't do it the same way as in Gens: i only resync CPUs when main-cpu is writing any of the comm register and sub-cpu did not read yet the previously written value. I manage this through a set of flags, which could eventually be extended to all comm registers, including ff8003/a12003, and it seems to work fine so far.

Anyway, this .iso starts fine under Gens or Kega, without any audio tracks and even without perfect synchro being enabled, so i suspect this is something else which i don't emulate correctly.

My problem is I cannot figure what sub-cpu is waiting for to display the first cutscene (konami headquarter building) when you press start during intro which would explain why it is taking so long compared to what it should be, then what it is waiting for to start the game (act 1 - snatch screen).

I can see a few play/pause commands and associated block transfer to prg-gram when i press start then nothing for a (long) while, then similar play/transfer/pause sequences just before the first cutscene and finally no commands anymore, only usual toc reading.

I also noticed that before the first and last play commands, the game always sends an undocumented 0x0a command with some unknown parameters. Not sure what it is for but i used gens as reference for this one to know how to fill CDD status registers.

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Wed May 09, 2012 11:25 am

Indeed i tested and this game does not even require perfect sync mode.
Maybe it comes from a more general problem with SCD gate array emulation, i got many troubles in getting good emulation of all the DMNA / RET stuff logic.
Check also your CDD/CDC interrupt code...
And i think you should try to test with others games... Sometime you can get stuck with a game where others games will give you better opportunities to fix it. Unfortunately i cannot give you better advices...

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Wed May 09, 2012 11:11 pm

I fixed it and it was indeed a very stupid bug: I forgot to handle word writes to Timer and Interrupt Mask registers because I thought only byte access were used for these ones :oops:

Thanks for the help anyway

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Thu May 10, 2012 7:55 am

Nice to hear you fixed it :) At this stage a lot of stupid problems can remain and you can lost many time in trying to trace them, it's why you should use severals games and fix easier first =)

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Sat May 19, 2012 2:07 pm

Stef wrote:At this stage a lot of stupid problems can remain and you can lost many time in trying to trace them, it's why you should use severals games and fix easier first =)
You are right, and I got now to the point where all the CD images I have (~35) seem to work fine, except one, Time Gal.

I know this game requires better synchronization between the two CPUs and I indeed improved my first implementation for this as it was required by a handful of other games (namely Silpheed, Lunar 1 & 2, Dracula Unleashed, Night Trap, Dark Wizard, etc...), but it does not seem to be the problem here, as, from logging register access, no CPU seem to miss any request/ack from the other . The problem I got is that SUB-CPU is continuously reading same ten sectors (LBA 165 to 173) and issuing the same PLAY command at 00:04:15 again & again, while MAIN-CPU is apparently waiting for SUB-CPU to finish its task, which off course never occurs . I have unfortunately no idea again why SUB-CPU does not read further. Could it be related to drive access time (which I do not emulate yet) ?

EDIT: hmm, seems like Cobra Command & Road Avenger have similar issue (lock on black screen after sonic boot screen and continuously read same sectors again & again)

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Sun May 20, 2012 11:07 am

I got a lot of "replay" bug where the sub CPU was reading again and again the same sectors. That means an expected action did not happened so the BIOS think the read was incorrect and read again the same sectors.
What do you mean by not emulating "drive access time" ? Is it only seek time ? or you speak about the 1/75 s needed to read a sector ? The last one is very important and i guess you're already emulating it otherwise you'll experience many problems in FMV. About seek time, i'm not sure that is really important, you could use a minimum seek time though...

As far i remember Time Gal is one of these games which required better sync butr nothing else special. I tried to implement an automatic CPU sync but it never got it to work perfectly. Mortal Kombat was very picky and i think, poorly programmed at some point :-/ I don't know how you implemented your auto sync but there are many ways of having CPU inter dependencies (not only in the gate array but all shared memory/port).
Did you try a "hard perfect synchro" by reducing execution timeslice to 8 (main-cpu) / 12 (sub-cpu) so you execute almost instruction by instruction ?

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Sun May 20, 2012 11:42 am

Stef wrote:I got a lot of "replay" bug where the sub CPU was reading again and again the same sectors. That means an expected action did not happened so the BIOS think the read was incorrect and read again the same sectors.
The strange thing I noticed by analyzing my logs more deeply is that it seems SUB-CPU is out of sync with the CDD/CDC interrupts: it attempts to read two sectors from CDC buffer in a row (from CDC host register), before the second sector had any chance to be decoded/written to this CDC buffer address, so it ends up reading a former sector (since the buffer acts as a ring buffer). I tried to increase the size of CDC buffer from 16KB to 64KB (as it is apparently done in Gens/Picodrive) but it did not helped.

I think I have to investigate why it is doing this.
What do you mean by not emulating "drive access time" ? Is it only seek time ? or you speak about the 1/75 s needed to read a sector ? The last one is very important and i guess you're already emulating it otherwise you'll experience many problems in FMV. About seek time, i'm not sure that is really important, you could use a minimum seek time though...
no, 75hz interrupts are accurately implemented, i meant the seek/play time latency, i.e some delay between the seek/play commands and the drive actually reading (resp. seeking) data from (resp. to) the wanted sector.

but it did not helped, i just verified by adding similar delay as in Picodrive and it did not changed anything
As far i remember Time Gal is one of these games which required better sync but nothing else special. I tried to implement an automatic CPU sync but it never got it to work perfectly. Mortal Kombat was very picky and i think, poorly programmed at some point :-/ I don't know how you implemented your auto sync but there are many ways of having CPU inter dependencies (not only in the gate array but all shared memory/port).
what I do is keeping both CPU in sync (running up to current opposed CPU cycle timestamp) when a communication register ($03; $0e-$2f) is written by any of the 2 CPU

it's also bound to CPU register polling detection in fact, since MAIN-CPU is always "beyond" compared to SUB-CPU (I run it first on the line): I stop MAIN-CPU when it reads the same register twice consecutively (with same PC and reasonable cycle count differences) and I restart it when SUB-CPU writes said register. At this time, MAIN-CPU cycle count is "rewinded" to be in sync with SUB-CPU, so that next CPU write to communication registers can keep MAIN-CPU in sync (I don't know if I'm very clear).
The same is done on the other side.

Off course, it does not cover all possible cases (for example, one CPU reading a register only once and expecting the other CPU to have updated the value a few cycles ago) or shared Word-RAM access but seemed enough to get a vast majority of sync-sensitive games running fine. Though I just verified and Mortal Kombat hangs as well on startup so I guess i will need to improve this once again :?
Did you try a "hard perfect synchro" by reducing execution timeslice to 8 (main-cpu) / 12 (sub-cpu) so you execute almost instruction by instruction ?
not yet, in fact I' m trying my best to avoid ending up with this solution :lol:

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Sun May 20, 2012 4:54 pm

Eke wrote: The strange thing I noticed by analyzing my logs more deeply is that it seems SUB-CPU is out of sync with the CDD/CDC interrupts: it attempts to read two sectors from CDC buffer in a row (from CDC host register), before the second sector had any chance to be decoded/written to this CDC buffer address, so it ends up reading a former sector (since the buffer acts as a ring buffer). I tried to increase the size of CDC buffer from 16KB to 64KB (as it is apparently done in Gens/Picodrive) but it did not helped.
I also experienced that strange behavior but that may due to weird "border" effect of something else. I'm also certain your CPU sync is the problem.
what I do is keeping both CPU in sync (running up to current opposed CPU cycle timestamp) when a communication register ($03; $0e-$2f) is written by any of the 2 CPU
Relying only of those communication registers is definitely not sufficient, there are so much ways for interacting between both CPU...
it's also bound to CPU register polling detection in fact, since MAIN-CPU is always "beyond" compared to SUB-CPU (I run it first on the line): I stop MAIN-CPU when it reads the same register twice consecutively (with same PC and reasonable cycle count differences) and I restart it when SUB-CPU writes said register. At this time, MAIN-CPU cycle count is "rewinded" to be in sync with SUB-CPU, so that next CPU write to communication registers can keep MAIN-CPU in sync (I don't know if I'm very clear).
The same is done on the other side.
Yeah i understand what you means, it looks similar to what i did in my Gens rewrite for "CPU synchronization" (as well for Z80) except i was doing that for all shared space (even plain memory).
How do you handle the rewind stuff ? are you saving CPU context at some point or reverse it with sort of "inverse execution" ??
Off course, it does not cover all possible cases (for example, one CPU reading a register only once and expecting the other CPU to have updated the value a few cycles ago) or shared Word-RAM access but seemed enough to get a vast majority of sync-sensitive games running fine. Though I just verified and Mortal Kombat hangs as well on startup so I guess i will need to improve this once again :?
Yeah, you really need to sync CPU on all shared areas, some games uses their own concurrencies methods in word ram for instance so if you don't synchronize on word ram access these games won't work...
Did you try a "hard perfect synchro" by reducing execution timeslice to 8 (main-cpu) / 12 (sub-cpu) so you execute almost instruction by instruction ?
not yet, in fact I' m trying my best to avoid ending up with this solution :lol:
Unfortunately at some point it will be the easiest way of getting all Sega CD games working... You can actually avoid that but your synchronization code will be very complex and not necessary faster than using small timeslice with fast CPU context switch :)

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Sun May 20, 2012 5:59 pm

How do you handle the rewind stuff ? are you saving CPU context at some point or reverse it with sort of "inverse execution" ??
I simply adjust the cycle counter of the stopped CPU to match other CPU cycle count, something like that: m68k.cycles = (s68k.cycles * MCLOCKS_PER_LINE) / SCD_CLOCKS_PER_LINE)

When a CPU is stopped, its cycle counter is also forced to the end of the line so it does not execute instructions anymore until next event, rewinding it is just a matter of re-adjusting its cycle counter to match "real" stopping time, i.e somewhere between the time it was stopped and the end of line, giving him a chance to execute instructions before end of line.

And I don't use context switch but two static CPU cores sharing common code through static #include , not sure which method is faster but it was easier than rewriting the CPU core to use cpu context pointer...
Stef wrote: Yeah, you really need to sync CPU on all shared areas, some games uses their own concurrencies methods in word ram for instance so if you don't synchronize on word ram access these games won't work...
but wouldn't shared Word-RAM access be handled through register A1003/FF8003 sync anyway ? I mean, Word-RAM is not really "shared" as it must be switched for CPU to communicate together through it, right ?



Unfortunately at some point it will be the easiest way of getting all Sega CD games working... You can actually avoid that but your synchronization code will be very complex and not necessary faster than using small timeslice with fast CPU context switch :)
well, I tried bruteforce and executing both CPU opcode by opcode... but it did not helped (and seems like SUB-CPU BIOS does not even send the PLAY command I initially saw)

BUT, while trying to implement this, I made some mistake and somehow managed to get the game going further (and do not send the same PLAY command again & again but instead read all needed sectors) by having both CPU running much more cycles than normally expected. Off course, it crashes later because cycle counter are going out of control but it indeed seems to indicate the issue is CPU timings, like something happening too early or too late.

For the record, the difference between the two logs is that, in the correct one, SUB-CPU starts reading CDC host data after the 3rd block has been written then continue reading block as CDC is decoding & writing them (which is BIOS normal way of doing), while in my initial log, only 8 sectors are written then CDC buffer write is disabled and only after that SUB-CPU starts reading 5 blocks from the 4th (?) block...

Hum, maybe this should be moved to its own thread, this is going way off-topic :oops:

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Sun May 20, 2012 9:18 pm

but wouldn't shared Word-RAM access be handled through register A1003/FF8003 sync anyway ? I mean, Word-RAM is not really "shared" as it must be switched for CPU to communicate together through it, right ?
To be honest i don't really remember all the gate array and memory share stuff on mega CD, after reading back some docs it seems you cannot access any ram bank (word ram as prg ram) at same time by both cpu (which make sense) but i remember i had really some bad time in trying to do cpu sync only on gate array register access...
well, I tried bruteforce and executing both CPU opcode by opcode... but it did not helped (and seems like SUB-CPU BIOS does not even send the PLAY command I initially saw)

BUT, while trying to implement this, I made some mistake and somehow managed to get the game going further (and do not send the same PLAY command again & again but instead read all needed sectors) by having both CPU running much more cycles than normally expected. Off course, it crashes later because cycle counter are going out of control but it indeed seems to indicate the issue is CPU timings, like something happening too early or too late.
...
Well, if your execution flow changes depending of your cycle execution size then that definitely means it is related to synchro between main and sub cpu ;)
Did you test your brute force method (opcode per opcode execution) with others games to see if it was correct ?
Also you said yuo were synching on communication register, do you do it on DMNA / RET and others shared registers ?

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Mon May 21, 2012 6:58 am

Stef wrote: Did you test your brute force method (opcode per opcode execution) with others games to see if it was correct ?
Actually, I refined my opcode-by-opcode implementation and it got past the black screen now in Time Gal (but the intro is completely garbled). Same thing for Road Avenger & Cobra Command ( those 3 games seem to use the same start program). If I look into my log, I can see that the first PLAY command is still in error (CDC host read starts after 8 sectors being written to buffer RAM and write to buffer is disabled) but the second repeated one is fine (all needed sectors are read until the end). However, sync between the two CPU appears to be exactly the same in both cases, with MAIN-CPU always being idled when PLAY command are being sent, waiting for SUB-CPU to write his communication flags register in both cases. Even code flow appears to be exactly the same up to this second PLAY command: the only difference is that a CDC transfer is started right after the second PLAY command in the good case, while it is delayed as before in the bad case.


I think I need to figure how CDC transfer are performed (by the BIOS ? game program ?) and what trigger them (they seem to be related to INT2 from MAIN-CPU), as it does not seem to be related to shared registers...
Stef wrote: Also you said yuo were synching on communication register, do you do it on DMNA / RET and others shared registers ?
yes, I sync on the memory mode register as well as communication flags/command/status word registers

Stef
Very interested
Posts: 3131
Joined: Thu Nov 30, 2006 9:46 pm
Location: France - Sevres
Contact:

Post by Stef » Mon May 21, 2012 8:33 am

Eke wrote: Actually, I refined my opcode-by-opcode implementation and it got past the black screen now in Time Gal (but the intro is completely garbled). Same thing for Road Avenger & Cobra Command ( those 3 games seem to use the same start program).
If I look into my log, I can see that the first PLAY command is still in error (CDC host read starts after 8 sectors being written to buffer RAM and write to buffer is disabled) but the second repeated one is fine (all needed sectors are read until the end).
Ok so now you can start to debug what is wrong there :p
I'm not sure but the intro you're speaking about is the "team wolf" logo or something like that ?
Maybe you have some issues with your CDC controller code, timing interrupt or just status flags.
However, sync between the two CPU appears to be exactly the same in both cases, with MAIN-CPU always being idled when PLAY command are being sent, waiting for SUB-CPU to write his communication flags register in both cases. Even code flow appears to be exactly the same up to this second PLAY command: the only difference is that a CDC transfer is started right after the second PLAY command in the good case, while it is delayed as before in the bad case.
Honestly i made severals time the same observation : the flow execution looks exactly the same in both case, but one fails without any visible reason where the other continue normal execution... in fact you do not see exactly all and sometime the flow execution can change for a very minor difference (CDC register read which return the expected value earlier because of timing change).
You cannot rely only on your observations on execution flow, you miss some stuff. The concurrent execution make debugging a lot more difficult.
I think I need to figure how CDC transfer are performed (by the BIOS ? game program ?) and what trigger them (they seem to be related to INT2 from MAIN-CPU), as it does not seem to be related to shared registers...
I don't remember how this game what doing that part but i'm confident now you're going further you will quickly find what is the problem :)
Maybe a minor mistake in CDC controller emulation.

Eke
Very interested
Posts: 885
Joined: Wed Feb 28, 2007 2:57 pm
Contact:

Post by Eke » Mon May 21, 2012 9:38 pm

Well, I was wrong: in fact, running opcode by opcode does not make any good, the only thing that somehow made it go past the black screen on startup was to significantly increase either the number of SUB-CPU cycles executed per line (I forgot I still had this slightly modified in my previous test... oops) or the CDD/CDC interrupts frequency.

:?

And in fact, now that I think about, increasing the number of SUB-CPU cycles executed per line would also make CDC/CDD interrupts triggered faster since they are tied together in my implementation (75 interrupts /sec => 1 interrupt each 1250000/75 = 500000/3 SUB-CPU cycles so what I do is incrementing a CDD cycle counter by 3*SCD_CYCLES_PER_LINE on each line and when this counter goes past 500000, process CDD/CDC stuff including interrupts then decrement the counter by 500000).

I have the feeling it's something related to IRQ2 vs IRQ4/5 sync but still can't figure exact issue.

Post Reply