Questions on writing a new Mega CD emulator

Ask anything your want about Mega/SegaCD programming.

Moderator: Mask of Destiny

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 7:38 pm

Everything looks shifted by one word… you sure it isn't a DMA problem rather than ASIC? I recall there's a pretty big problem with DMAing from word RAM where the first access wouldn't be correct and games had to compensate for it. Forgot the exact details tho, although it should show up somewhere in the manuals.
Sik is pronounced as "seek", not as "sick".

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 7:48 pm

It could very easily be a bug elsewhere in the Mega Drive emulator.

I would have thought if DMA were broken that we'd have an issue with most cartridge-based games, and yet I'm not currently aware of any. Granted I have not extensively tested the library. But if it's specific to just Mega CD word RAM, yiiiiiiiiiiikes. I don't even know how to emulate something like that. I would understand if it were an issue with the CDC DMA, but the BIOS isn't using CDC DMA.

Still, it's worth looking into, as that could be it, thank you.

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 8:34 pm

I recall it's a timing issue (word RAM is too slow and the DMA hardware has zero notion of paying attention to the bus signals, which in itself has caused other problems). Virtua Racing (SVP) has a similar issue with the SVP RAM if I remember correctly.

I'll try to see where in the docs this is mentioned (and exactly what the bug is)

EDIT: bingo

Image

…explanation doesn't seem to be very obvious (these docs were translated from Japanese and it honestly wasn't a great job as everything tends to sound awkward) but it seems to be the issue I'm talking about at least.

EDIT 2: I should also probably mention that the mask ROMs in cartridges and the 68000's work RAM pretty much never have issues with DMA (aside from the 128KB boundaries in the former case) which is probably why you never spotted anything weird going on. The problems arise when you start throwing in more complex hardware into the mix and said hardware isn't that fast.

EDIT 3: I hate how there's both work RAM and word RAM (which are two completely separate things :​v)
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 10:06 pm

I see that GPGX is caching some stuff when starting a new block render
Yes, to optimize rendering, I precalculate all the parameters that remain unchanged and initialize address pointers when the gfx operation is triggered rather than recalculating or reading again everything on each line, assuming no software will try to modify ASIC registers on-the-fly during gfx operation.

Note that I also use some lookup tables (LUT) initialised in gfx_init to optimize some calculations (for the record, I was mostly inspirated by Charles MacDonald's VDP rendering code that heavily made use of LUTs for optimizing tiles rendering). This probably makes the gfx rendering code a little bit harder to follow but I generally tried to comment my code with as much details as possible.
An odd thing with GPGX is the initial bufferStart, which adds reg[0x60]&0x3f to it (the image offset) as a pixel displacement.
That is because bufferStart/bufferIndex is a pixel 'address' (written byte address = bufferIndex >> 1 ) to make processing easier (all width/offset register values are in pixels unit). Register ff8060 contains line offset (0-7) in bits 5:3 and pixel offset (0-7) in bits 2:0 for the image buffer start address but since base address (in register ff8058) is always aligned to the start of a 8x8 pixels cell and 8-pixels lines within a cell are arranged sequentially in memory, the 6-bit value in register ff8060 actually corresponds to pixel offset (0-63) from the base address.
Each tile is always 64 bytes, which is 16x16 pixels worth of data, seemingly unaffected by the stamp tile size bit.
No, each tile/cell in image buffer is 64 (8×8) pixels. As explained above, all calculations (including address and offset values) are treated in pixels unit.
I guess it's odd that it's ((bufferIndex&7)!=7) instead of something like
You have to keep in mind that, because of image buffer offset register, the image buffer start address can be anywhere in the virtual cell map and it is not necessarely aligned to the first of the eight pixels within a cell line so your proposed code would be incorrect in such case. In short, bufferStart (initial value of bufferIndex) & 7 is not always equal to 0, hence why this is coded like this.
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.
Well, not this time :wink:
Bit 15 is actually used to indicate gfx operation is in progress and cleared when it ends. For some reason, it's only documented in hardware manual but not software manual :roll:
See https://www.megadrive.org/elbarto/megac ... Cdh-35.gif

I am quite sure this bit is polled in some games instead of just using int1
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.
To me, it looks like the pixels of each rendered cell are flipped horizontally but I am not sure. Either you are not interpreting stamp data HFLP/RT0/RT1 bits correctly or you are reading/writing pixel data at incorrect offset. This might be a common little-endian/big-endian mistake where data written as byte must be swapped on little-endian platforms if directly read and interpreted as 16-bit word elsewhere, for example VDP DMA). In GPGX, this is handled by READ_BYTE/WRITE_BYTE macros.

Another possible issue is that you are not emulating some (badly documented) issue when doing VDP DMA from Mega CD RAM: for this kind of DMA, software will always set VDP destination address one word before wanted address (apparently to take in account some memory read-out latency) so you have to take this in account and adjust DMA destination address and DMA remaining length when copying data from Word-RAM. See also https://github.com/ekeeke/Genesis-Plus- ... trl.c#L803
[EDIT] Missed Sik's post while typing mine but that's indeed the same issue he mentionned.

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 » Thu May 02, 2019 4:49 am

That was it, thank you Sik and Eke!

Image

Man, I keep thinking this system can't get any flakier and yet it keeps managing to impress me. I have no clue how to emulate this cleanly.

I can't implement it as a one-time action inside writes to VDP register 1 (which sets the DMA enable flag), because it seems like other DMA registers get written after that and before the transfer commences. So instead, I have to set a "starting DMA flag", and then act on it inside the VDP DMA load handler.

This is hacked in for now, I know I need to check ranges 600000-7fffff once I implement the /CART_IN pin, and only do this for the Sega CD peripheral.

And even moreso, I need to make the first write just outright fail to write anything useful rather than repeating the second word twice.

Anyway, the basic behavior should be:
VRAM[0] = WRAM[0]
VRAM[1] = WRAM[1]
VRAM[2] = WRAM[2]
But in practice, it does:
VRAM[0] = <nothing, open bus, whatever>
VRAM[1] = WRAM[0]
VRAM[2] = WRAM[1]
So when it says to set the WRAM address one greater, that then causes:
VRAM[0] = <nothing>
VRAM[1] = WRAM[1]
VRAM[2] = WRAM[2]

Code: Select all

auto VDP::DMA::run() -> void {
  if(!io.enable || io.wait) return;

  if(!vdp.io.command.bit(5)) return;
  if(io.mode <= 1) return load();
  if(io.mode == 2) return fill();
  if(!vdp.io.command.bit(4)) return;
  if(io.mode == 3) return copy();
}

auto VDP::DMA::load() -> void {
  cpu.wait |= Wait::VDP_DMA;

  uint24 address = io.mode.bit(0) << 23 | io.source << 1;
  auto data = cpu.read(1, 1, address);
  vdp.writeDataPort(data);

  io.source.bits(0,15)++;
  if(io.starting) {
    io.starting = 0;
    if(address >= 0x200000 && address <= 0x3fffff) io.source.bits(0,15)--;
  }

  if(--io.length == 0) {
    vdp.io.command.bit(5) = 0;
    cpu.wait &=~ Wait::VDP_DMA;
  }
}
Well anyway ... on to CD emulation. Pray for me ._.

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 » Thu May 02, 2019 5:19 am

byuu wrote:
Thu May 02, 2019 4:49 am
Man, I keep thinking this system can't get any flakier and yet it keeps managing to impress me. I have no clue how to emulate this cleanly.

I can't implement it as a one-time action inside writes to VDP register 1 (which sets the DMA enable flag), because it seems like other DMA registers get written after that and before the transfer commences. So instead, I have to set a "starting DMA flag", and then act on it inside the VDP DMA load handler.
All the DMA enable flag does is make sure DMA doesn't trigger by accident (because, um, the DMA hardware is really kind of buggy… it's probably the buggiest part on the whole system). Think of it like a safety lock. You're probably looking for the DMA trigger instead, which is when the 68000 writes the destination address (as that command is what actually starts the DMA).

Emulating this "properly" would likely involve emulating the full bus interactions between the individual parts. Not sure if you're willing to go that low level (heck, does Exodus attempt to go that far?).
Sik is pronounced as "seek", not as "sick".

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 » Thu May 02, 2019 5:59 am

I really don't have a clue how to emulate this.

The big mystery to me is, how do regular CPU reads and writes work? If it's really just the work RAM being slow, wouldn't it take just a touch too long to respond to regular reads as well? And even in the case of DMA, won't the address lines get incremented before the read completes? And won't that have very ... interesting side effects?

It feels like there are propagation delays between both the address *and* data lines in play here, that are different between the CPUs and the VDP. And ... no, not even I am willing to go that far. Bus conflict handling (eg SNES SA-1 stalls when the CPU accesses the same memory location), very basic bus hold delays, pipelining, and prefetch (cache) emulation are as deep into the weeds as I'll go.

I simply don't have the headroom to emulate things at a lower level than I already do. Computers (and/or my code) just aren't fast enough.

In any case, if someone wants a good emulator detection test case for a Sega CD emulator, perform a DMA transfer across the boundaries of word RAM. Eg 1ffffe-200002 or 3ffffe-400002. Let's hope the authors of Titan Overdrive CD don't see this post.

The best I can think of is to design a separate bus read function just for DMA, and everything will just fall back to the regular bus read, except for SVP and Sega CD word RAM, which will have a one-word latch. Another alternate approach would be a flag set in the emulator so you can check "IsVdpDma()" inside the word RAM read code to do the same latching effect.

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 » Thu May 02, 2019 6:14 am

byuu wrote:
Thu May 02, 2019 5:59 am
The big mystery to me is, how do regular CPU reads and writes work? If it's really just the work RAM being slow, wouldn't it take just a touch too long to respond to regular reads as well? And even in the case of DMA, won't the address lines get incremented before the read completes? And won't that have very ... interesting side effects?
Hint (of something I already said earlier): DMA has a complete disregard for doing bus accesses correctly. Whether it works correctly or not depends on how forgiving is the memory in question (the MD's work RAM clearly is, and most ROMs are fast enough too, but anything that really needs proper timings is going to screw up like this). At least that's my understanding.

You should be swearing not at the Mega CD here, but at DMA :​v It's not the only place where DMA is a massive pain in the ass (e.g. you have to ensure Z80 doesn't access the 68000 bus while doing DMA because if Z80 and VDP try to access at the same time the whole DMA transfer is broken on some revisions, and the whole "don't cross 128KB boundaries" problem, and from what I recall in some devkits DMA outright doesn't work at all from cartridge area, which is why Sonic 1 copies everything to RAM before doing a DMA)

Anyway, huh, if you want a single place to poke, when the destination address is written is probably what you're looking for. You'll still need to do it internally anyway since the source/length registers are modified during the transfer (they're used as counters as-is)… maybe your current approach is more foolproof :​P
byuu wrote:
Thu May 02, 2019 5:59 am
In any case, if someone wants a good emulator detection test case for a Sega CD emulator, perform a DMA transfer across the boundaries of word RAM. Eg 1ffffe-200002 or 3ffffe-400002. Let's hope the authors of Titan Overdrive CD don't see this post.
Just beware, trying to do a DMA from $1FFFFE to $200002 will result in it wrapping around back to $1E0000 because of the 128KB boundary issue and this is usually emulated since there are hacks and homebrew that break hard on real hardware for not paying attention to this.

EDIT: miscounted address digits
Last edited by Sik on Thu May 02, 2019 9:43 am, edited 1 time in total.
Sik is pronounced as "seek", not as "sick".

TmEE co.(TM)
Very interested
Posts: 2440
Joined: Tue Dec 05, 2006 1:37 pm
Location: Estonia, Rapla City
Contact:

Re: Questions on writing a new Mega CD emulator

Post by TmEE co.(TM) » Thu May 02, 2019 6:24 am

Top address byte of DMA transfer is a page register so to say, only the lower two bytes actually increment with each transfer, thus the 128KB pages where transfers take place.
Mida sa loed ? Nagunii aru ei saa ;)
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen

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 » Thu May 02, 2019 6:27 am

I really don't have a clue how to emulate this.
Looking at your load() function code, you could easily emulate this by using an alternate read() function to read from cpu space, something like this (also missing mega cd hardware condition check but you get the idea):

Code: Select all

  uint16 VDP::DMA::readbus(uint24 address) -> void { 
  uint16 data;
  if(address >= 0x200000 && address <= 0x3fffff)
  {
      // Word-RAM read-out latency
      data = cpu.databus;
      cpu.databus = cpu.read(1, 1, address);
  }
  else
  {
       data = cpu.databus = cpu.read(1, 1, address);
  }
  
  return data;
   
EDIT: oops, looks like I got ninjaed again
The big mystery to me is, how do regular CPU reads and writes work?
They wait for !DTACK to be asserted by gate-array (which presumably takes RAM access time in account) but probably VDP does not (hence why you can still access illegal areas with DMA without locking VDP).
And even in the case of DMA, won't the address lines get incremented before the read completes? And won't that have very ... interesting side effects?
Address lines will be changed for the next access, which does not occur immediately but at the next access slot (two pixels later at max rate) so likely the previous memory access is finished at that time (it's not THAT slow, just slow enough to miss VDP read-out time period) and Gate-Array is ready for next access.

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 » Thu May 02, 2019 8:55 am

In Gens i did it in a very hacky / simple way :
When DMA starts, If DMA source address comes from Word RAM (or CELL) region then i subtract 2 from the initial source address :D
Definitely hacky but working :D

notaz
Very interested
Posts: 193
Joined: Mon Feb 04, 2008 11:58 pm
Location: Lithuania

Re: Questions on writing a new Mega CD emulator

Post by notaz » Thu May 02, 2019 12:58 pm

Eke wrote:
Thu May 02, 2019 6:27 am
The big mystery to me is, how do regular CPU reads and writes work?
They wait for !DTACK to be asserted by gate-array (which presumably takes RAM access time in account) but probably VDP does not (hence why you can still access illegal areas with DMA without locking VDP).
Isn't /DTACK driven for the whole 0-800000 range by the MD's bus arbiter (or was it VDP)?
I don't think it's routed through the expansion port.

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 » Thu May 02, 2019 2:49 pm

Indeed you are right; my mistake, that would be on sub-CPU side only.

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Re: Questions on writing a new Mega CD emulator

Post by Nemesis » Fri May 03, 2019 5:16 am

While MegaCD emulation is still very much on my to-do list, I have compiled a fair few resources for it over the years. There's one largely untapped resource you should take a look at. I own a couple of these Cross Product MegaCD dev units: https://segaretro.org/SNASM_Mega-CD. This has as part of it an ISA card that fits into a PC, and emulates the CD hardware. For years, the software to drive this C-Trac CD emulation hardware was missing from the public domain. It surfaced more recently, but when hunting for it, I actually successfully tracked down the hardware designer that made that card, as well as the software developer that wrote the PC software for it. Although they didn't have the PC software saved, the hardware guy did send me a bunch of files. Here's the dump he sent me:
http://nemesis.exodusemulator.com/MegaD ... lation.tgz

It could be a useful reference, as this stuff was developed on contract for Sega, with access to their engineers and all the internal information that they needed to pull it off. There's some interesting stuff like the schematics for the C-Trac card, but the coolest thing is probably the source for the program that runs on the 6303 microprocessor on that card. There's some obviously useful info from first glance. Want to know all the CDC command and status codes? Here you go:

Code: Select all

;drive command nibbles

CMD_NOP         EQU                $0                            ;nop command
CMD_STOP        EQU                $1                            ;stop motor
CMD_REPORTREQ   EQU                $2                            ;change report type
CMD_READ        EQU                $3                            ;read ROM data
CMD_SEEK        EQU                $4                            ;seek to a given location
CMD_PAUSE       EQU                $6                            ;pause the drive
CMD_PLAY        EQU                $7                            ;start playing from current location
CMD_FWD         EQU                $8                            ;forward skip and playback
CMD_RVS         EQU                $9                            ;reverse skip and playback
CMD_TRACKSKIP   EQU                $A                            ;start track skipping
CMD_TRACKCUE    EQU                $B                            ;track cueing
CMD_DOORCLOSE   EQU                $C                            ;close the door
CMD_DOOROPEN    EQU                $D                            ;open the door

;drive status nibbles

STATUS_STOP     EQU                $0                            ;disc is set, motor not in operation
STATUS_PLAY     EQU                $1                            ;data or audio playback in progress
STATUS_SEEK     EQU                $2                            ;seek operation by read or seek command
STATUS_SCAN     EQU                $3                            ;skipping to a specific track
STATUS_PAUSE    EQU                $4                            ;paused at designated track or frame
STATUS_DOOROPEN EQU                $5                            ;the door mechanism is open
STATUS_SUMERROR EQU                $6                            ;a checksum error occurred during communication
STATUS_COMMANDERROR    EQU         $7                            ;an invalid command has arrived
STATUS_FUNCTIONERROR    EQU        $8                            ;some sort of error occurred during command execution
STATUS_TOCREAD  EQU                $9                            ;TOC is being read at the moment
STATUS_TRACKING EQU                $A                            ;currently skipping tracks
STATUS_NODISC   EQU                $B                            ;door is closed, but cannot focus on disc
STATUS_DISCEND  EQU                $C                            ;paused in the leadout
STATUS_DISCIN   EQU                $D                            ;paused in the leadin
STATUS_TRAYMOVING   EQU            $E                            ;the disc door mechanism is in operation
Take a look at the full microcontroller code though, as in it you have a complete, known working implementation of the low-level drive behaviour, with comments to boot. If you're not sure what you should do to implement a particular command, just take a look and see what they did.

Now I haven't gone down the rabbit hole of attempting MegaCD emulation myself yet, and I've done little more than browse through the information I've come across, but I suspect with the Sega developer manuals, the Sega service manuals, and the LC8951 docs I expect you already have, combined with this information, that will cover a lot of the bases when it comes to the basics of the CD control hardware itself.
Last edited by Nemesis on Fri May 03, 2019 5:46 am, edited 1 time in total.

Nemesis
Very interested
Posts: 791
Joined: Wed Nov 07, 2007 1:09 am
Location: Sydney, Australia

Re: Questions on writing a new Mega CD emulator

Post by Nemesis » Fri May 03, 2019 5:22 am

I should mention, I also know of two people who have a bunch of official docs, containing lots of technical bulletins that have never been scanned/released publicly before. I'm hoping to be able to get at least one of them to scan and share these documents at some point in the near future, as there could be valuable information contained in them that could help emulation. Also, I've spent some time working with the Pioneer LaserActive hardware, AKA, the "MegaLD" system. You'll be pleased to know that MegaLD hardware isn't that much of a stretch past the MegaCD itself, it's really just an extra set of hardware registers mapped in at another address, which I've mostly worked out already. If you get the MegaCD part solid, you'll be able to add support for the MegaLD system easily enough from there. Although getting complete good rips of the analog video is a work in progress, ripping the digital content is a solved problem. If you're making a serious push into this area, I'd recommend planning to go the whole way and support the MegaLD hardware.

EDIT: Here's a list of the MegaCD technical bulletins with descriptions that I currently know of, which we don't have:

Code: Select all

#2 - The attached document contains information regarding use of backup RAM on the Sega-CD
#3 - The attached document is a supplement to the sega-cd manual and contains notes nd updates on a variety of subjects
#4 - contains updates on basic sample playback frequency for the Sega -CD PCM sound chip
#5 - waiting during Vram reading and also ap roblem with the Super Target Development Systems
#6 - problems encountered while using Macintosh Sound Designer to produce Redbook audio
#7 - New standards for Sega-CD ID table
#16 - additional cd software development guidelins as well as updates to the CD-ROM Manual
#17 - contains info of sega approved write-once writers and discs
#18 - swapping PRG-RAM (program RAM) into the Genesis side by halding the SUB CPU the method is unsafe and unstable

Post Reply