Questions on writing a new Mega CD emulator

Ask anything your want about Mega/SegaCD programming.

Moderator: Mask of Destiny

TascoDLX
Very interested
Posts: 262
Joined: Tue Feb 06, 2007 8:18 pm

Re: Questions on writing a new Mega CD emulator

Post by TascoDLX » Sun Apr 28, 2019 8:07 am

byuu wrote:
Fri Apr 26, 2019 1:09 pm
So it turns out the Z80 is driving the YM2612, which is where my audio is missing. It hits this routine and dies:

https://github.com/DarkMorford/scd-bios ... g0.z#L1126
It seems to be a bug in the Z80 code that (remarkably) doesn't prevent the program from working properly, but you need to support the mirroring of ZRAM at 0x2000. Don't expect code trace to make much sense though. It's just... yeah.
Eke wrote:
Sat Apr 27, 2019 12:30 pm
As for MODE bit, I think it takes priority over RET bit (also remember it is writable on Sub-CPU side only, and DMNA bit is not modified on writes from Sub-CPU anyway) but I don't think any games attempt to modify both bits together.
Of course, there is no priority, since it's effectively setting the RET bit for both modes at the same time. For instance, consider...

[ff8003]=00 (switch to 2M)
[ff8003]=01 (give WordRAM to MAIN)
[ff8003]=04 (switch to 1M, set bank=0)

versus

[ff8003]=00 (switch to 2M)
[ff8003]=05 (switch to 1M, set bank=1)

As you'd expect, the 1M WordRAM banks are assigned differently, based on last write to bit 0. However, [ff8003]=05 has the unexpected result of setting RET=1 despite the mode bit. In switching back to 2M (say, by setting [ff8003]=00), WordRAM will have already been assigned to MAIN, and the register value will reflect this: [ff8003]==01.

Similarly, from the MAIN side, setting the DMNA bit while in 1M will affect 2M WordRAM assignment, even though setting DMNA=1 in 1M is supposed to have no effect. Rise of the Dragon and Ultraverse Prime were the test cases I examined. ROTD sets DMNA=1 in 1M and expects SUB to have WordRAM after the switch to 2M. UP sets bank in 1M (so, RET=1) and expects MAIN to have WordRAM after the switch to 2M.

It's a shame they didn't give different names to those bits in 1M mode since they have separate functions, but as it turns out they still kinda work the same in 1M? Go figure.
Eke wrote:
Sat Apr 27, 2019 12:30 pm
The manual implies this is a temporary state, which means that, after a few cycles (nothing known about this one either and probably not visible from software), reset state is stabilized. The manual indicates initial state for this register is 0001h, which means 2M mode with Word-RAM assigned to Main-CPU.
Not that I've tested it, but a "forced reset" of the gate array presumably resets the register (thus, WordRAM assignment) in the same way. It's an odd case because it's initiated by software, then the ASIC performs the reset. The Flux cart issues a forced reset, but maybe not in a way that requires accurate emulation of it.
TmEE co.(TM) wrote:
Sat Apr 27, 2019 11:01 pm
The CD section clock is 44100 * 384 = 16934400Hz
Indeed. 16.9344MHz is the clock used by the Sony chipset for CD-ROM/-DA operations; a frequency also common in other CD-based systems. Elsewhere, you may see 33.8688MHz (2x) or 67.7376MHz (4x).

Near
Very interested
Posts: 109
Joined: Thu Feb 28, 2008 4:45 pm

Re: Questions on writing a new Mega CD emulator

Post by Near » Sun Apr 28, 2019 6:34 pm

Afaik, it is only needed for int1 (graphics operation) and not the case for int2
Oh wild, it's int1, not int2. Okay, well I guess I'll wait until I can run those games and see if it's needed in my case or not.
it seems you are confusing Gens and Genesis Plus GX emulators
Ah indeed I was, sorry about that ^-^;;
here is a backup of my dev diary
That will definitely help, thank you!
See also this thread for some interesting discussions: viewtopic.php?f=5&t=908
Yeah, the graphics hardware is giving me trouble. I am currently not getting the Sega CD logo at all, nor is the BIOS writing to the vector base pointer to start a graphics drawing operation. Even if I force some INT1 interrupts, they just do nothing.

Even once I figure out what the deal is there, I'm in the same boat: I don't understand how it works at all. It's extremely different from mode 7 on the SNES. Well I'll keep studying.
On sub-CPU side, this is little different because Sega CD ASIC only get A1-A19 so registers are actually mirrored in xf8xxxx.
Would that apply to the RAM as well? Seems like it would ...
It won't because the bus is 16-bit and many registers must be accessed as 16-bit words only (this is clearly mentionned in software manuals)
May I ask which document/page? I'm having trouble finding it.
you actually need to write 0 to that bit from Main-CPU software to set the DMNA bit to 1 on Main-CPU and Sub-CPU side (on read)
To make sure I understand what you mean ... so if I write from the main CPU with DMNA=1 but RET=1, it just becomes a no-operation?
Note that there are some undocumented stuff regarding the word-RAM assignment when MODE bit is switched to 2M
Yeah, I see the graphics hardware only starts when in 2M mode (per Genesis Plus GX), so the immediate question is what happens if you change the mode in the middle of rendering.
Note that there are some undocumented stuff regarding the word-RAM assignment when MODE bit is switched to 2M, depending on the last value written to DMNA or RET bits on either sides during 1M mode
This sounds terrifying. I will put it off for now but make a note of it, thank you.
I don't know anything about the later or where you get that from but I doubt it exists
LC89510 manual. CTRL1.d7. I imagine it's not used by the Mega CD, though.
(neither /CMD or /HWR inputs are connected in Mega CD according to service manuals).
Very good to know, I won't waste more time on it then, thank you.
The documentation is 'so bad' because it is missing ONE bit description?
I mean look, I'm quick to admit to not being that intelligent.

I found the manuals for the TLCS-900/H SoC in the NGPC (TMP95C061.pdf) a whole lot easier to follow than these.

I'd rather not derail on this, though. So, respectfully disagree? ^-^;;
I found a very old document i wrote when i started to reverse engineering how CDD worked
Thank you! I'll take a look when I get a bit further along.
(including access to sourcecode of this 4-bit micro-controller program)
Hesitant to ask, but is the MCU program dumped? I know emulating it would be silly, but I'd still be interested.

I remember looking into the SNES PlayStation's 4-bit CD controller MCU, and there was a debug mode to read it out, but of course the owner wasn't interested in attempting that ^^;
I think I've seen Gens (and maybe Picodrive as well) sourcecode doing this but I don't think it's true.
Okay, I've undone that so it won't clear now, thank you.
It seems to be a bug in the Z80 code that (remarkably) doesn't prevent the program from working properly, but you need to support the mirroring of ZRAM at 0x2000. Don't expect code trace to make much sense though. It's just... yeah.
Oh geez, that was exactly the problem, thank you so much ^-^

I had to lower the volume to 25% to match against my PSG + YM2612 (>>2&~15 instead of &~63), but it sounds great now.

Next step, figure out why the BIOS isn't doing anything with the graphics ASIC / stamp / image stuff.
Indeed. 16.9344MHz is the clock used by the Sony chipset for CD-ROM/-DA operations; a frequency also common in other CD-based systems. Elsewhere, you may see 33.8688MHz (2x) or 67.7376MHz (4x).
Well that's annoying, but I may go ahead and cheat this one. I only need 75hz interrupts from my CDD emulation, so having yet another cooperative thread just for that seems ... too costly. We'll see though.

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

Re: Questions on writing a new Mega CD emulator

Post by Stef » Mon Apr 29, 2019 8:10 am

Eke wrote:
Sat Apr 27, 2019 10:45 pm
There is also some good information in X-Eye technical manuals that appeared recently, see this thread: viewtopic.php?f=5&t=2707
Oh god, if i had this documentation back in time... i spent so many time (and CD) to RE the MegaCD just for this CDD stuff :D I didn't had any MegaCD back in time and it's Red5 (the guy owning GenesisProject website which was referencing Megadrive emulators compatibility list back in time) who literally sent me for free a SegaCD2 from England so i could develop SegaCD emulation in Gens :)

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

Re: Questions on writing a new Mega CD emulator

Post by Eke » Mon Apr 29, 2019 9:57 am

Stef wrote:
Mon Apr 29, 2019 8:10 am
Oh god, if i had this documentation back in time... i spent so many time (and CD) to RE the MegaCD just for this CDD stuff :D I didn't had any MegaCD back in time and it's Red5 (the guy owning GenesisProject website which was referencing Megadrive emulators compatibility list back in time) who literally sent me for free a SegaCD2 from England so i could develop SegaCD emulation in Gens :)
Yes, that's what I thought too when these docs appeared (when I worked on this, I only had your CDD_inf doc and Gens/Picodrive sourcecode, still better than nothing I guess, BIOS disassembly also helped a lot figuring some unknown status codes like Lead-Out), must have been a hell of reverse-engineering job.
Back when I was gathering tech infos about Sega CD emulation tricks on various old forums (mostly Eidolon's Inn Tavern and Gens/Fusion 'official' forums... sadly both down now), I remember reading some posts where you were asking questions about CDD to one emudev (Gerrie ?) who apparently had access to some infos (former licensed dev like Steve maybe?) and his answer was basically "I am not ready to reveal these infos yet" as it was a trade secret... strange old times 😁

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

Re: Questions on writing a new Mega CD emulator

Post by Stef » Mon Apr 29, 2019 10:28 am

It was Steve Snake (the author of KGen then Kega), he probably has some "internal" information as he did worked on some Sega titles back in time, but if they probably had a complete BIOS documentation, i think they didn't had much more than us about the CDD itself as BIOS was doing the job to hide low level stuffs here. Michel Gerrie developed Xega (another multi Sega System emulator) and we were "working together" about this MegaCD wall we wanted to break but afaik Michel Gerrie didn't has a SegaCD so he was mainly relying on my findings and follow, i think Michel Gerrie did actually asked Steve Snake about the CDD infos but he refused... Steve Snake was always very secret about his discovers, he never wanted to share them (or only when other people figured them...). I remember that i asked him about how he fixed YM emulation but again he didn't wanted to share. Well, that was the game ;)

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

Re: Questions on writing a new Mega CD emulator

Post by Eke » Mon Apr 29, 2019 6:21 pm

Would that apply to the RAM as well? Seems like it would
Yes, the main ASIC is handling entire sub-CPU address decoding so this apply to everything mapped in sub-CPU range (no software rely on this though)
May I ask which document/page? I'm having trouble finding it.
You have to look at registers description in the Hardware Manual (the one with greyed out parts), here are some examples with the mention "word access" (note that there is a pdf version of these manuals available online, not sure where exactly, maybe on segaretro):
https://www.megadrive.org/elbarto/megac ... Cdh-27.gif
https://www.megadrive.org/elbarto/megac ... Cdh-28.gif
https://www.megadrive.org/elbarto/megac ... Cdh-39.gif

Most likely (I couldn't confirm it so I did not emulate this behavior), ASIC ignores /UDS and /LDS for these address and always reads/writes 16-bit data there. For 8-bit reads it does not matter as 68k CPU will always pick the correct part (LSB or MSB) according to /LDS and /UDS state but this might lead to unexpected consequences on 8-bit writes (since 68k CPU puts written byte value to both D0-D7 and D8-D15, both upper and lower part of these registers probably get updated with same 8-bit value).

Also note that there are also mention of write-only registers ("0" or "O" on the "W" line also means associated bit is read-only and writing it has no effect) and also read-only ones (usually, this means the value of register is changing during processing and does not correspond to initial written value).
To make sure I understand what you mean ... so if I write from the main CPU with DMNA=1 but RET=1, it just becomes a no-operation?
RET bit does not matter, it can not be written by main-CPU so it keeps its previous value but yes, in 1M mode (but not in 2M mode), writing 1 to bit 0 has no effect on the read value and writing 0 set DMNA bit value to 1. Writing 1 to DMNA bit in 1M mode although have some side-effect when switching back to 2M mode, as mentionned earlier and as explained by TascoDlx above.
Yeah, I see the graphics hardware only starts when in 2M mode (per Genesis Plus GX), so the immediate question is what happens if you change the mode in the middle of rendering.
The graphics function is just a process that runs in parralel of others (CPU, DMA, refresh) so it likely still starts or runs in 1M mode, it's just that memory mapping is different so when read/write access are done in Word-RAM by the graphics function, they don't go at the desired physical address. I don't emulate this edge case as no games do this and it was more optimized to work directly on memory pointers than using memory handlers. Probably the reason why I checked if 2M mode is enabled before starting graphics operation although it's not 100% correct.
LC89510 manual. CTRL1.d7. I imagine it's not used by the Mega CD, though.

This is not a configurable interrupt source, this is just a bit that configure the decoder to insert an internal sync signal every 2352 bytes received from serial input pin (CD data stream input). This is a safety feature in case of errors in CD data stream input that would make the decoder misses the sync pattern between each CD sector and you can safely ignore this bit (as well as many others in these CTRL registers) because you will never emulate processing of a real CD data bitstream but instead have already decoded CD sectors at the input of the emulated chips.

On that note, I don't know how much you know about CD audio/data low-level format but it helped me a lot to study available literature that described how CD processing is done at low level, the various layers existing around logical sector data, subcodes, etc... when reading Mega CD technical docs (lot of things in LC8951 user manual were especially much easier to understand when you know what they are referring to). This doc in particular was quite helpful: https://www.google.com/url?sa=t&source= ... ma-130.pdf
Hesitant to ask, but is the MCU program dumped? I know emulating it would be silly, but I'd still be interested.
Not that I know. It also does not help that the used MCU chips are different not only between different Mega CD models (model 1, model 2, cdx/multimega, wondermega 1/2, x-eye, etc) but also depend on drive models (Model1/Model2 used different brand of CD drives). Apparently some of the used CDD chips have a ROM read-out mode but I have never heard of anyone having attempted to dump the internal ROM of any of these chips.
Even if not emulating this chip at low level, disassembling the internal program would still be interesting to figure exact behavior of the gate-array interface, as well as figuring communication protocol entirely.

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

Re: Questions on writing a new Mega CD emulator

Post by Eke » Tue Apr 30, 2019 6:24 am

Would that apply to the RAM as well? Seems like it would
Yes, the main ASIC is handling entire sub-CPU address decoding so this apply to everything mapped in sub-CPU range (no software rely on this though)
May I ask which document/page? I'm having trouble finding it.
You have to look at registers description in the Hardware Manual (the one with greyed out parts), here are some examples with the mention "word access" (note that there is a pdf version of these manuals available online, not sure where exactly, maybe on segaretro):
https://www.megadrive.org/elbarto/megac ... Cdh-27.gif
https://www.megadrive.org/elbarto/megac ... Cdh-28.gif
https://www.megadrive.org/elbarto/megac ... Cdh-39.gif

Most likely (I couldn't confirm it so I did not emulate this behavior), ASIC ignores /UDS and /LDS for these address and always reads/writes 16-bit data there. For 8-bit reads it does not matter as 68k CPU will always pick the correct part (LSB or MSB) according to /LDS and /UDS state but this might lead to unexpected consequences on 8-bit writes (since 68k CPU puts written byte value to both D0-D7 and D8-D15, both upper and lower part of these registers probably get updated with same 8-bit value).

Also note that there are also mention of write-only registers ("0" or "O" on the "W" line also means associated bit is read-only and writing it has no effect) and also read-only ones (usually, this means the value of register is changing during processing and does not correspond to initial written value).
To make sure I understand what you mean ... so if I write from the main CPU with DMNA=1 but RET=1, it just becomes a no-operation?
RET bit does not matter, it can not be written by main-CPU so it keeps its previous value but yes, in 1M mode (but not in 2M mode), writing 1 to bit 0 has no effect on the read value and writing 0 set DMNA bit value to 1. Writing 1 to DMNA bit in 1M mode although have some side-effect when switching back to 2M mode, as mentionned earlier and as explained by TascoDlx above.
Yeah, I see the graphics hardware only starts when in 2M mode (per Genesis Plus GX), so the immediate question is what happens if you change the mode in the middle of rendering.
The graphics function is just a process that runs in parralel of others (CPU, DMA, refresh) so it likely still starts or runs in 1M mode, it's just that memory mapping is different so when read/write access are done in Word-RAM by the graphics function, they don't go at the desired physical address. I don't emulate this edge case as no games do this and it was more optimized to work directly on memory pointers than using memory handlers. Probably the reason why I checked if 2M mode is enabled before starting graphics operation although it's not 100% correct.
LC89510 manual. CTRL1.d7. I imagine it's not used by the Mega CD, though.

This is not a configurable interrupt source, this is just a bit that configure the decoder to insert an internal sync signal every 2352 bytes received from serial input pin (CD data stream input). This is a safety feature in case of errors in CD data stream input that would make the decoder misses the sync pattern between each CD sector and you can safely ignore this bit (as well as many others in these CTRL registers) because you will never emulate processing of a real CD data bitstream but instead have already decoded CD sectors at the input of the emulated chips.

On that note, I don't know how much you know about CD audio/data low-level format but it helped me a lot to study available literature that described how CD processing is done at low level, the various layers existing around logical sector data, subcodes, etc... when reading Mega CD technical docs (lot of things in LC8951 user manual were especially much easier to understand when you know what they are referring to). This doc in particular was quite helpful: https://www.google.com/url?sa=t&source= ... ma-130.pdf
Hesitant to ask, but is the MCU program dumped? I know emulating it would be silly, but I'd still be interested.
Not that I know. It also does not help that the used MCU chips are different not only between different Mega CD models (model 1, model 2, cdx/multimega, wondermega 1/2, x'eye, etc) but also depend on drive models (Model1/Model2 used different brand of CD drives). Apparently *EDITED* one of them (the one used in X'Eye console) has a ROM read-out mode but the others haven't (at least it's not documented in datasheets) and I have never heard of anyone having attempted to dump the internal ROM of any of these chips.
Even if not emulating this chip at low level, disassembling the internal program would still be interesting to figure exact behavior of the interface with gate-array, as well as figuring communication protocol entirely.

Near
Very interested
Posts: 109
Joined: Thu Feb 28, 2008 4:45 pm

Re: Questions on writing a new Mega CD emulator

Post by Near » Tue Apr 30, 2019 11:46 am

This is really kicking my ass, heh.

Currently, the USA Sega CD 2.00w BIOS says "PRESS START BUTTON" at power-on, which will take me into the BIOS where it does say "NO DISC".
But I want it to say "NO DISC" at the boot screen, which appears to be the correct behavior as per videos I've seen online.

Starting with cdd_inf.txt:

Code: Select all

Command 0x0000: Get CDD status
  This command returns the current status of the CDD

   RS0 = 0x00 : This indicates that no CD is present in the tray.
   RS0 = 0x01 : This indicates the CDD is playing an audio track.
   RS0 = 0x04 : This indicates that a CD is present in the tray
                (no operation occuring).
   RS0 = 0x05 : This indicates the CD tray is open.
   RS0 = 0x09 : This indicates that a stop operation occured (0x0100) and
                a CD was present.
This is also what Picodrive uses. But this pushes the BIOS to "CHECKING DISK" that never stops.

Genesis Plus GX and the X-Eye manual state that Stop = 0x0, and No Disc = 0xb, so I am going with that for now.

...

Is ff8038-ff8041 writable? (CDD status registers.) I am guessing not, and that they're for the CDD to write to them.
The manual says that writing ReceiveStatus[7] generates an INT4 IRQ, but I would presume it would actually be ReceiveStatus[9] when the checksum is written.

Are writes to ff8042-ff804b always word-based? The manual doesn't say so. If not, will a write to ff804a trigger a CDD command process?
For now, I'm emulating it as though it honors /UDS and /LDS:

Code: Select all

  if(address >= 0xff8042 && address <= 0xff804b) {  //address&1 is always 0, could just as easily say address <= 0xff804a here
    uint index = address - 0xff8042;  //0, 2, 4, 6, 8
    if(lower) cdd.command[index | 1] = data.bits(0, 3);  //lower=1 for /LDS
    if(upper) cdd.command[index | 0] = data.bits(8,11);  //upper=1 for /UDS
    if(lower && (index | 1) == 9) {  //unconfirmed
      cdd.command.sending = 1;
    //cdd.process();
    }
  }
If I trigger the CDD processing (check command[0] and update status[] values) immediately, and fire a CDD IRQ immediately, the BIOS hangs forever with no text at the bottom of the screen.

Upon writing ff804b (command[9]) ... if I set ff8036.d0=1 (DTS), then wait until the next CDD 75hz timing event to clear DTS and process the command, and then fire a CDD IRQ immediately there, things work, but still shows "PRESS START BUTTON".

Upon writing ff804b ... if I immediately process the command, and then set ff8036.d1=1 (DRS), then wait until the next CDD 75hz timing event to clear DRS and fire a CDD IRQ, things also work, but still shows "PRESS START BUTTON".

What is the actual correct behavior? Does either DTS or DRS wait for the next 75hz CDD timing event? Or do they just take a small (but non-zero amount of time) to complete?

Inside the CDD processing function ... the X-Eye manual states that command[0]=0x0 is "no operation", but Genesis Plus GX states it's a latched copy of the status (cdd.status variable.)

When Genesis Plus GX executes command 0x1 (stop drive), it sets status[0]=0x0 (STOP), and then cdd.status=0xb (NO_DISC). It doesn't seem to matter whether I do this indirect latching or not, I still get "PRESS START BUTTON".

When Geneis Plus GX executes command[0]=0x1, command[3]=0x4 (first and last track numbers), it's returning 0x1 in status[3] and 0x0 in status[5] even when there is no disc present. I presume it should be 0x0 for both when there's no disc. But it doesn't seem to matter either way.

When Genesis Plus GX executes command[0]=0x0, it does not clear status[1-8], unless status[3]=0xf, as some sort of Lunar: Silver Star workaround, but at least in the BIOS, this condition isn't true. So I am only setting status[0]=cdd.status here.

...

Upon processing a CDD command, Genesis Plus clears command[8] and command[9] = 0. Is this confirmed behavior? Is it really both words?
This doesn't even serve the purpose of truly invalidating the CDD command checksum, because if command[0-6]=0 and command[7]=0xf, then the checksum would still be valid. So it feels like it should either erase command[0-9] or not bother erasing any of it.

...

Upon powering on the system, things only seem to work if I clear status[0-8], set status[9]=0xf (eg valid checksum), and have cdd.status=0xb (NO_DISC). If the checksum isn't valid, the BIOS hands forever. If I give status[0]=0x0 (STOP), the BIOS goes to "CHECKING DISC" forever.

...

No matter what I do, the BIOS will hang unless CDD IRQs are generated every 75hz. Trying to rely on DTS being set to process a command, or DRS being set to execute the IRQ, and then clearing those flags and preventing subsequent IRQs, will hang the BIOS forever. As long as HOCK=1, the IRQs seem to have to fire every 75hz.

I have corrected HOCK (which stands for host clock enable) to stop the CDD completely, and not fire IRQs, and nothing clears HOCK other than writing ff8036.d2=0.

...

So that's where I am stuck after six hours ... any advice? ;__;

Near
Very interested
Posts: 109
Joined: Thu Feb 28, 2008 4:45 pm

Re: Questions on writing a new Mega CD emulator

Post by Near » Tue Apr 30, 2019 1:50 pm

Okay, one step closer. GX fires an IRQ right away on writes to ff8037 when HOCK transitions from 0 to 1.
With that, I don't have to always fire an IRQ every 75hz for the CDD.

Code: Select all

auto MCD::CDD::clock() -> void {
  if(!hostClockEnable || ++counter < 434) return;
  counter = 0;
  //75hz
  if(status.receiving) {
    status.receiving = 0;
    irq.raise();
  }
}
I am binding status.receiving as DRS.

It looks like GX is also setting regs[0x4a]=0x00 when a status result is pending, and regs[0x4a]=0xf0 when one isn't.
But command[9] is a 4-bit value. It doesn't seem to make a difference if I emulate this behavior or not.
It does at least explain why ff804a writes in GX were clearing command[8-9] to 0x0.

Still not getting any further, but at least the code is closer still to how GX works. The BIOS executes:
CDD process 0x0
CDD process 0x1
CDD process 0x2:0x4
CDD process 0x2:0x3
CDD process 0x1
CDD process 0x0 <repeats forever>

...

Edit: well that's really annoying. It doens't say "No disc" on real hardware either. That might be a v2.21 BIOS thing, or something, I don't know.

Alright then, I'll move onto trying to figure out this insane graphics rotozoom stamp/image/over-underlay stuff next before I try and delve into actually reading CDs.

HardWareMan
Very interested
Posts: 745
Joined: Sat Dec 15, 2007 7:49 am
Location: Kazakhstan, Pavlodar

Re: Questions on writing a new Mega CD emulator

Post by HardWareMan » Tue Apr 30, 2019 5:47 pm

We already discussed nIRQ frequency: viewtopic.php?f=13&t=793&start=105#p31682
It's like a nIRQ not synced to any of signal. As I said before, this signal has 6,33ms period time slot. But it don't fire up at every slot (6,33ms), some slots a skipped (when drive controller busy?).
BTW, 6,33ms is ~157Hz. Maybe it is 2x 1/75? 75*2=150Hz and I got just measurement error?

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

Re: Questions on writing a new Mega CD emulator

Post by Eke » Tue Apr 30, 2019 8:56 pm

Is ff8038-ff8041 writable? (CDD status registers.) I am guessing not, and that they're for the CDD to write to them.
Indeed they are not. They are written by the ASIC upon receiving CDD status words through the CDD interface (4 bits at a time).
The manual says that writing ReceiveStatus[7] generates an INT4 IRQ, but I would presume it would actually be ReceiveStatus[9] when the checksum is written.
Pretty sure the manual is correct. As said in a previous post, interrupt handler (which reads CDD status registers then update CDD command registers) does not run immediately when IRQ is trigerred, there is some latency because of 68k interrupt processing. They likely decided to trigger IRQ a little bit earlier to save some time for interrupt handler, the last CDD status registers (with checksum value) will still have been updated when the interrupt handler starts executing.
Are writes to ff8042-ff804b always word-based? The manual doesn't say so.
If nothing is indicated (cf. my previous post) then it means there are no restrictions, which is kinda logical since each byte address corresponds to one of the 10 command nibble. It is just that word-access is more efficient so probably all BIOS do that.
If not, will a write to ff804a trigger a CDD command process?
If you mean a byte write then no, since (according to the software manual) the ASIC waits for the last command 4-bit value to be updated (in ff804b) to start sending the command words on CDD interface.
If I trigger the CDD processing (check command[0] and update status[] values) immediately, and fire a CDD IRQ immediately, the BIOS hangs forever with no text at the bottom of the screen.
That's because that is not how the hardware works. CDD starts processing the command shortly after the last command register has been updated (not immediately since it has to wait for the receipt of the 10 command nibbles through ASIC->CDD interface but emulating immediate transmission works fine since CDD is not emulated at low level anyway) BUT it waits for the next 75hz period start before asserting /IRQ signal on CDD->ASIC interface (which makes the ASIC start receiving next CDD status nibbles then triggering a level 4 interrupt when 8th nibble has been received).
Upon writing ff804b (command[9]) ... if I set ff8036.d0=1 (DTS), then wait until the next CDD 75hz timing event to clear DTS and process the command, and then fire a CDD IRQ immediately there, things work, but still shows "PRESS START BUTTON".
This should work correctly, although emulating DTS bit is not necessary (it does not seem to be checked by BIOS as emulating immediate transmission works just fine and most likely it is cleared much sooner than what you are doing since the transfer of 4-bit command nibbles is quite fast, and CDD needs some time before next 75hz period to process it). It would also be more correct to process CDD command soon after the last command register rather than just before the next 75hz interrupt (but it does not really matters, as long as you updated CDD status registers before int4 handler is executed).
Upon writing ff804b ... if I immediately process the command, and then set ff8036.d1=1 (DRS), then wait until the next CDD 75hz timing event to clear DRS and fire a CDD IRQ, things also work, but still shows "PRESS START BUTTON".
Yes because it is similar to previous one (only difference is you process CDD command at a different time but, as explained above, it makes no difference from software point of view). Again, DRS bit likely does not work like this (it is probably set to 1 shortly before int4 triggering when the CDD status reception starts and cleared shortly after int4 is triggered once the 10th status nibble has been received by CDD interface in ASIC registers) and is not necessary to emulate (immediate status transfer works just fine).
What is the actual correct behavior? Does either DTS or DRS wait for the next 75hz CDD timing event? Or do they just take a small (but non-zero amount of time) to complete?
hardware behavior likely is the following:
1) CDD asserts /IRQ to indicate it is ready to send its status then wait for HOCK signal falling edge
2) Gate-Array asserts HOCK signal (if corresponding bit is set to 1 in register) to indicate it is ready to receive CDD status then wait for CDCK signal to reset HOCK signal
3) CDD asserts CDCK signal while transmitting first 4-bit data on interface then wait for next HOCK falling edge to send next nibble, etc
4) Gate-Array set DRS bit to 1, copy first nibble to first status register then assert HOCK signal again to get next nibble,etc
5) when 8th status nibble has been copied to internal register, Gate-Array triggers level 4 interrupt request
6) when 10th status nibble has been read, Gate-Array clears DRS bit and CDD waits for next HOCK falling edge indicating command transmission
7) when 10th command register is written by software (normally by BIOS during int4 processing), Gate-Array set DTS bit to 1, asserts HOCK signal while putting first command nibble on CDD interface then wait for CDCK before sending next nibble, etc
8 ) when 10th command nibble has been written to CDD interface, Gate-Array clears DTS bit then waits for next /IRQ edge from CDD, while CDD starts processing the received command (and updates its status) then waits for next 75hz period

As seen above, there are various ways to emulate this but what matters is that int4 occurs every 1/75s when communication is functional and that CDD command is processed and CDD status registers updated before next int4 processing by BIOS. And DTS and DRS bits can be left cleared all the time (simulating zero nibble transmission time on CDD interface) without issues (exact transmission timing is unknown).
Inside the CDD processing function ... the X-Eye manual states that command[0]=0x0 is "no operation", but Genesis Plus GX states it's a latched copy of the status (cdd.status variable.)
In case of nop command, CDD still send its status to gate-array.
When Genesis Plus GX executes command 0x1 (stop drive), it sets status[0]=0x0 (STOP), and then cdd.status=0xb (NO_DISC). It doesn't seem to matter whether I do this indirect latching or not, I still get "PRESS START BUTTON".
I only set status to 0xb if there is no disc (cdd.loaded=0), otherwise it is set to 0x9 (READ TOC). In latter case, this matches what is described in CDD_inf.txt: apparently, after the disc is stopped, status only briefly stays to 0x0 then change to 0x9. Not sure if Stef doc is 100% correct but it looks like drive is never really stopped and lead-in area is read again.
When Geneis Plus GX executes command[0]=0x1, command[3]=0x4 (first and last track numbers), it's returning 0x1 in status[3] and 0x0 in status[5] even when there is no disc present. I presume it should be 0x0 for both when there's no disc. But it doesn't seem to matter either way.
I guess you meant when command[0]=0x2, that I still return first track value set to 01 without caring if there is a disc inserted or not. It's just I didn't bothered emulating these edge cases as they never happen, BIOS seem yo only send NOP command after the first status reporting NO_DISC.
I have no idea what values are set by CDD in RS1-RS8 in that case, maybe 0xf to indicate invalid status infos...or maybe 0x0 to indicate zero tracks, I don't know.
When Genesis Plus GX executes command[0]=0x0, it does not clear status[1-8], unless status[3]=0xf, as some sort of Lunar: Silver Star workaround, but at least in the BIOS, this condition isn't true. So I am only setting status[0]=cdd.status here.
nop command let the status unchanged normally
This behavior is just to delay the update of status after a seek command because some games didn't liked that the status immediately returned at the first interrupt following seek command was already indicating seek destination so I have to force the status invalid (by setting RS1 to 0xf) for some interrupts to simulate seek time. After seek command, BIOS use nop command to continuously read the status until seek is finished so I used this to reset RS1-RS8 to some valid value after seek is finished because Lunar expected it to be valid at least once after seek before sending play command. I guess this could have been done in cdd_update function where cdd.latency is handled, not sure why this ended here but there must be some reason.
Upon processing a CDD command, Genesis Plus clears command[8] and command[9] = 0. Is this confirmed behavior? Is it really both words?
This doesn't even serve the purpose of truly invalidating the CDD command checksum, because if command[0-6]=0 and command[7]=0xf, then the checksum would still be valid. So it feels like it should either erase command[0-9] or not bother erasing any of it.
Hum, this is just a dirty hack to indicate the latest command register has been written (by forcing it to zero, which I agree is not very clever either) so that I know when 75h period is over if int4 should be triggered or not
(indeed, as explained above, it seems that CDD stops triggering /IRQ and sending status if gate-array stops sending command so int4 is also stoped . I was apparently too lazy to use a separated boolean flag and thought it was a 'brillant' idea to reuse existing registers (CDD command checksum computed by BIOS is not verified so it didn't matter). I guess I should rework that part as it is indeed not very clean.
On real hardware, nothing get cleared by hardware off course (except on reset).
Upon powering on the system, things only seem to work if I clear status[0-8], set status[9]=0xf (eg valid checksum), and have cdd.status=0xb (NO_DISC). If the checksum isn't valid, the BIOS hands forever. If I give status[0]=0x0 (STOP), the BIOS goes to "CHECKING DISC" forever.
BIOS verifies CDD status checksum at the start of int4 processing so it must be properly updated, yes.
And it seems stop status is supposed to be only temporary as discussed above, it automatically changes to 0x9 (read toc) or 0xb (no disc) after some time.
It looks like GX is also setting regs[0x4a]=0x00 when a status result is pending, and regs[0x4a]=0xf0 when one isn't.
But command[9] is a 4-bit value. It doesn't seem to make a difference if I emulate this behavior or not.
It does at least explain why ff804a writes in GX were clearing command[8-9] to 0x0.
Yes, it is related to hack above :oops:
Edit: well that's really annoying. It doens't say "No disc" on real hardware either. That might be a v2.21 BIOS thing, or something, I don't know.
Fom what I remember, "press start" message is correct on both Model 1 and Model 2 US/PAL BIOS. On Model 1, when there is no disc, it still let you enter the menu interface because that's the only way you can open the tray to insert a disc. Model 2 (2.00/2.11) BIOS apparently kept that behavior although the tray could only be opened manually, maybe to keep consistency with previous model.
2.21 is CDX/Multimega BIOS and it has indeed different behaviors because I think it can be used as portable CD player.

Near
Very interested
Posts: 109
Joined: Thu Feb 28, 2008 4:45 pm

Re: Questions on writing a new Mega CD emulator

Post by Near » Wed May 01, 2019 1:29 am

Thank you very much, this is incredibly useful information.

Don't mind the shortcuts with the registers, if it works it works. I just wanted to confirm them before copying the same approach ^-^;;

In unrelated news, I looked into the 4-bit MCU more.

The Sega CD model 2 uses the NEC uPD75006G. Unfortunately, it cannot be read out digitally. The program verification mode is only on the uPD750016 through the MD0-3 pins. It is a mask ROM model, which means our only viable option would be to decap the chip and try to read it out optically.

In light of that, the JVC X-Eye seems to be our best shot. That uses the Hitachi HD404019, which does seem to have a program read-out mode. But we would be analyzing a third-party hardware system instead of something from Sega themselves. It could still lead to some insights, like what if anything CDD command 0xD is for. I can't say it'd prove to be a boon for Sega CD emulation, just more of a curiosity.

Sik
Very interested
Posts: 939
Joined: Thu Apr 10, 2008 3:03 pm
Contact:

Re: Questions on writing a new Mega CD emulator

Post by Sik » Wed May 01, 2019 5:01 am

byuu wrote:
Wed May 01, 2019 1:29 am
In light of that, the JVC X-Eye seems to be our best shot. That uses the Hitachi HD404019, which does seem to have a program read-out mode. But we would be analyzing a third-party hardware system instead of something from Sega themselves. It could still lead to some insights, like what if anything CDD command 0xD is for. I can't say it'd prove to be a boon for Sega CD emulation, just more of a curiosity.
That sounds like a bad idea actually. From messing with emulators, the Sega firmwares are more or less interchangeable (they all seem to work regardless of emulation warnings), but Wondermega and X'Eye end up waiting forever for the CD drive, which implies JVC's hardware is likely using a different set of 68000-facing commands (there's a good reason why Sega docs were opaque about this).

In other words, if you want to be able to emulate the Sega firmwares (at least some of them), you pretty much need to go with one of Sega's models. If you try to emulate the X'Eye you'll be probably stuck to emulating X'Eye and Wondermega only :​P
Sik is pronounced as "seek", not as "sick".

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

Re: Questions on writing a new Mega CD emulator

Post by Eke » Wed May 01, 2019 6:25 am

X'Eye and Wondermega1/2 BIOS are compatible with Genesis Plus GX using the exact same CDD emulation model as with other models so I don't think CDD programs are incompatible or even so much different but rather that they are all based on the same source, with some added internal functions to support the extra features (LCD panel on CDX/Multimega, Voice Masking control on X'Eye/Wondermegas,...) but those are not handled by the BIOS so you can ignore them in emulator and still get BIOS working fine.

The commands described in X'Eye technical manual are the same as the ones used in other BIOS, and the status codes are compatible as well (in particular error codes which are all checked by Model1/2 BIOS).

For the record, X'Eye and Wondermega1/2 BIOS hanged initially in my emulator (and likely others) because of 2 reasons:

1) backup RAM is accessed at a mirrored location (not 100% sure about this one though, I think it's something I noticed when my emulation was still at early stage and I was trying all existing BIOS to find emulation flaws)

2) value written to register ff8034 (CD-DA fader/DAC command register) does not correspond to official Mega CD documentation: in particular Bit15 is set to 1, which in turn locks the BIOS if you don't clear it on read (EFDT status bit still expected to work as described)

The reason for the latter is that the value written in this register is just a raw copy of 16-bit serial data stream sent by Gate-Array to CD-DA DAC chip on ATT output pin (DAC control data) and that X-Eye/Wondermega1/2 used different chips with different control data format (CXD2554M in Wondermega M1, SM5841A in Wondermega M2/X'Eye) instead of LC7883 chip, while keeping the same ASIC and DAC hardware interface as other models.

I emulate all DACs command format in Genesis Plus GX (see https://github.com/ekeeke/Genesis-Plus- ... cd.c#L1309) and verified CD-DA fading worked fine with any BIOS using Earthworm Jim CD option menu which lets you adjust CD-DA volume output.
Note how the ASIC always send 16 bits of data on serial interface: for DACs with 8-bit commands, LSB is set to 0 and those bits are simply ignored by the DAC.

Near
Very interested
Posts: 109
Joined: Thu Feb 28, 2008 4:45 pm

Re: Questions on writing a new Mega CD emulator

Post by Near » Wed May 01, 2019 2:14 pm

Well, I almost have graphics working (pretty much directly lifted from Genesis Plus GX for now, extremely helpful code!)
But one last issue is kicking my tail again >_>

Image
Larger: https://i.imgur.com/kXjqtED.png

Gone over the code like 50 times. I'm sure it's a simple mistake somewhere, but ...

My attempt at emulation is here: https://pastebin.com/raw/ajdT2g7A
Based off of: https://github.com/ekeeke/Genesis-Plus- ... gfx.c#L472

I had to adapt a lot because I do things differently internally:
* word RAM is uint16 wram[0x20000] instead of uint8 wram[0x40000]
* my I/O handlers store the written bits to the address bits as described in the manual, eg:

Code: Select all

  if(address == 0xff805e) {
    gpu.image.base.bits(5,17) = data.bits(3,15);
  }
* I have N-bit types (eg uint24), which are lovely for skipping things like the need to mask xpos,ypos by 0xffffff and such

So, I see that GPGX is caching some stuff when starting a new block render (the BIOS seems to render 304x40 block chunks at a time):
* dotMask, stampShift, mapShift, mapPtr, bufferStart, bufferOffset
It doesn't seem to make a difference whether I cache those in gfx_start or recompute them in gfx_render, so I prefer the latter so I have less internal struct state to worry about.

The I/O registers end up taking byte addresses if you go by the manual bits, eg: ff805e states A5-A17=D3-D15. But since it's word RAM, that's really A4-A16 ... or you treat it like Sega Genesis addresses where A0 is always clear and >>1 into the word array. Easy enough, so then we have 18-bit addresses. But then GPGX computes bufferStart as a 19-bit address, since we have two pixels per byte. Also workable. Finally, GPGX does some with pixel_in and pixel_out to be able to read and write 8-bits at a time, which I can simplify with a function to read and write nibbles.

Code: Select all

auto MCD::GPU::read(uint19 address) -> uint4 {
  static const uint table[] = {12, 8, 4, 0};
  uint lo = table[address & 3], hi = lo + 3;
  return mcd.wram[address >> 2].bits(lo, hi);
}
auto MCD::GPU::write(uint19 address, uint4 data) -> void {
  static const uint table[] = {12, 8, 4, 0};
  uint lo = table[address & 3], hi = lo + 3;
  mcd.wram[address >> 2].bits(lo, hi) = data;
}
(I've tried every possible combination of nibble indexes here to no avail. 0,4,8,12; 4,0,12,8; 8,12,0,4; etc. Pretty sure it should be 12,8,4,0 in any case.)

Unsure if vdots=0 is the same as vdots=256, but it doesn't matter for this BIOS issue. Same goes for hdots=0.

An odd thing with GPGX is the initial bufferStart, which adds reg[0x60]&0x3f to it (the image offset) as a pixel displacement.
The BIOS routine is always setting zero here, so it's not related to my rendering issue, but it's interesting nonetheless.

Inside the renderer:

Code: Select all

if ((bufferIndex & 7) != 7)      bufferIndex++;
else      bufferIndex += gfx.bufferOffset;
// gfx.bufferOffset = (((scd.regs[0x5c>>1].byte.l & 0x1f) + 1) << 6) - 7;
Okay simple enough. For each line rendered, we run for hdots worth of pixels.
Every time our index hits an 8-pixel tile edge, we seek to the next tile. The -7 compensates for us having incremented 7 times already.
Each tile is always 64 bytes, which is 16x16 pixels worth of data, seemingly unaffected by the stamp tile size bit.

I guess it's odd that it's ((bufferIndex&7)!=7) instead of something like:

Code: Select all

for(uint i=0;i<hdots;i++) {
  if((i&7)!=7) ...
But, I guess that's just how it works.

There's also this:

Code: Select all

/* end of graphics operation */
scd.regs[0x58>>1].byte.h = 0;
I'm guessing this is another workaround to save a bit, and that the actual Main-CPU determines the end of a graphics transfer via the IRQ.

But ... yeah, none of it explains my image result. It looks as if I'm rendering one line too many/too few, and that each other tile column is offset by one, but no matter which variables I tweak, nothing seems to improve either case. Fun.

Post Reply