Mocking up a Sega CD

Official support forum for the Exodus Emulation Platform

Moderator: Nemesis

Post Reply
DarkMorford
Interested
Posts: 15
Joined: Tue Mar 17, 2015 4:09 pm
Location: Redmond, WA

Mocking up a Sega CD

Post by DarkMorford » Tue Jul 28, 2015 2:04 am

I'm trying to "wire up" a system in Exodus to help with disassembling/analyzing the Sega CD BIOS. For now I'm just wanting to mock up the main parts; I'll worry about accuracy later.

Just to get started, I'm trying to hook up the 4 Mbit PRG-RAM, the two 1 Mbit Word RAM banks, and a fake set of Gate Array "registers" directly to the 68k. I've looked at the Mega Drive configurations that Exodus ships with, and given the current lack of documentation this is what I've got so far:

Code: Select all

<Module xmlns="http://www.exodusemulator.com/Schema/ExodusSchema.xsd" SystemClassName="SegaMegaCD" ModuleClassName="SegaMegaCD" ModuleInstanceName="MCD2" ModuleDisplayName="Mega-CD II" ManufacturerCode="SEGA" ManufacturerDisplayName="Sega" ProductionYear="1993">

	<!-- Devices -->
	<Device DeviceName="M68000" InstanceName="Sub 68000" />
	<Device DeviceName="RAM16Variable" InstanceName="PRG-RAM" MemoryEntryCount="0x40000" />
	<Device DeviceName="RAM16Variable" InstanceName="Word RAM 0" MemoryEntryCount="0x10000" />
	<Device DeviceName="RAM16Variable" InstanceName="Word RAM 1" MemoryEntryCount="0x10000" />
	
	<!-- "Dummy" devices until I can actually get things in -->
	<Device DeviceName="RAM8" InstanceName="Gate Array" MemoryEntryCount="0x200" />

	<!-- Clocks -->
	<ClockSource Name="CLK12M" ClockType="Direct" InitialValue="12500000" />

	<!-- Busses -->
	<BusInterface Name="SubCpuBus" AddressBusWidth="20" DataBusWidth="16" />
	
	<!-- Clock Mapping -->
	<BusInterface.MapClockSource BusInterfaceName="SubCpuBus" ClockSourceName="CLK12M" DeviceInstanceName="Sub 68000" TargetClockLine="CLK" />
	
	<!-- Device/Bus Mapping -->
	<BusInterface.MapDevice BusInterfaceName="SubCpuBus" DeviceInstanceName="PRG-RAM" InterfaceNumber="2" MemoryMapBase="0x0" MemoryMapSize="0x80000" />
	<BusInterface.MapDevice BusInterfaceName="SubCpuBus" DeviceInstanceName="Word RAM 0" InterfaceNumber="2" MemoryMapBase="0x80000" MemoryMapSize="0x20000" />
	<BusInterface.MapDevice BusInterfaceName="SubCpuBus" DeviceInstanceName="Word RAM 1" InterfaceNumber="2" MemoryMapBase="0xA0000" MemoryMapSize="0x20000" />
	
	<BusInterface.MapDevice BusInterfaceName="SubCpuBus" DeviceInstanceName="Gate Array" InterfaceNumber="1" MemoryMapBase="0xFF8000" MemoryMapSize="0x200" />
</Module>
This doesn't work in Exodus, though - it complains that "ValidateDevice failed for Sub 68000".

Also, I'd like to have a "dumb" ASIC to work with at some point. Again, accuracy is less important to me right now than just having something that acts right. I don't care about the cycle timing, I don't even really need the graphics processing - just something to keep the communication registers synced up and swap RAM banks between the two 68Ks as necessary. I've got a strong programming background, so I'd be fine with coding this up myself. I'm just not sure where to start.

So two questions, I guess. What do I need to get this super-basic version actually running, and how much time/effort am I looking at to implement a really stupid mock-up of the ASIC?

EDIT: I realized I forgot to attach the 68k to its bus. Added this line to the configuration and now it loads correctly in Exodus:

Code: Select all

<Device.ReferenceBus DeviceInstanceName="Sub 68000" BusInterfaceName="SubCpuBus" ReferenceName="BusInterface" />
But when I load in a memory snapshot with the sub-CPU program, it doesn't come in right; seems like it's only getting every other byte for some reason. Now I'm curious as to what all the other BusInterface.MapDevice options I'm seeing in the Mega Drive configs (CELineConditions, AddressLineFilter, etc.) do, and what the different InterfaceNumbers are for...

DarkMorford
Interested
Posts: 15
Joined: Tue Mar 17, 2015 4:09 pm
Location: Redmond, WA

Post by DarkMorford » Wed Jul 29, 2015 2:05 am

Okay, so I've started adding a few more bits and pieces to my module configuration to be hooked in later; the full file is over on pastebin (to prevent a huge copy-paste in here). I can load this into Exodus, load the RAM image with the sub-CPU programs, and for the most part it's doing what I expect at this stage.

I've got a couple minor issues with the memory, though. Here's the relevant bits:

Code: Select all

<Device DeviceName="RAM16Variable" InstanceName="PRG-RAM" MemoryEntryCount="0x40000" />
<Device DeviceName="RAM8" InstanceName="Gate Array" MemoryEntryCount="0x200" />

<BusInterface.MapDevice BusInterfaceName="SubCpuBus" DeviceInstanceName="PRG-RAM" InterfaceNumber="2" MemoryMapBase="0x0" MemoryMapSize="0x80000" AddressDiscardLowerBitCount="1" />
<BusInterface.MapDevice BusInterfaceName="SubCpuBus" DeviceInstanceName="Gate Array" MemoryMapBase="0xFF8000" MemoryMapSize="0x200" />
For the PRG-RAM segment, word and dword accesses seem to be working fine, but byte writes appear to go into both halves of the word they belong to. Similar problem with my "gate array"; it looks like only the even bytes get written to, even if an odd byte address is set. I've got a feeling it has to do with the CELineConditions attribute, but I don't even know what "CE" stands for, let alone what the different conditions mean. Any ideas?

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

Re: Mocking up a Sega CD

Post by Nemesis » Tue Sep 01, 2015 9:37 am

Really sorry I haven't gotten back to you DarkMorford, I hope this hasn't killed the interest you had in your attempt! I've had a lot of family issues come up in the last couple of months, and I've been doing a lot of travel for work. This hasn't really settled down yet, there's still a lot going on, and I've got a few big trips coming up (Chile in a couple of weeks, the US a month or so after that), but I'll try and check in here when I can.

I can't give a comprehensive answer to your question right now due to very little time, but I'll say a few points that I can expand on later if you need it. Basically, you need to look at schematics for this stuff where possible. If you track down the Mega Drive schematics, you'll find a lot of the line mappings and names basically mirror the physical connections in the real system. I can't remember off the top of my head if schematics/service manuals have been leaked or reverse-engineered for the MegaCD, but that would be a place to start if they exist. In terms of what a "CE" line is, it's basically a slightly artificial distinction that currently exists in Exodus between lines that can trigger changes in other devices, and lines that can affect what devices respond to bus operations. This distinction will completely disappear when I complete the bus overhaul in a later version, at which point there'll simply be "lines", and a new concept of "triggers" which will support all concepts.

Any way, for your specific issue, in the Mega Drive, you've got a funny case for ram and the M68000. First of all, understand that the M68000 has a 16-bit data bus, and therefore has 16 data lines. Although you can do 8, 16, and 32 bit access from a programming point of view, from a hardware point of view, this is made up of one or more 16-bit operations. There are a bunch of other control lines that get asserted when an operation is being performed, which inform other devices of what kind of access is occurring (read/write), and whether the upper, lower, or both 8-bit halves of the data bus are being used. If you do an 8-bi write in code, what actually happens is a 16-bit write occurs, with the 8-bit data mirrored in the upper and lower halves of the data bus, and devices must use the other control lines to handle 8-bit writes if they need to. On to the RAM, in the real system, you've got two separate chips for RAM, one for the upper byte, and one for the lower byte. When you do a single 16-bit read, BOTH chips actually respond, writing in their corresponding halves. When you do a write, either one, or both chips respond, depending on the state asserted by some of those magic "CE" lines. In complex systems like the Mega Drive, there are often external chips, sometimes called "Bus Arbiters" or the like, that provide additional control lines or somehow manage the interaction between devices. In the Mega Drive, several chips participate, with the VDP asserting a number of additional lines, the write strobe lines for the RAM chips being two of them.

In Exodus, I consider these two separate devices to be logically one device. Even though they're physically separate in the system, they act as one device as far as I'm concerned. It's also much nicer from a debugging point of view to view this memory as one continuous set, rather than split between two chips. To that end, I define the RAM as a 16-bit RAM device, and through the mappings, manage the concept of 8-bit writes separately from 16-bit reads and writes. In the Mega CD, if 8-bit writes are handled correctly to the program RAM (are they? Have you tested?), then that must be done through these line mappings. I don't have the docs handy or the time to look them up, but look for the upper and lower strobe lines for the data bus from the M68000, they're there somewhere. You need to use these lines to set up CE line conditions on your mappings, so that you can handle the 8-bit writes separately from the 16-bit reads/writes.

DarkMorford
Interested
Posts: 15
Joined: Tue Mar 17, 2015 4:09 pm
Location: Redmond, WA

Re: Mocking up a Sega CD

Post by DarkMorford » Fri Nov 13, 2015 11:57 pm

Thanks, Nemesis, that makes a lot of sense. I do have schematics for the Sega CD floating around somewhere, I just need to find a place that'll print them full-size (A3 paper) so I have that for reference.

As I mentioned in my original post, I'd like to make a "dumb" (i.e., not 100% accurate) 315-5548 ASIC/"gate array" module for Exodus for testing, as that would be much easier than trying to follow everything by hand or having two instances of Easy68K running. (And maybe as a base for a more accurate module later on.) There doesn't seem to be a whole lot of documentation yet on how to write plugins, though, so I was hoping you could give me sort of a crash course to get started, and I can try to work from there.

I'm thinking about starting with just sort of a "dummy" chip that sits between the 68K and the PRG-RAM and just passes the data transparently back and forth, but that in itself presents an interesting issue. Looking at the block diagram and schematic, it appears there are two address buses involved—one that connects the ASIC to PRG-RAM and the CDC; the other connects the 68K, ASIC, backup SRAM, and PCM generator—that share the same data bus. How would something like this be modeled in the module XML file?

Thanks!

EDIT: Uploaded a portion of the block diagram to better clarify that last question. The two address buses are highlighted in red and blue, the "shared" data bus is in yellow.

DarkMorford
Interested
Posts: 15
Joined: Tue Mar 17, 2015 4:09 pm
Location: Redmond, WA

Re: Mocking up a Sega CD

Post by DarkMorford » Fri Jul 01, 2016 3:44 pm

I'm still interested in working on this for my own use and possibly contributing more formally to Exodus in the future to get Sega CD support working. I'm just not entirely sure where to start on it—do I start with the XML module file and then code the chips to fit that? Just start jumping in to the chip code? A different method altogether? Any guidance here would be greatly appreciated.

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

Re: Mocking up a Sega CD

Post by Nemesis » Sat Jul 09, 2016 7:06 pm

I'd start with what's simplest, and expand up from there. In your case, the MegaCD gate array exposes itself to the sub-cpu in a 0x200 byte block at 0xFF8000, and to the main CPU in a 0x30 byte block at 0xA12000. To get going, I would just create a new device which has handlers for read and write access, and map it using two different "interface" numbers to those bus addresses. For example:

Code: Select all

<Device DeviceName="MegaCDGateArray" InstanceName="Gate Array"/>

<BusInterface.MapDevice BusInterfaceName="M68kBus" DeviceInstanceName="Gate Array" MemoryMapBase="0xA12000" MemoryMapSize="0x30" InterfaceNumber="1" />
<BusInterface.MapDevice BusInterfaceName="SubCpuBus" DeviceInstanceName="Gate Array" MemoryMapBase="0xFF8000" MemoryMapSize="0x200" InterfaceNumber="2" />
When you handle the subcode region properly this should really be split off into a separate interface block, but there's no need to start with that, you can just map the entire 0x200 byte subcpu region in one block to get going. Your device itself can be extremely simple. You basically would just need to create a new class which derives from the "Device" base class, and implements the ReadInterface/WriteInterface methods. This would allow you to potentially respond to reads and writes to these mapped regions, with the provided interface number allowing you to distinguish between access from the main and sub CPU. You can refer to existing devices for examples on this. The "MDBusArbiter" device is an example of a device with a complex bus interface that bridges buses and has many interface blocks. The "YM2612" device is an example of a device with a basic bus interface that just responds to reads and writes over a single interface block.

Post Reply