Questions on writing a new Mega CD emulator
Moderator: Mask of Destiny
Re: Questions on writing a new Mega CD emulator
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".
Re: Questions on writing a new Mega CD emulator
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.
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.
Re: Questions on writing a new Mega CD emulator
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
…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)
I'll try to see where in the docs this is mentioned (and exactly what the bug is)
EDIT: bingo
…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".
Re: Questions on writing a new Mega CD emulator
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.I see that GPGX is caching some stuff when starting a new block render
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.
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.An odd thing with GPGX is the initial bufferStart, which adds reg[0x60]&0x3f to it (the image offset) as a pixel displacement.
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.Each tile is always 64 bytes, which is 16x16 pixels worth of data, seemingly unaffected by the stamp tile size bit.
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 guess it's odd that it's ((bufferIndex&7)!=7) instead of something like
Well, not this timeI'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.
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
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
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.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.
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.
Re: Questions on writing a new Mega CD emulator
That was it, thank you Sik and Eke!
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]
Well anyway ... on to CD emulation. Pray for me ._.
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;
}
}
Re: Questions on writing a new Mega CD emulator
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).byuu wrote: ↑Thu May 02, 2019 4:49 amMan, 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.
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".
Re: Questions on writing a new Mega CD emulator
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.
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.
Re: Questions on writing a new Mega CD emulator
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.byuu wrote: ↑Thu May 02, 2019 5:59 amThe 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?
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
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".
-
- Very interested
- Posts: 2443
- Joined: Tue Dec 05, 2006 1:37 pm
- Location: Estonia, Rapla City
- Contact:
Re: Questions on writing a new Mega CD emulator
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
http://www.tmeeco.eu
Files of all broken links and images of mine are found here : http://www.tmeeco.eu/FileDen
Re: Questions on writing a new Mega CD emulator
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):I really don't have a clue how to emulate this.
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;
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).The big mystery to me is, how do regular CPU reads and writes work?
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.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?
-
- 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
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
Definitely hacky but working
When DMA starts, If DMA source address comes from Word RAM (or CELL) region then i subtract 2 from the initial source address
Definitely hacky but working
Re: Questions on writing a new Mega CD emulator
Isn't /DTACK driven for the whole 0-800000 range by the MD's bus arbiter (or was it VDP)?Eke wrote: ↑Thu May 02, 2019 6:27 amThey 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).The big mystery to me is, how do regular CPU reads and writes work?
I don't think it's routed through the expansion port.
Re: Questions on writing a new Mega CD emulator
Indeed you are right; my mistake, that would be on sub-CPU side only.
Re: Questions on writing a new Mega CD emulator
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:
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.
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
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.
Re: Questions on writing a new Mega CD emulator
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:
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