Remove Genesis.cs core

This commit is contained in:
adelikat 2017-05-01 16:27:40 -05:00
parent 621a533dde
commit ffd743cac7
15 changed files with 8 additions and 2641 deletions

View File

@ -28,7 +28,6 @@ namespace BizHawk.Client.EmuHawk
ControllerImages.Add("Dual Gameboy Controller", Properties.Resources.GBController);
ControllerImages.Add("SMS Controller", Properties.Resources.SMSController);
ControllerImages.Add("Genesis 3-Button Controller", Properties.Resources.GENController);
ControllerImages.Add("GPGX Genesis Controller", Properties.Resources.GENController);
ControllerImages.Add("Saturn Controller", Properties.Resources.SaturnController);

View File

@ -512,28 +512,28 @@
<Compile Include="Consoles\Nintendo\GBA\LibVBANext.cs" />
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.cs" />
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.IDebuggable.cs">
<DependentUpon>MGBAHawk.cs</DependentUpon>
<DependentUpon>MGBAHawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.IInputPollable.cs">
<DependentUpon>MGBAHawk.cs</DependentUpon>
<DependentUpon>MGBAHawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.IMemoryDomains.cs">
<DependentUpon>MGBAHawk.cs</DependentUpon>
<DependentUpon>MGBAHawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.ISaveRam.cs">
<DependentUpon>MGBAHawk.cs</DependentUpon>
<DependentUpon>MGBAHawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.ISettable.cs">
<DependentUpon>MGBAHawk.cs</DependentUpon>
<DependentUpon>MGBAHawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.ISoundProvider.cs">
<DependentUpon>MGBAHawk.cs</DependentUpon>
<DependentUpon>MGBAHawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.IStatable.cs">
<DependentUpon>MGBAHawk.cs</DependentUpon>
<DependentUpon>MGBAHawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\MGBAHawk.IVideoProvider.cs">
<DependentUpon>MGBAHawk.cs</DependentUpon>
<DependentUpon>MGBAHawk.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\VBANext.cs" />
<Compile Include="Consoles\Nintendo\GBA\VBANext.IDebuggable.cs">
@ -1001,12 +1001,6 @@
</Compile>
<Compile Include="Consoles\PC Engine\PCEngine.TurboCD.cs" />
<Compile Include="Consoles\PC Engine\ScsiCDBus.cs" />
<Compile Include="Consoles\Sega\Genesis\Cart\EEPROM.cs" />
<Compile Include="Consoles\Sega\Genesis\Cart\RomHeader.cs" />
<Compile Include="Consoles\Sega\Genesis\Cart\SaveRAM.cs" />
<Compile Include="Consoles\Sega\Genesis\Genesis.Input.cs" />
<Compile Include="Consoles\Sega\Genesis\Genesis.IO.cs" />
<Compile Include="Consoles\Sega\Genesis\Native68000\Musashi.cs" />
<Compile Include="Consoles\Sega\gpgx64\GPGX.ISoundProvider.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
@ -1259,12 +1253,6 @@
<Compile Include="Libretro\LibretroCore_InputCallbacks.cs" />
<Compile Include="MemoryBlock.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Consoles\Sega\Genesis\Genesis.cs" />
<Compile Include="Consoles\Sega\Genesis\GenVDP.cs" />
<Compile Include="Consoles\Sega\Genesis\GenVDP.DMA.cs" />
<Compile Include="Consoles\Sega\Genesis\GenVDP.Render.cs" />
<Compile Include="Consoles\Sega\Genesis\MemoryMap.68000.cs" />
<Compile Include="Consoles\Sega\Genesis\MemoryMap.Z80.cs" />
<Compile Include="Sound\CDAudio.cs" />
<Compile Include="Sound\HuC6280PSG.cs" />
<Compile Include="Sound\IAsyncSoundProvider.cs" />
@ -1282,7 +1270,6 @@
<Content Include="Consoles\Nintendo\NES\Docs\sunsoft.txt" />
<Content Include="Consoles\Nintendo\NES\Docs\sxrom_notes.txt" />
<Content Include="Consoles\PC Engine\Compat.txt" />
<Content Include="Consoles\Sega\Genesis\Compat.txt" />
<Content Include="Consoles\Sega\SMS\Compat.txt" />
<Content Include="Notes.txt" />
</ItemGroup>

View File

@ -1,67 +0,0 @@
using System;
using System.Globalization;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
partial class Genesis
{
// State
bool EepromEnabled;
int EepromSize;
int EepromAddrMask;
int SdaInAddr, SdaInBit;
int SdaOutAddr, SdaOutBit;
int SclAddr, SclBit;
int SdaInCurrValue, SclCurrValue;
// Code
void InitializeEeprom(GameInfo game)
{
if (game["EEPROM"] == false)
return;
EepromEnabled = true;
EepromAddrMask = game.GetHexValue("EEPROM_ADDR_MASK");
EepromSize = EepromAddrMask + 1;
var t = game.OptionValue("SDA_IN").Split(':');
SdaInAddr = int.Parse(t[0], NumberStyles.HexNumber);
SdaInBit = int.Parse(t[1]);
t = game.OptionValue("SDA_OUT").Split(':');
SdaOutAddr = int.Parse(t[0], NumberStyles.HexNumber);
SdaOutBit = int.Parse(t[1]);
t = game.OptionValue("SCL").Split(':');
SclAddr = int.Parse(t[0], NumberStyles.HexNumber);
SclBit = int.Parse(t[1]);
SaveRAM = new byte[EepromSize];
Console.WriteLine("EEPROM enabled. Size: ${0:X} SDA_IN: ${1:X}:{2} SDA_OUT: ${3:X}:{4}, SCL: ${5:X}:{6}",
EepromSize, SdaInAddr, SdaInBit, SdaOutAddr, SdaOutBit, SclAddr, SclBit);
}
void WriteByteEeprom(int address, byte value)
{
if (address == SdaInAddr)
{
SdaInCurrValue = (value >> SdaInBit) & 1;
Console.WriteLine("SDA_IN: {0}", SdaInCurrValue);
}
if (address == SclAddr)
{
SclCurrValue = (value >> SclBit) & 1;
Console.WriteLine("SCL: {0}", SclCurrValue);
}
// todo: logic!
}
}
}

View File

@ -1,61 +0,0 @@
using System;
using System.Text;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
partial class Genesis
{
public string RH_Console { get { return GetRomString(0x100, 0x10); } }
public string RH_Copyright { get { return GetRomString(0x110, 0x10); } }
public string RH_NameDomestic { get { return GetRomString(0x120, 0x30); } }
public string RH_NameExport { get { return GetRomString(0x150, 0x30); } }
public int RH_RomSize { get { return GetRomLongWord(0x1A4); } }
public string RH_Region { get { return GetRomString(0x1F0, 3); } }
public bool RH_SRamPresent { get { return (RomData[0x1B2] & 0x40) != 0; } }
public int RH_SRamCode { get { return (RomData[0x1B2] >> 3) & 3; } }
public int RH_SRamStart { get { return GetRomLongWord(0x1B4); } }
public int RH_SRamEnd { get { return GetRomLongWord(0x1B8); } }
public string RH_SRamInterpretation()
{
switch (RH_SRamCode)
{
case 0: return "Even and odd addresses";
case 2: return "Even addresses";
case 3: return "Odd addresses";
default: return "Invalid type";
}
}
string GetRomString(int offset, int len)
{
return Encoding.ASCII.GetString(RomData, offset, len).Trim();
}
int GetRomLongWord(int offset)
{
return (RomData[offset] << 24) | (RomData[offset + 1] << 16) | (RomData[offset + 2] << 8) | RomData[offset + 3];
}
void LogCartInfo()
{
Console.WriteLine("==================");
Console.WriteLine("ROM Cartridge Data");
Console.WriteLine("==================");
Console.WriteLine("System: {0}", RH_Console);
Console.WriteLine("Copyright: {0}", RH_Copyright);
Console.WriteLine("Name (Dom): {0}", RH_NameDomestic);
Console.WriteLine("Name (Exp): {0}", RH_NameExport);
Console.WriteLine("Region: {0}", RH_Region);
Console.WriteLine("Rom Size: {0,7} (${0:X})", RH_RomSize);
Console.WriteLine("SRAM Used: {0}", RH_SRamPresent);
if (RH_SRamPresent)
{
Console.WriteLine("SRAM Start: {0,7} (${0:X})", RH_SRamStart);
Console.WriteLine("SRAM End: {0,7} (${0:X})", RH_SRamEnd);
Console.WriteLine("SRAM Type: {0}", RH_SRamInterpretation());
}
}
}
}

View File

@ -1,49 +0,0 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
partial class Genesis
{
bool SaveRamEnabled;
bool SaveRamEveryOtherByte;
int SaveRamStartOffset;
int SaveRamEndOffset;
int SaveRamLength;
byte[] SaveRAM = new byte[0];
void InitializeSaveRam(GameInfo game)
{
if (EepromEnabled)
return;
if (game["DisableSaveRam"] || RH_SRamPresent == false)
return;
SaveRamEnabled = true;
SaveRamEveryOtherByte = RH_SRamCode != 0;
SaveRamStartOffset = RH_SRamStart;
SaveRamEndOffset = RH_SRamEnd;
if (game["SaveRamStartOffset"])
SaveRamStartOffset = game.GetHexValue("SaveRamStartOffset");
if (game["SaveRamEndOffset"])
SaveRamEndOffset = game.GetHexValue("SaveRamEndOffset");
SaveRamLength = (SaveRamEndOffset - SaveRamStartOffset) + 1;
if (SaveRamEveryOtherByte)
SaveRamLength = ((SaveRamEndOffset - SaveRamStartOffset) / 2) + 1;
SaveRAM = new byte[SaveRamLength];
Console.WriteLine("SaveRAM enabled. Start: ${0:X6} End: ${1:X6} Length: ${2:X} Mode: {3}", SaveRamStartOffset, SaveRamEndOffset, SaveRamLength, RH_SRamInterpretation());
}
public byte[] CloneSaveRam() { return (byte[])SaveRAM.Clone(); }
public void StoreSaveRam(byte[] data) { Array.Copy(data, SaveRAM, data.Length); }
public bool SaveRamModified { get; private set; }
}
}

View File

@ -1,156 +0,0 @@
68000:
Timings:
- MULU/MULS/DIVU/DIVS have funky timings.
- How many cycles does TRAP take to execute?
- How many cycles does it take to accept an interrupt?
- AND has some funky timings when it comes to immediates?
GAMES:
Ahhh! Real monsters - no sound
Adventures of Mighty Max - crashes memory mapper
Air Diver - suffer from lack of 2cell vscroll
Alien Storm.... gfx glitches. control glitches?
Altered Beast: start with 0 health, 0 lives???
Another World .. broken.. (E) tho
Arcus Odyssey does UNHANDLED Z80 READs... is this a problem?
Awesome Possum - controls are wrong
Batman - flicker and music is messed
Batman Forever - significant gfx issues
Battle Squadron - crashes memory mapper woo!
Blood Shot - FPS game - some texture corruption
Bonkers - substantial gfx corruption
Buck Rogers crashes in a fun way
Burning Force - some gfx issues. works way better than it has in the past though!
Cheese Cat-astrophe - crashes renderer!!
Chester Cheetah - freezes when starting new game
Chuck Rock - Music messed up
Contra Hard Corps: Scrolling is messed up in level 1... used to work.
Dashin' Desperados .. bottom screen messed up
Death Duel crashes my renderer... (!)
Decap Attack - Item select screen messed up
Double Dragon doesn't boot
Devilish/Bad Omen - intro messed up.... interesting debug target
Dune... freezes in intro
Exile - Immediate gfx issues... Debug target.
F1 World Championship... Gfx issue at bottom of screen... quite cool working game though!
Fatal Rewind - appears to do a read.w on FFFFFF... that would be an address error. read.l messes up too. Ergo: basically doesnt work.
Final Blow - Music messed up
Fire Shark - Messed up sound
Flavio's Raster FX Test.. doesnt work
Foreman for Real doent boot
Galaxy Force II - gfx issue in level select screen, and in level-end "shoot the core" part
Gargoyles... gameplay is nonfunctional
Gauntlet 4 .. title screen messed. gfx corruption. immediately - debug target.
Golden Axe - controls are jacked up
Golden Axe 3 intro.... weirder than before for sure
Grind Stormer
Herzog Zwei .. doesnt boot fully
Insector X .. title screen gfx issue
James Pond 3 crash in intro
Jim Power - gfx glitches
Jurassic Park 2 - crashes in intro
Lemmings: Sound is royally effed... gfx glitches.
Marvel Land .. holy shit thats psychadelic
Mega Turrican some gfx glitches
Mortal Kombat...
MUSHA: Intro music starts too soon. Suffers from lack of 2-cell-vertical-scroll mode.
Out of This World... pretty substantially broken
Outrun 2019, much more significant gfx issues.
Outrun, minor gfx issue. maybe related to h-int timing?
Outrunners, some gfx issues... but not as bad as you might think!!! apparently doesnt use interlace mode?
Panorama Cotton still not working right
Power Monger messed up
RamboIII - intro gfx corrupted - MAYBE GOOD DEBUGGING TARGET
Shining in the Darkness: Check out sprites in the tavern... very odd
Sonic 2: Aside from lack of interlace mode, the shadows in the special stage are white....?
Sonic 2: Something still amiss with title screen sprite masking
Sonic 3 serious gfx glitches
Star Control - Shit gets crazy
Steel Empire - controls messed up. probably gfx issues also.
Sub-Terrania some gfx issues in intro
Super Hang-On - Sprite priority/masking isnt happening the way its supposed to on the tracks.
Super Fantasy Zone: Sound totally boned, missing graphics
TaleSpin - gfx glitches
The Humans
The Immortal
Truxton - Sound is jaaaacked.
Verytex - gfx issues
Zero Tolerance - gfx bugs that didnt used to happen :(
Zombies At My Neighbors: doesnt boot really
Zoop doesnt boot
======================================================
Fixed Issues: (listed for regression testing purposes)
======================================================
(Sprites X/Y are 10-bit, but must be masked to 9-bit)
- Dragon's Revenge.... ball sprite seems missing? of all the sprites to not show up...
- Fire Shark - Sprites dont render... VERY similar to Truxton. Same engine?
- Truxton - Sprites do not appear to be rendering.
- Zero Wing - Sprites arent rendering.......
Flavio's DMA test... DMAs when it shouldnt!! - Masking off too much of the VDP command code
Fun-n-Games fails its fadeouts. -- Fixed CRAM reads. I failed math.
Sonic Spinball executes a VSRAM read -- Implemented VSRAM reads.
Bugs related to longword read/write VRAM. Multiple bugs were present including sign-extension and endians and crap.
- Crusader of Centy- Text boxes messed up
- Eternal Champions - immediate gfx corruption
- Garfield... immediate gfx corruption. debug target.
- Kid Chameleon - gfx corruption on bounce bricks and level-end effect
Games to test window calculation on:
- Road Blasters (doesnt use window, but Window & Nametable A are at same location)
- Out of this World - Same as Road Blasters
- Musha,Gaiares - Window at top
- Eliminate Down - Window at bottom
- Monster World 4, Wonder Boy in Monster World - Window at top - needs window scroll plane size adjustment
- D&D Warriors of the Eternal Sun - Window at bottom and at sides
- Truxton, Fire Shark - Window on Right
======================================================
TODO: non-instant DMA emulation
TODO: Add 68000/VDP interrupt enable delay (one instruction, similar to After Burner/PCE)
TODO: H-Ints timing possibly not correct... Some game raster effects work, others don't work as expected. (could be HVC tho)
TODO: Test DMA/ VDP command words.... I'm not at all convinced that VRAM is always correct
==============
Notable games:
==============
Ghouls n Ghosts sets up the graphics planes backwards from normal, by setting the plane A to be low priority and Plane B to be high priority.
If you have a bug in your priority code this may find it.
Revenge of Shinobi will not play DAC sounds if YM2612 registers are not initialized to L/R channels enabled.
Ballz doesnt really initialize hardly any VDP registers, relies on VDP registers powered-on to the correct values
Contra appears to use VDP A0 set = byte-swap. Not sure if its important in anyway in that game, but the byte swap happens.
Games that use VRAM->VRAM Copy: D&D Warriors of the Eternal Sun, MUSHA, Devilish, Viewpoint
Games that require accurate VRAM fill emulation include Thunder Force IV,
Contra Hard Corps, Revenge of Shinobi, Taiga Drama, and Sword of Vermillion.
Sonic Spinball reads from VSRAM
Games to test for sprite masking/overflow:
- Sonic 1 title screen (uses overflow to mask)
- Sonic 2 title screen (uses sprite mask modes)
- Galaxy Force 2 level select (uses sprite mask)
- Landstalker sprite masking (chests and such)
- Nemesis test rom
Games known to use 2-cell vertical scroll mode:
- Air Diver
- Exo Squad
- Contra Hard Corps (giant robot boss on stage 1)
- MUSHA (stage 3)

View File

@ -1,112 +0,0 @@
using System;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
public partial class GenVDP
{
// TODO: make this a requirement of constructor?
public Func<int, short> DmaReadFrom68000; // TODO make ushort
public int DmaLength { get { return Registers[19] | (Registers[20] << 8); } }
public int DmaMode { get { return (Registers[23] >> 6) & 3; } }
public int DmaSource
{
get
{
if ((Registers[23] & 0x80) == 0) // 68000 -> VRAM copy mode
return ((Registers[21] << 1) | (Registers[22] << 9) | (Registers[23] << 17)) & 0xFFFFFE;
// Else VRAM/VRAM copy mode
return (Registers[21] | (Registers[22] << 8)) & 0xFFFFFE;
}
}
bool DmaFillModePending;
void ExecuteVramFill(ushort data)
{
if (data != 0)
Console.WriteLine("fill word is not zero {0:X4}", data);
Log.Note("VDP", "DMA FILL REQD, WRITE {0:X4}, {1:X4} times, at {2:X4}", data, DmaLength, VdpDataAddr);
// TODO: It should spread this out, not do it all at once.
int length = DmaLength;
if (length == 0)
length = 0x10000;
byte fillByte = (byte)(data >> 8);
do
{
VRAM[VdpDataAddr] = fillByte;
Log.Note("VDP", "VRAM DMA FILL Write: [{0:X4}] = {1:X2}", VdpDataAddr, fillByte);
UpdatePatternBuffer(VdpDataAddr);
VdpDataAddr += Registers[15];
} while (--length > 0);
// TOOD: test if the length register updated? One would assume so...
Registers[19] = 0;
Registers[20] = 0;
// TODO: Source registers should be incremented also (even for Fill)
DmaFillModePending = false;
}
void Execute68000VramCopy()
{
Log.Note("VDP", "DMA 68000 -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);
int length = DmaLength;
if (length == 0)
length = 0x10000;
int source = DmaSource;
do
{
ushort value = (ushort)DmaReadFrom68000(source);
source += 2;
// TODO funky source behavior
WriteVdpData(value);
} while (--length > 0);
Registers[19] = 0;
Registers[20] = 0;
// TODO: update DMA source registers.
// TODO: find correct number of 68k cycles to burn
}
void ExecuteVramVramCopy()
{
Log.Note("VDP", "DMA VRAM -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);
int length = DmaLength;
if (length == 0)
length = 0x10000;
int source = DmaSource;
do
{
byte data = VRAM[source];
VRAM[VdpDataAddr] = data;
UpdatePatternBuffer(VdpDataAddr);
Log.Note("VDP", "VRAM/VRAM Copy VRAM[{0:X4}] = {1:X2}", VdpDataAddr, data);
source = (source + 1) & 0xFFFF;
VdpDataAddr += Registers[0xF];
} while (--length > 0);
Registers[19] = 0;
Registers[20] = 0;
// TODO: length, source registers should be updated....
// TODO: find correct number of 68k cycles to burn
}
}
}

View File

@ -1,525 +0,0 @@
using System;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
public partial class GenVDP
{
// Priority buffer contents have the following values:
// 0 = Backdrop color
// 1 = Plane B Low Priority
// 2 = Plane A Low Priority
// 4 = Plane B High Priority
// 5 = Plane A High Priority
// 9 = Sprite has been drawn
byte[] PriorityBuffer = new byte[320];
static readonly byte[] PalXlatTable = { 0, 0, 36, 36, 73, 73, 109, 109, 145, 145, 182, 182, 219, 219, 255, 255 };
public void RenderLine()
{
if (DisplayEnabled)
{
Array.Clear(PriorityBuffer, 0, 320);
// TODO: I would like to be able to render Scroll A before Scroll B, in order to minimize overdraw.
// But at the moment it complicates priority stuff.
if (CellBasedVertScroll == false)
{
RenderScrollB();
RenderScrollA();
}
else
{
RenderScrollBTwoCellVScroll();
RenderScrollATwoCellVScroll();
}
RenderSpritesScanline();
}
else
{
// If display is disabled, fill in with background color.
for (int i = 0; i < FrameWidth; i++)
FrameBuffer[(ScanLine * FrameWidth) + i] = BackgroundColor;
}
//if (ScanLine == 223) // shrug
// RenderPalette();
}
void RenderPalette()
{
for (int p = 0; p < 4; p++)
for (int i = 0; i < 16; i++)
FrameBuffer[(p * FrameWidth) + i] = Palette[(p * 16) + i];
}
void RenderScrollAScanline(int xScroll, int yScroll, int nameTableBase, int startPixel, int endPixel, bool window)
{
const int lowPriority = 2;
const int highPriority = 5;
int yTile = ((ScanLine + yScroll) / 8) % NameTableHeight;
int nameTableWidth = NameTableWidth;
if (window)
nameTableWidth = (DisplayWidth == 40) ? 64 : 32;
// this is hellllla slow. but not optimizing until we implement & understand
// all scrolling modes, shadow & hilight, etc.
// in thinking about this, you could convince me to optimize the PCE background renderer now.
// Its way simple in comparison. But the PCE sprite renderer is way worse than gen.
for (int x = startPixel; x < endPixel; x++)
{
int xTile = Math.Abs(((x + (1024 - xScroll)) / 8) % nameTableWidth);
int xOfs = Math.Abs((x + (1024 - xScroll)) & 7);
int yOfs = (ScanLine + yScroll) % 8;
int cellOfs = nameTableBase + (yTile * nameTableWidth * 2) + (xTile * 2);
int nameTableEntry = VRAM[cellOfs] | (VRAM[cellOfs + 1] << 8);
int patternNo = nameTableEntry & 0x7FF;
bool hFlip = ((nameTableEntry >> 11) & 1) != 0;
bool vFlip = ((nameTableEntry >> 12) & 1) != 0;
bool priority = ((nameTableEntry >> 15) & 1) != 0;
int palette = (nameTableEntry >> 13) & 3;
if (priority && PriorityBuffer[x] >= highPriority) continue;
if (!priority && PriorityBuffer[x] >= lowPriority) continue;
if (vFlip) yOfs = 7 - yOfs;
if (hFlip) xOfs = 7 - xOfs;
int texel = PatternBuffer[(patternNo * 64) + (yOfs * 8) + (xOfs)];
if (texel == 0) continue;
int pixel = Palette[(palette * 16) + texel];
FrameBuffer[(ScanLine * FrameWidth) + x] = pixel;
PriorityBuffer[x] = (byte)(priority ? highPriority : lowPriority);
}
}
void RenderScrollAScanlineTwoCellVScroll(int xScroll, int nameTableBase, int startPixel, int endPixel, bool window)
{
const int lowPriority = 2;
const int highPriority = 5;
int fineHScroll = xScroll & 15;
int nameTableWidth = NameTableWidth;
if (window)
nameTableWidth = (DisplayWidth == 40) ? 64 : 32;
for (int x = startPixel; x < endPixel; x++)
{
int vsramUnitOffset = ((x - fineHScroll) / 16) % 40;
int yScroll = VSRAM[vsramUnitOffset * 2] & 0x3FF;
int yTile = ((ScanLine + yScroll) / 8) % NameTableHeight;
int xTile = Math.Abs(((x + (1024 - xScroll)) / 8) % nameTableWidth);
int xOfs = Math.Abs((x + (1024 - xScroll)) & 7);
int yOfs = (ScanLine + yScroll) % 8;
int cellOfs = nameTableBase + (yTile * nameTableWidth * 2) + (xTile * 2);
int nameTableEntry = VRAM[cellOfs] | (VRAM[cellOfs + 1] << 8);
int patternNo = nameTableEntry & 0x7FF;
bool hFlip = ((nameTableEntry >> 11) & 1) != 0;
bool vFlip = ((nameTableEntry >> 12) & 1) != 0;
bool priority = ((nameTableEntry >> 15) & 1) != 0;
int palette = (nameTableEntry >> 13) & 3;
if (priority && PriorityBuffer[x] >= highPriority) continue;
if (!priority && PriorityBuffer[x] >= lowPriority) continue;
if (vFlip) yOfs = 7 - yOfs;
if (hFlip) xOfs = 7 - xOfs;
int texel = PatternBuffer[(patternNo * 64) + (yOfs * 8) + (xOfs)];
if (texel == 0) continue;
int pixel = Palette[(palette * 16) + texel];
FrameBuffer[(ScanLine * FrameWidth) + x] = pixel;
PriorityBuffer[x] = (byte)(priority ? highPriority : lowPriority);
}
}
void CalculateWindowScanlines(out int startScanline, out int endScanline)
{
int data = Registers[0x12];
int windowVPosition = data & 31;
bool fromTop = (data & 0x80) == 0;
if (windowVPosition == 0)
{
startScanline = -1;
endScanline = -1;
return;
}
if (fromTop)
{
startScanline = 0;
endScanline = (windowVPosition * 8);
}
else
{
startScanline = windowVPosition * 8;
endScanline = FrameHeight;
}
}
void CalculateWindowPosition(out int startPixel, out int endPixel)
{
int data = Registers[0x11];
int windowHPosition = (data & 31) * 2; // Window H position is set in 2-cell increments
bool fromLeft = (data & 0x80) == 0;
if (windowHPosition == 0)
{
startPixel = -1;
endPixel = -1;
return;
}
if (fromLeft)
{
startPixel = 0;
endPixel = (windowHPosition * 8);
if (endPixel > FrameWidth)
endPixel = FrameWidth;
}
else
{
startPixel = windowHPosition * 8;
endPixel = FrameWidth;
if (startPixel > FrameWidth)
{
startPixel = -1;
endPixel = -1;
}
}
}
void RenderScrollA()
{
// Calculate scroll offsets
int hscroll = CalcHScrollPlaneA(ScanLine);
int vscroll = VSRAM[0] & 0x3FF;
// Calculate window dimensions
int startWindowScanline, endWindowScanline;
int startWindowPixel, endWindowPixel;
CalculateWindowScanlines(out startWindowScanline, out endWindowScanline);
CalculateWindowPosition(out startWindowPixel, out endWindowPixel);
// Render scanline
if (ScanLine >= startWindowScanline && ScanLine < endWindowScanline) // Window takes up whole scanline
{
RenderScrollAScanline(0, 0, NameTableAddrWindow, 0, FrameWidth, true);
}
else if (startWindowPixel != -1) // Window takes up partial scanline
{
if (startWindowPixel == 0) // Window grows from left side
{
RenderScrollAScanline(0, 0, NameTableAddrWindow, 0, endWindowPixel, true);
RenderScrollAScanline(hscroll, vscroll, NameTableAddrA, endWindowPixel, FrameWidth, false);
}
else // Window grows from right side
{
RenderScrollAScanline(hscroll, vscroll, NameTableAddrA, 0, startWindowPixel, false);
RenderScrollAScanline(0, 0, NameTableAddrWindow, startWindowPixel, FrameWidth, true);
}
}
else // No window this scanline
{
RenderScrollAScanline(hscroll, vscroll, NameTableAddrA, 0, FrameWidth, false);
}
}
void RenderScrollATwoCellVScroll()
{
// Calculate scroll offsets
int hscroll = CalcHScrollPlaneA(ScanLine);
// Calculate window dimensions
int startWindowScanline, endWindowScanline;
int startWindowPixel, endWindowPixel;
CalculateWindowScanlines(out startWindowScanline, out endWindowScanline);
CalculateWindowPosition(out startWindowPixel, out endWindowPixel);
// Render scanline
if (ScanLine >= startWindowScanline && ScanLine < endWindowScanline) // Window takes up whole scanline
{
RenderScrollAScanline(0, 0, NameTableAddrWindow, 0, FrameWidth, true);
}
else if (startWindowPixel != -1) // Window takes up partial scanline
{
if (startWindowPixel == 0) // Window grows from left side
{
RenderScrollAScanline(0, 0, NameTableAddrWindow, 0, endWindowPixel, true);
RenderScrollAScanlineTwoCellVScroll(hscroll, NameTableAddrA, endWindowPixel, FrameWidth, false);
}
else // Window grows from right side
{
RenderScrollAScanlineTwoCellVScroll(hscroll, NameTableAddrA, 0, startWindowPixel, false);
RenderScrollAScanline(0, 0, NameTableAddrWindow, startWindowPixel, FrameWidth, true);
}
}
else // No window this scanline
{
RenderScrollAScanlineTwoCellVScroll(hscroll, NameTableAddrA, 0, FrameWidth, false);
}
}
void RenderScrollB()
{
int bgColor = BackgroundColor;
int xScroll = CalcHScrollPlaneB(ScanLine);
int yScroll = VSRAM[1] & 0x3FF;
const int lowPriority = 1;
const int highPriority = 4;
int yTile = ((ScanLine + yScroll) / 8) % NameTableHeight;
// this is hellllla slow. but not optimizing until we implement & understand
// all scrolling modes, shadow & hilight, etc.
// in thinking about this, you could convince me to optimize the PCE background renderer now.
// Its way simple in comparison. But the PCE sprite renderer is way worse than gen.
for (int x = 0; x < FrameWidth; x++)
{
int xTile = Math.Abs(((x + (1024 - xScroll)) / 8) % NameTableWidth);
int xOfs = Math.Abs((x + (1024 - xScroll)) & 7);
int yOfs = (ScanLine + yScroll) % 8;
int cellOfs = NameTableAddrB + (yTile * NameTableWidth * 2) + (xTile * 2);
int nameTableEntry = VRAM[cellOfs] | (VRAM[cellOfs + 1] << 8);
int patternNo = nameTableEntry & 0x7FF;
bool hFlip = ((nameTableEntry >> 11) & 1) != 0;
bool vFlip = ((nameTableEntry >> 12) & 1) != 0;
bool priority = ((nameTableEntry >> 15) & 1) != 0;
int palette = (nameTableEntry >> 13) & 3;
if (priority && PriorityBuffer[x] >= highPriority) continue;
if (!priority && PriorityBuffer[x] >= lowPriority) continue;
if (vFlip) yOfs = 7 - yOfs;
if (hFlip) xOfs = 7 - xOfs;
int texel = PatternBuffer[(patternNo * 64) + (yOfs * 8) + (xOfs)];
int pixel = Palette[(palette * 16) + texel];
if (texel != 0)
{
FrameBuffer[(ScanLine * FrameWidth) + x] = pixel;
PriorityBuffer[x] = (byte)(priority ? highPriority : lowPriority);
}
else
{
FrameBuffer[(ScanLine * FrameWidth) + x] = bgColor;
}
}
}
void RenderScrollBTwoCellVScroll()
{
int bgColor = BackgroundColor;
int xScroll = CalcHScrollPlaneB(ScanLine);
int fineHScroll = xScroll & 15;
const int lowPriority = 1;
const int highPriority = 4;
for (int x = 0; x < FrameWidth; x++)
{
int vsramUnitOffset = ((x - fineHScroll) / 16) % 40;
int yScroll = VSRAM[(vsramUnitOffset * 2) + 1] & 0x3FF;
int yTile = ((ScanLine + yScroll) / 8) % NameTableHeight;
int xTile = Math.Abs(((x + (1024 - xScroll)) / 8) % NameTableWidth);
int xOfs = Math.Abs((x + (1024 - xScroll)) & 7);
int yOfs = (ScanLine + yScroll) % 8;
int cellOfs = NameTableAddrB + (yTile * NameTableWidth * 2) + (xTile * 2);
int nameTableEntry = VRAM[cellOfs] | (VRAM[cellOfs + 1] << 8);
int patternNo = nameTableEntry & 0x7FF;
bool hFlip = ((nameTableEntry >> 11) & 1) != 0;
bool vFlip = ((nameTableEntry >> 12) & 1) != 0;
bool priority = ((nameTableEntry >> 15) & 1) != 0;
int palette = (nameTableEntry >> 13) & 3;
if (priority && PriorityBuffer[x] >= highPriority) continue;
if (!priority && PriorityBuffer[x] >= lowPriority) continue;
if (vFlip) yOfs = 7 - yOfs;
if (hFlip) xOfs = 7 - xOfs;
int texel = PatternBuffer[(patternNo * 64) + (yOfs * 8) + (xOfs)];
int pixel = Palette[(palette * 16) + texel];
if (texel != 0)
{
FrameBuffer[(ScanLine * FrameWidth) + x] = pixel;
PriorityBuffer[x] = (byte)(priority ? highPriority : lowPriority);
}
else
{
FrameBuffer[(ScanLine * FrameWidth) + x] = bgColor;
}
}
}
static readonly int[] SpriteSizeTable = { 8, 16, 24, 32 };
Sprite sprite;
void RenderSpritesScanline()
{
int scanLineBase = ScanLine * FrameWidth;
int processedSprites = 0;
int processedSpritesThisLine = 0;
int processedDotsThisLine = 0;
bool spriteMaskPrecursor = false;
// This is incredibly unoptimized. TODO...
FetchSprite(0);
while (true)
{
if (sprite.Y > ScanLine || sprite.Y + sprite.HeightPixels <= ScanLine)
goto nextSprite;
processedSpritesThisLine++;
processedDotsThisLine += sprite.WidthPixels;
if (sprite.X > -128)
spriteMaskPrecursor = true;
if (sprite.X == -128 && spriteMaskPrecursor)
break; // apply sprite mask
if (sprite.X + sprite.WidthPixels <= 0)
goto nextSprite;
if (sprite.HeightCells == 2)
sprite.HeightCells = 2;
int yline = ScanLine - sprite.Y;
if (sprite.VFlip)
yline = sprite.HeightPixels - 1 - yline;
int paletteBase = sprite.Palette * 16;
if (sprite.HFlip == false)
{
int pattern = sprite.PatternIndex + ((yline / 8));
for (int xi = 0; xi < sprite.WidthPixels; xi++)
{
if (sprite.X + xi < 0 || sprite.X + xi >= FrameWidth)
continue;
if (sprite.Priority == false && PriorityBuffer[sprite.X + xi] >= 3) continue;
if (PriorityBuffer[sprite.X + xi] == 9) continue;
int pixel = PatternBuffer[((pattern + ((xi / 8) * sprite.HeightCells)) * 64) + ((yline & 7) * 8) + (xi & 7)];
if (pixel != 0)
{
FrameBuffer[scanLineBase + sprite.X + xi] = Palette[paletteBase + pixel];
PriorityBuffer[sprite.X + xi] = 9;
}
}
}
else
{ // HFlip
int pattern = sprite.PatternIndex + ((yline / 8)) + (sprite.HeightCells * (sprite.WidthCells - 1));
for (int xi = 0; xi < sprite.WidthPixels; xi++)
{
if (sprite.X + xi < 0 || sprite.X + xi >= FrameWidth)
continue;
if (sprite.Priority == false && PriorityBuffer[sprite.X + xi] >= 3) continue;
if (PriorityBuffer[sprite.X + xi] == 9) continue;
int pixel = PatternBuffer[((pattern + ((-xi / 8) * sprite.HeightCells)) * 64) + ((yline & 7) * 8) + (7 - (xi & 7))];
if (pixel != 0)
{
FrameBuffer[scanLineBase + sprite.X + xi] = Palette[paletteBase + pixel];
PriorityBuffer[sprite.X + xi] = 9;
}
}
}
nextSprite:
if (sprite.Link == 0)
break;
if (++processedSprites >= SpriteLimit)
break;
if (processedSpritesThisLine >= SpritePerLineLimit)
break;
if (processedDotsThisLine >= DotsPerLineLimit)
break;
if (DisplayWidth == 32 && sprite.Link >= 64)
break;
FetchSprite(sprite.Link);
}
}
void FetchSprite(int spriteNo)
{
// Note - X/Y coordinates are 10-bits (3FF) but must be masked to 9-bits (1FF)
// In interlace mode this behavior should change
int SatBase = SpriteAttributeTableAddr + (spriteNo * 8);
sprite.Y = (VRAM[SatBase + 0] | (VRAM[SatBase + 1] << 8) & 0x1FF) - 128;
sprite.X = (VRAM[SatBase + 6] | (VRAM[SatBase + 7] << 8) & 0x1FF) - 128;
sprite.WidthPixels = SpriteSizeTable[(VRAM[SatBase + 3] >> 2) & 3];
sprite.HeightPixels = SpriteSizeTable[VRAM[SatBase + 3] & 3];
sprite.WidthCells = ((VRAM[SatBase + 3] >> 2) & 3) + 1;
sprite.HeightCells = (VRAM[SatBase + 3] & 3) + 1;
sprite.Link = VRAM[SatBase + 2] & 0x7F;
sprite.PatternIndex = (VRAM[SatBase + 4] | (VRAM[SatBase + 5] << 8)) & 0x7FF;
sprite.HFlip = ((VRAM[SatBase + 5] >> 3) & 1) != 0;
sprite.VFlip = ((VRAM[SatBase + 5] >> 4) & 1) != 0;
sprite.Palette = (VRAM[SatBase + 5] >> 5) & 3;
sprite.Priority = ((VRAM[SatBase + 5] >> 7) & 1) != 0;
}
struct Sprite
{
public int X, Y;
public int WidthPixels, HeightPixels;
public int WidthCells, HeightCells;
public int Link;
public int Palette;
public int PatternIndex;
public bool Priority;
public bool HFlip;
public bool VFlip;
}
int CalcHScrollPlaneA(int line)
{
int ofs = 0;
switch (Registers[11] & 3)
{
case 0: ofs = HScrollTableAddr; break;
case 1: ofs = HScrollTableAddr + ((line & 7) * 4); break;
case 2: ofs = HScrollTableAddr + ((line & ~7) * 4); break;
case 3: ofs = HScrollTableAddr + (line * 4); break;
}
int value = VRAM[ofs] | (VRAM[ofs + 1] << 8);
return value & 0x3FF;
}
int CalcHScrollPlaneB(int line)
{
int ofs = 0;
switch (Registers[11] & 3)
{
case 0: ofs = HScrollTableAddr; break;
case 1: ofs = HScrollTableAddr + ((line & 7) * 4); break;
case 2: ofs = HScrollTableAddr + ((line & ~7) * 4); break;
case 3: ofs = HScrollTableAddr + (line * 4); break;
}
int value = VRAM[ofs + 2] | (VRAM[ofs + 3] << 8);
return value & 0x3FF;
}
}
}

View File

@ -1,572 +0,0 @@
using System;
using System.IO;
using System.Globalization;
using BizHawk.Common;
using BizHawk.Common.BufferExtensions;
using BizHawk.Common.IOExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
public sealed partial class GenVDP : IVideoProvider
{
// Memory
public byte[] VRAM = new byte[0x10000];
public ushort[] CRAM = new ushort[64];
public ushort[] VSRAM = new ushort[40];
public byte[] Registers = new byte[0x20];
public byte[] PatternBuffer = new byte[0x20000];
public int[] Palette = new int[64];
public int[] FrameBuffer = new int[320 * 224];
public int FrameWidth = 320;
public int FrameHeight = 224;
public int ScanLine;
public int HIntLineCounter;
public bool HInterruptsEnabled { get { return (Registers[0] & 0x10) != 0; } }
public bool DisplayEnabled { get { return (Registers[1] & 0x40) != 0; } }
public bool VInterruptEnabled { get { return (Registers[1] & 0x20) != 0; } }
public bool DmaEnabled { get { return (Registers[1] & 0x10) != 0; } }
public bool CellBasedVertScroll { get { return (Registers[11] & 0x04) != 0; } }
public bool InDisplayPeriod { get { return ScanLine < 224 && DisplayEnabled; } }
ushort NameTableAddrA;
ushort NameTableAddrB;
ushort NameTableAddrWindow;
ushort SpriteAttributeTableAddr;
ushort HScrollTableAddr;
int NameTableWidth = 32;
int NameTableHeight = 32;
int DisplayWidth;
int SpriteLimit;
int SpritePerLineLimit;
int DotsPerLineLimit;
bool ControlWordPending;
ushort VdpDataAddr;
byte VdpDataCode;
const int CommandVramRead = 0;
const int CommandVramWrite = 1;
const int CommandCramWrite = 3;
const int CommandVsramRead = 4;
const int CommandVsramWrite = 5;
const int CommandCramRead = 8;
public ushort VdpStatusWord = 0x3400;
public const int StatusHorizBlanking = 0x04;
public const int StatusVerticalBlanking = 0x08;
public const int StatusOddFrame = 0x10;
public const int StatusSpriteCollision = 0x20;
public const int StatusSpriteOverflow = 0x40;
public const int StatusVerticalInterruptPending = 0x80;
public bool VdpDebug = false;
public Func<int> GetPC;
public GenVDP()
{
WriteVdpRegister(00, 0x04);
WriteVdpRegister(01, 0x04);
WriteVdpRegister(02, 0x30);
WriteVdpRegister(03, 0x3C);
WriteVdpRegister(04, 0x07);
WriteVdpRegister(05, 0x67);
WriteVdpRegister(10, 0xFF);
WriteVdpRegister(12, 0x81);
WriteVdpRegister(15, 0x02);
Log.Note("VDP", "VDP init routine complete");
}
public ushort ReadVdp(int addr)
{
switch (addr)
{
case 0:
case 2:
return ReadVdpData();
case 4:
case 6:
return ReadVdpControl();
default:
return ReadHVCounter();
}
}
public void WriteVdp(int addr, ushort data)
{
switch (addr)
{
case 0:
case 2:
WriteVdpData(data);
return;
case 4:
case 6:
WriteVdpControl(data);
return;
}
}
public void WriteVdpControl(ushort data)
{
Log.Note("VDP", "Control Write {0:X4} (PC={1:X6})", data, GetPC());
if (ControlWordPending == false)
{
if ((data & 0xC000) == 0x8000)
{
int reg = (data >> 8) & 0x1F;
byte value = (byte)(data & 0xFF);
WriteVdpRegister(reg, value);
VdpDataCode = 0;
}
else
{
ControlWordPending = true;
VdpDataAddr &= 0xC000;
VdpDataAddr |= (ushort)(data & 0x3FFF);
VdpDataCode &= 0x3C;
VdpDataCode |= (byte)(data >> 14);
//Console.WriteLine("Address = {0:X4}", VdpDataAddr);
//Console.WriteLine("Code = {0:X2}", VdpDataCode);
}
}
else
{
ControlWordPending = false;
// Update data address and code
VdpDataAddr &= 0x3FFF;
VdpDataAddr |= (ushort)((data & 0x03) << 14);
//Console.WriteLine("Address = {0:X4}", VdpDataAddr);
VdpDataCode &= 0x03;
VdpDataCode |= (byte)((data >> 2) & 0x3C);
//Log.Note("VDP", "Code = {0:X2}", VdpDataCode);
if ((VdpDataCode & 0x20) != 0 && DmaEnabled) // DMA triggered
{
//Console.WriteLine("DMA TIME!");
// what type of DMA?
switch (Registers[23] >> 6)
{
case 2:
Log.Note("VDP", "VRAM FILL");
DmaFillModePending = true;
break;
case 3:
Log.Error("VDP", "VRAM COPY *");
ExecuteVramVramCopy();
break;
default:
Execute68000VramCopy();
break;
}
}
}
}
public ushort ReadVdpControl()
{
VdpStatusWord |= 0x0200; // Fifo empty // TODO kill this, emulating the damn FIFO.
ControlWordPending = false; // Hmm.. if this happens in an interrupt between 1st and 2nd word..
// sprite overflow flag should clear.
// sprite collision flag should clear.
return VdpStatusWord;
}
public void WriteVdpData(ushort data)
{
Log.Note("VDP", "Data port write: {0:X4} (PC={1:X6})", data, GetPC());
ControlWordPending = false;
// byte-swap incoming data when A0 is set
if ((VdpDataAddr & 1) != 0)
{
data = (ushort)((data >> 8) | (data << 8));
Log.Error("VDP", "VRAM byte-swap is happening because A0 is not 0. [{0:X4}] = {1:X4}", VdpDataAddr, data);
}
switch (VdpDataCode & 0xF)
{
case CommandVramWrite: // VRAM Write
VRAM[VdpDataAddr & 0xFFFE] = (byte)data;
VRAM[(VdpDataAddr & 0xFFFE) + 1] = (byte)(data >> 8);
//if (VdpDebug)
Log.Note("VDP", "VRAM[{0:X4}] = {1:X4}", VdpDataAddr, data);
UpdatePatternBuffer(VdpDataAddr & 0xFFFE);
UpdatePatternBuffer((VdpDataAddr & 0xFFFE) + 1);
VdpDataAddr += Registers[0x0F];
break;
case CommandCramWrite: // CRAM write
CRAM[(VdpDataAddr / 2) % 64] = data;
//if (VdpDebug)
Log.Note("VDP", "CRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 64, data);
ProcessPalette((VdpDataAddr / 2) % 64);
VdpDataAddr += Registers[0x0F];
break;
case CommandVsramWrite: // VSRAM write
VSRAM[(VdpDataAddr / 2) % 40] = data;
//if (VdpDebug)
Log.Note("VDP", "VSRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 40, data);
VdpDataAddr += Registers[0x0F];
break;
default:
Log.Error("VPD", "VDP DATA WRITE WITH UNHANDLED CODE!!! {0}", VdpDataCode & 7);
break;
}
if (DmaFillModePending)
{
ExecuteVramFill(data);
}
}
public ushort ReadVdpData()
{
int orig_addr = VdpDataAddr;
ushort retval = 0xBEEF;
switch (VdpDataCode & 0x0F)
{
case CommandVramRead:
//if ((VdpDataAddr & 1) != 0) throw new Exception("VRAM read is not word-aligned. what do?");
retval = VRAM[VdpDataAddr & 0xFFFE];
retval |= (ushort)(VRAM[(VdpDataAddr & 0xFFFE) + 1] << 8);
VdpDataAddr += Registers[0x0F];
break;
case CommandVsramRead:
retval = VSRAM[(VdpDataAddr / 2) % 40];
VdpDataAddr += Registers[0x0F];
return retval;
case CommandCramRead:
retval = CRAM[(VdpDataAddr / 2) % 64];
VdpDataAddr += Registers[0x0F];
return retval;
default:
throw new Exception("VRAM read with unexpected code!!! " + (VdpDataCode & 0x0F));
}
Log.Note("VDP", "VDP Data Read from {0:X4} returning {1:X4}", orig_addr, retval);
return retval;
}
ushort ReadHVCounter()
{
int vcounter = ScanLine;
if (vcounter > 0xEA)
vcounter -= 7;
// TODO generalize this across multiple video modes and stuff.
// TODO dont tie this to musashi cycle count.
// Figure out a "clean" way to get cycle counter information available to VDP.
// Oh screw that. The VDP and the cpu cycle counters are going to be intertwined pretty tightly.
int hcounter = (488 - Native68000.Musashi.GetCyclesRemaining()) * 255 / 488;
// FIXME: totally utterly wrong.
ushort res = (ushort)((vcounter << 8) | (hcounter & 0xFF));
//Console.WriteLine("READ HVC: V={0:X2} H={1:X2} ret={2:X4}", vcounter, hcounter, res);
return res;
}
public void WriteVdpRegister(int register, byte data)
{
//if (VdpDebug)
Log.Note("VDP", "Register {0}: {1:X2}", register, data);
switch (register)
{
case 0x00: // Mode Set Register 1
Registers[register] = data;
//if (VdpDebug)
//Log.Note("VDP", "HINT enabled: " + HInterruptsEnabled);
break;
case 0x01: // Mode Set Register 2
//if (VdpDebug)
//{
// Registers[register] = data;
// Log.Note("VDP", "DisplayEnabled: " + DisplayEnabled);
// Log.Note("VDP", "DmaEnabled: " + DmaEnabled);
// Log.Note("VDP", "VINT enabled: " + VInterruptEnabled);
//}
break;
case 0x02: // Name Table Address for Layer A
NameTableAddrA = (ushort)((data & 0x38) << 10);
//if (VdpDebug)
//Log.Note("VDP", "SET NTa A = {0:X4}", NameTableAddrA);
break;
case 0x03: // Name Table Address for Window
NameTableAddrWindow = (ushort)((data & 0x3E) << 10);
//if (VdpDebug)
//Log.Note("VDP", "SET NTa W = {0:X4}", NameTableAddrWindow);
break;
case 0x04: // Name Table Address for Layer B
NameTableAddrB = (ushort)(data << 13);
//if (VdpDebug)
//Log.Note("VDP", "SET NTa B = {0:X4}", NameTableAddrB);
break;
case 0x05: // Sprite Attribute Table Address
SpriteAttributeTableAddr = (ushort)(data << 9);
//if (VdpDebug)
//Log.Note("VDP", "SET SAT attr = {0:X4}", SpriteAttributeTableAddr);
break;
case 0x0A: // H Interrupt Register
//if (VdpDebug)
//Log.Note("VDP", "HInt occurs every {0} lines.", data);
break;
case 0x0B: // VScroll/HScroll modes
//if (VdpDebug)
//{
// if ((data & 4) != 0)
// Log.Note("VDP", "VSCroll Every 2 Cells Enabled");
// else
// Log.Note("VDP", "Full Screen VScroll");
// int hscrollmode = data & 3;
// switch (hscrollmode)
// {
// case 0: Log.Note("VDP", "Full Screen HScroll"); break;
// case 1: Log.Note("VDP", "Prohibited HSCROLL mode!!! But it'll work."); break;
// case 2: Log.Note("VDP", "HScroll every 1 cell"); break;
// case 3: Log.Note("VDP", "HScroll every line"); break;
// }
//}
break;
case 0x0C: // Mode Set #4
// TODO interlaced modes
if ((data & 0x81) == 0)
{
// Display is 32 cells wide
if (DisplayWidth != 32)
{
FrameBuffer = new int[256 * 224];
FrameWidth = 256;
DisplayWidth = 32;
SpriteLimit = 64;
SpritePerLineLimit = 16;
DotsPerLineLimit = 256;
}
}
else
{
// Display is 40 cells wide
if (DisplayWidth != 40)
{
FrameBuffer = new int[320 * 224];
FrameWidth = 320;
DisplayWidth = 40;
SpriteLimit = 80;
SpritePerLineLimit = 20;
DotsPerLineLimit = 320;
}
}
break;
case 0x0D: // H Scroll Table Address
HScrollTableAddr = (ushort)(data << 10);
//if (VdpDebug)
//Log.Note("VDP", "SET HScrollTab attr = {0:X4}", HScrollTableAddr);
break;
case 0x0F: // Auto Address Register Increment
//if (VdpDebug)
//Log.Note("VDP", "Set Data Increment to " + data);
break;
case 0x10: // Nametable Dimensions
switch (data & 0x03)
{
case 0: NameTableWidth = 32; break;
case 1: NameTableWidth = 64; break;
case 2: NameTableWidth = 32; break; // invalid setting
case 3: NameTableWidth = 128; break;
}
switch ((data >> 4) & 0x03)
{
case 0: NameTableHeight = 32; break;
case 1: NameTableHeight = 64; break;
case 2: NameTableHeight = 32; break; // invalid setting
case 3: NameTableHeight = 128; break;
}
break;
case 0x11: // Window H Position
int whp = data & 31;
bool fromright = (data & 0x80) != 0;
//if (VdpDebug)
//Log.Note("VDP", "Window H is {0} units from {1}", whp, fromright ? "right" : "left");
break;
case 0x12: // Window V
whp = data & 31;
fromright = (data & 0x80) != 0;
//if (VdpDebug)
//Log.Note("VDP", "Window V is {0} units from {1}", whp, fromright ? "lower" : "upper");
break;
case 0x13: // DMA Length Low
Registers[register] = data;
//Log.Note("VDP", "DMA Length = {0:X4}", DmaLength);
break;
case 0x14: // DMA Length High
Registers[register] = data;
//Log.Note("VDP", "DMA Length = {0:X4}", DmaLength);
break;
case 0x15: // DMA Source Low
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
case 0x16: // DMA Source Mid
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
case 0x17: // DMA Source High
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
}
Registers[register] = data;
}
void ProcessPalette(int slot)
{
byte r = PalXlatTable[(CRAM[slot] & 0x000F) >> 0];
byte g = PalXlatTable[(CRAM[slot] & 0x00F0) >> 4];
byte b = PalXlatTable[(CRAM[slot] & 0x0F00) >> 8];
Palette[slot] = Colors.ARGB(r, g, b);
}
void UpdatePatternBuffer(int addr)
{
PatternBuffer[(addr * 2) + 1] = (byte)(VRAM[addr ^ 1] & 0x0F);
PatternBuffer[(addr * 2) + 0] = (byte)(VRAM[addr ^ 1] >> 4);
}
public int[] GetVideoBuffer()
{
return FrameBuffer;
}
public int VirtualWidth { get { return 320; } }
public int VirtualHeight { get { return FrameHeight; } }
public int BufferWidth
{
get { return FrameWidth; }
}
public int BufferHeight
{
get { return FrameHeight; }
}
public int BackgroundColor
{
get { return Palette[Registers[7] & 0x3F]; }
}
#region State Save/Load Code
public void SaveStateText(TextWriter writer)
{
writer.WriteLine("[VDP]");
writer.Write("VRAM ");
VRAM.SaveAsHex(writer);
writer.Write("CRAM ");
CRAM.SaveAsHex(writer);
writer.Write("VSRAM ");
VSRAM.SaveAsHex(writer);
writer.Write("Registers ");
Registers.SaveAsHex(writer);
writer.WriteLine("ControlWordPending {0}", ControlWordPending);
writer.WriteLine("DmaFillModePending {0}", DmaFillModePending);
writer.WriteLine("VdpDataAddr {0:X4}", VdpDataAddr);
writer.WriteLine("VdpDataCode {0}", VdpDataCode);
writer.WriteLine("[/VDP]");
}
public void LoadStateText(TextReader reader)
{
while (true)
{
string[] args = reader.ReadLine().Split(' ');
if (args[0].Trim() == "") continue;
if (args[0] == "[/VDP]") break;
else if (args[0] == "VRAM") VRAM.ReadFromHex(args[1]);
else if (args[0] == "CRAM") CRAM.ReadFromHex(args[1]);
else if (args[0] == "VSRAM") VSRAM.ReadFromHex(args[1]);
else if (args[0] == "Registers") Registers.ReadFromHex(args[1]);
else if (args[0] == "ControlWordPending") ControlWordPending = bool.Parse(args[1]);
else if (args[0] == "DmaFillModePending") DmaFillModePending = bool.Parse(args[1]);
else if (args[0] == "VdpDataAddr") VdpDataAddr = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "VdpDataCode") VdpDataCode = byte.Parse(args[1]);
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
for (int i = 0; i < CRAM.Length; i++)
ProcessPalette(i);
for (int i = 0; i < VRAM.Length; i++)
UpdatePatternBuffer(i);
for (int i = 0; i < Registers.Length; i++)
WriteVdpRegister(i, Registers[i]);
}
public void SaveStateBinary(BinaryWriter writer)
{
writer.Write(VRAM);
writer.Write(CRAM);
writer.Write(VSRAM);
writer.Write(Registers);
writer.Write(ControlWordPending);
writer.Write(DmaFillModePending);
writer.Write(VdpDataAddr);
writer.Write(VdpDataCode);
}
public void LoadStateBinary(BinaryReader reader)
{
VRAM = reader.ReadBytes(VRAM.Length);
CRAM = reader.ReadUInt16s(CRAM.Length);
VSRAM = reader.ReadUInt16s(VSRAM.Length);
Registers = reader.ReadBytes(Registers.Length);
ControlWordPending = reader.ReadBoolean();
DmaFillModePending = reader.ReadBoolean();
VdpDataAddr = reader.ReadUInt16();
VdpDataCode = reader.ReadByte();
for (int i = 0; i < CRAM.Length; i++)
ProcessPalette(i);
for (int i = 0; i < VRAM.Length; i++)
UpdatePatternBuffer(i);
for (int i = 0; i < Registers.Length; i++)
WriteVdpRegister(i, Registers[i]);
}
#endregion
}
}

View File

@ -1,132 +0,0 @@
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
partial class Genesis
{
bool SegaCD = false;
class IOPort
{
public byte Data;
public byte Control;
public byte TxData;
public byte RxData;
public byte SCtrl;
// TODO- a reference to connected device? That gets into the issue of configuring different types of controllers. :|
public bool TH { get { return (Data & 0x40) != 0; } }
}
IOPort[] IOPorts = new IOPort[]
{
new IOPort { Data = 0x7F, TxData = 0xFF, RxData = 0xFF, SCtrl = 0xFF },
new IOPort { Data = 0x7F, TxData = 0xFF, RxData = 0xFF, SCtrl = 0xFF },
new IOPort { Data = 0x7F, TxData = 0xFF, RxData = 0xFF, SCtrl = 0xFF }
};
byte ReadIO(int offset)
{
offset >>= 1;
offset &= 0x0F;
if (offset > 1)
Log.Note("CPU", "^^^ IO Read {0}: 00", offset);
switch (offset)
{
case 0: // version
byte value = (byte)(SegaCD ? 0x00 : 0x20);
switch ((char)RomData[0x01F0])
{
case 'J': value |= 0x00; break;
case 'U': value |= 0x80; break;
case 'E': value |= 0xC0; break;
case 'A': value |= 0xC0; break;
case '4': value |= 0x80; break;
default: value |= 0x80; break;
}
//value |= 1; // US
Log.Note("CPU", "^^^ IO Read 0: {0:X2}", value);
return value;
case 1: // Port A
lagged = false;
ReadController(ref IOPorts[0].Data);
Log.Note("CPU", "^^^ IO Read 1: {0:X2}", IOPorts[0].Data);
return IOPorts[0].Data;
case 2: return 0xFF;
case 3: return 0xFF;
case 0x04: return IOPorts[0].Control;
case 0x05: return IOPorts[1].Control;
case 0x06: return IOPorts[2].Control;
case 0x07: return IOPorts[0].TxData;
case 0x08: return IOPorts[0].RxData;
case 0x09: return IOPorts[0].SCtrl;
case 0x0A: return IOPorts[1].TxData;
case 0x0B: return IOPorts[1].RxData;
case 0x0C: return IOPorts[1].SCtrl;
case 0x0D: return IOPorts[2].TxData;
case 0x0E: return IOPorts[2].RxData;
case 0x0F: return IOPorts[2].SCtrl;
}
Log.Note("CPU", "^^^ IO Read {0}: {1:X2}", offset, 0xFF);
return 0xFF;
}
void WriteIO(int offset, int value)
{
offset >>= 1;
offset &= 0x0F;
switch (offset)
{
case 0x00: break;
case 0x01: IOPorts[0].Data = (byte)value; break;
case 0x02: IOPorts[1].Data = (byte)value; break;
case 0x03: IOPorts[2].Data = (byte)value; break;
case 0x04: IOPorts[0].Control = (byte)value; break;
case 0x05: IOPorts[1].Control = (byte)value; break;
case 0x06: IOPorts[2].Control = (byte)value; break;
case 0x07: IOPorts[0].TxData = (byte)value; break;
case 0x08: IOPorts[0].RxData = (byte)value; break;
case 0x09: IOPorts[0].SCtrl = (byte)value; break;
case 0x0A: IOPorts[1].TxData = (byte)value; break;
case 0x0B: IOPorts[1].RxData = (byte)value; break;
case 0x0C: IOPorts[1].SCtrl = (byte)value; break;
case 0x0D: IOPorts[2].TxData = (byte)value; break;
case 0x0E: IOPorts[2].RxData = (byte)value; break;
case 0x0F: IOPorts[2].SCtrl = (byte)value; break;
}
}
void ReadController(ref byte data)
{
InputCallbacks.Call();
data &= 0xC0;
if ((data & 0x40) != 0) // TH high
{
if (Controller.IsPressed("P1 Up") == false) data |= 0x01;
if (Controller.IsPressed("P1 Down") == false) data |= 0x02;
if (Controller.IsPressed("P1 Left") == false) data |= 0x04;
if (Controller.IsPressed("P1 Right") == false) data |= 0x08;
if (Controller.IsPressed("P1 B") == false) data |= 0x10;
if (Controller.IsPressed("P1 C") == false) data |= 0x20;
}
else
{ // TH low
if (Controller.IsPressed("P1 Up") == false) data |= 0x01;
if (Controller.IsPressed("P1 Down") == false) data |= 0x02;
if (Controller.IsPressed("P1 A") == false) data |= 0x10;
if (Controller.IsPressed("P1 Start") == false) data |= 0x20;
}
}
}
}

View File

@ -1,20 +0,0 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
partial class Genesis
{
public static readonly ControllerDefinition GenesisController = new ControllerDefinition
{
Name = "Genesis 3-Button Controller",
BoolButtons =
{
"Reset",
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 A", "P1 B", "P1 C", "P1 Start"
}
};
public ControllerDefinition ControllerDefinition { get { return GenesisController; } }
public IController Controller { private get; set; }
}
}

View File

@ -1,495 +0,0 @@
#define MUSASHI
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Common.Components;
using BizHawk.Emulation.Cores.Components;
using BizHawk.Emulation.Cores.Components.M68000;
using BizHawk.Emulation.Cores.Components.Z80;
using Native68000;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
[CoreAttributes(
"GenesisHawk",
"Vecna",
isPorted: false,
isReleased: false
)]
public sealed partial class Genesis : IEmulator, ISaveRam, IStatable, IInputPollable
{
private int _lagcount = 0;
private bool lagged = true;
private bool islag = false;
// ROM
public byte[] RomData;
// Machine stuff
public MC68000 MainCPU;
public Z80A SoundCPU;
public GenVDP VDP;
public SN76489 PSG;
public YM2612 YM2612;
public byte[] Ram = new byte[0x10000];
public byte[] Z80Ram = new byte[0x2000];
private bool M68000HasZ80Bus = false;
private bool Z80Reset = false;
private bool Z80Runnable { get { return (Z80Reset == false && M68000HasZ80Bus == false); } }
private SoundMixer SoundMixer;
[FeatureNotImplemented]
public IInputCallbackSystem InputCallbacks { get { throw new NotImplementedException(); } }
public void ResetCounters()
{
Frame = 0;
_lagcount = 0;
islag = false;
}
// Genesis timings:
// 53,693,175 Machine clocks / sec
// 7,670,454 Main 68000 cycles / sec (7 mclk divisor)
// 3,579,545 Z80 cycles / sec (15 mclk divisor)
// At 59.92 FPS:
// 896,081 mclks / frame
// 128,011 Main 68000 cycles / frame
// 59,738 Z80 cycles / frame
// At 262 lines/frame:
// 3420 mclks / line
// ~ 488.5 Main 68000 cycles / line
// 228 Z80 cycles / line
// Video characteristics:
// 224 lines are active display. The remaining 38 lines are vertical blanking.
// In H40 mode, the dot clock is 480 pixels per line.
// 320 are active display, the remaining 160 are horizontal blanking.
// A total of 3420 mclks per line, but 2560 mclks are active display and 860 mclks are blanking.
#if MUSASHI
VdpCallback _vdp;
ReadCallback read8;
ReadCallback read16;
ReadCallback read32;
WriteCallback write8;
WriteCallback write16;
WriteCallback write32;
#endif
public Genesis(CoreComm comm, GameInfo game, byte[] rom)
{
ServiceProvider = new BasicServiceProvider(this);
CoreComm = comm;
MainCPU = new MC68000();
SoundCPU = new Z80A();
YM2612 = new YM2612() { MaxVolume = 23405 };
PSG = new SN76489() { MaxVolume = 4681 };
VDP = new GenVDP();
VDP.DmaReadFrom68000 = ReadWord;
(ServiceProvider as BasicServiceProvider).Register<IVideoProvider>(VDP);
SoundMixer = new SoundMixer(YM2612, PSG);
MainCPU.ReadByte = ReadByte;
MainCPU.ReadWord = ReadWord;
MainCPU.ReadLong = ReadLong;
MainCPU.WriteByte = WriteByte;
MainCPU.WriteWord = WriteWord;
MainCPU.WriteLong = WriteLong;
MainCPU.IrqCallback = InterruptCallback;
// ---------------------- musashi -----------------------
#if MUSASHI
_vdp = vdpcallback;
read8 = Read8;
read16 = Read16;
read32 = Read32;
write8 = Write8;
write16 = Write16;
write32 = Write32;
Musashi.RegisterVdpCallback(Marshal.GetFunctionPointerForDelegate(_vdp));
Musashi.RegisterRead8(Marshal.GetFunctionPointerForDelegate(read8));
Musashi.RegisterRead16(Marshal.GetFunctionPointerForDelegate(read16));
Musashi.RegisterRead32(Marshal.GetFunctionPointerForDelegate(read32));
Musashi.RegisterWrite8(Marshal.GetFunctionPointerForDelegate(write8));
Musashi.RegisterWrite16(Marshal.GetFunctionPointerForDelegate(write16));
Musashi.RegisterWrite32(Marshal.GetFunctionPointerForDelegate(write32));
#endif
// ---------------------- musashi -----------------------
SoundCPU.ReadMemory = ReadMemoryZ80;
SoundCPU.WriteMemory = WriteMemoryZ80;
SoundCPU.WriteHardware = (a, v) => { Console.WriteLine("Z80: Attempt I/O Write {0:X2}:{1:X2}", a, v); };
SoundCPU.ReadHardware = x => 0xFF;
SoundCPU.IRQCallback = () => SoundCPU.Interrupt = false;
Z80Reset = true;
RomData = new byte[0x400000];
for (int i = 0; i < rom.Length; i++)
RomData[i] = rom[i];
SetupMemoryDomains();
#if MUSASHI
Musashi.Init();
Musashi.Reset();
VDP.GetPC = () => Musashi.PC;
#else
MainCPU.Reset();
VDP.GetPC = () => MainCPU.PC;
#endif
InitializeCartHardware(game);
}
public IEmulatorServiceProvider ServiceProvider { get; private set; }
void InitializeCartHardware(GameInfo game)
{
LogCartInfo();
InitializeEeprom(game);
InitializeSaveRam(game);
}
public void FrameAdvance(bool render, bool rendersound)
{
lagged = true;
Frame++;
PSG.BeginFrame(SoundCPU.TotalExecutedCycles);
YM2612.BeginFrame(SoundCPU.TotalExecutedCycles);
// Do start-of-frame events
VDP.HIntLineCounter = VDP.Registers[10];
//VDP.VdpStatusWord &=
unchecked { VDP.VdpStatusWord &= (ushort)~GenVDP.StatusVerticalBlanking; }
for (VDP.ScanLine = 0; VDP.ScanLine < 262; VDP.ScanLine++)
{
//Log.Error("VDP","FRAME {0}, SCANLINE {1}", Frame, VDP.ScanLine);
if (VDP.ScanLine < VDP.FrameHeight)
VDP.RenderLine();
Exec68k(365);
RunZ80(171);
// H-Int now?
VDP.HIntLineCounter--;
if (VDP.HIntLineCounter < 0 && VDP.ScanLine < 224) // FIXME
{
VDP.HIntLineCounter = VDP.Registers[10];
VDP.VdpStatusWord |= GenVDP.StatusHorizBlanking;
if (VDP.HInterruptsEnabled)
{
Set68kIrq(4);
//Console.WriteLine("Fire hint!");
}
}
Exec68k(488 - 365);
RunZ80(228 - 171);
if (VDP.ScanLine == 224)
{
VDP.VdpStatusWord |= GenVDP.StatusVerticalInterruptPending;
VDP.VdpStatusWord |= GenVDP.StatusVerticalBlanking;
Exec68k(16); // this is stupidly wrong.
// End-frame stuff
if (VDP.VInterruptEnabled)
Set68kIrq(6);
SoundCPU.Interrupt = true;
//The INT output is asserted every frame for exactly one scanline, and it can't be disabled. A very short Z80 interrupt routine would be triggered multiple times if it finishes within 228 Z80 clock cycles. I think (but cannot recall the specifics) that some games have delay loops in the interrupt handler for this very reason.
}
}
PSG.EndFrame(SoundCPU.TotalExecutedCycles);
YM2612.EndFrame(SoundCPU.TotalExecutedCycles);
if (lagged)
{
_lagcount++;
islag = true;
}
else
islag = false;
}
void Exec68k(int cycles)
{
#if MUSASHI
Musashi.Execute(cycles);
#else
MainCPU.ExecuteCycles(cycles);
#endif
}
void RunZ80(int cycles)
{
// I emulate the YM2612 synced to Z80 clock, for better or worse.
// So we still need to keep the Z80 cycle count accurate even if the Z80 isn't running.
if (Z80Runnable)
SoundCPU.ExecuteCycles(cycles);
else
SoundCPU.TotalExecutedCycles += cycles;
}
void Set68kIrq(int irq)
{
#if MUSASHI
Musashi.SetIRQ(irq);
#else
MainCPU.Interrupt = irq;
#endif
}
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
return new Dictionary<string, RegisterValue>
{
{ "A-0", MainCPU.A[0].s32 },
{ "A-1", MainCPU.A[1].s32 },
{ "A-2", MainCPU.A[2].s32 },
{ "A-3", MainCPU.A[3].s32 },
{ "A-4", MainCPU.A[4].s32 },
{ "A-5", MainCPU.A[5].s32 },
{ "A-6", MainCPU.A[6].s32 },
{ "A-7", MainCPU.A[7].s32 },
{ "D-0", MainCPU.D[0].s32 },
{ "D-1", MainCPU.D[1].s32 },
{ "D-2", MainCPU.D[2].s32 },
{ "D-3", MainCPU.D[3].s32 },
{ "D-4", MainCPU.D[4].s32 },
{ "D-5", MainCPU.D[5].s32 },
{ "D-6", MainCPU.D[6].s32 },
{ "D-7", MainCPU.D[7].s32 },
{ "SR", MainCPU.SR },
{ "Flag X", MainCPU.X },
{ "Flag N", MainCPU.N },
{ "Flag Z", MainCPU.Z },
{ "Flag V", MainCPU.V },
{ "Flag C", MainCPU.C }
};
}
int vdpcallback(int level) // Musashi handler
{
InterruptCallback(level);
return -1;
}
void InterruptCallback(int level)
{
unchecked { VDP.VdpStatusWord &= (ushort)~GenVDP.StatusVerticalInterruptPending; }
}
public CoreComm CoreComm { get; private set; }
// TODO: Implement ISoundProvider
/*
public IAsyncSoundProvider SoundProvider
{
get { return SoundMixer; }
}
public ISyncSoundProvider SyncSoundProvider { get { return new FakeSyncSound(SoundMixer, 735); } }
*/
public int Frame { get; set; }
public int LagCount { get { return _lagcount; } set { _lagcount = value; } }
public bool IsLagFrame { get { return islag; } set { islag = value; } }
public bool DeterministicEmulation { get { return true; } }
public string SystemId { get { return "GEN"; } }
public void SaveStateText(TextWriter writer)
{
var buf = new byte[141501 + SaveRAM.Length];
var stream = new MemoryStream(buf);
var bwriter = new BinaryWriter(stream);
SaveStateBinary(bwriter);
writer.WriteLine("Version 1");
writer.Write("BigFatBlob ");
buf.SaveAsHex(writer);
/*writer.WriteLine("[MegaDrive]");
MainCPU.SaveStateText(writer, "Main68K");
SoundCPU.SaveStateText(writer);
PSG.SaveStateText(writer);
VDP.SaveStateText(writer);
writer.WriteLine("Frame {0}", Frame);
writer.WriteLine("Lag {0}", _lagcount);
writer.WriteLine("IsLag {0}", islag);
writer.Write("MainRAM ");
Ram.SaveAsHex(writer);
writer.Write("Z80RAM ");
Z80Ram.SaveAsHex(writer);
writer.WriteLine("[/MegaDrive]");*/
}
public void LoadStateText(TextReader reader)
{
var buf = new byte[141501 + SaveRAM.Length];
var version = reader.ReadLine();
if (version != "Version 1")
throw new Exception("Not a valid state vesrion! sorry! your state is bad! Robust states will be added later!");
var omgstate = reader.ReadLine().Split(' ')[1];
buf.ReadFromHex(omgstate);
LoadStateBinary(new BinaryReader(new MemoryStream(buf)));
/*while (true)
{
string[] args = reader.ReadLine().Split(' ');
if (args[0].Trim() == "") continue;
if (args[0] == "[MegaDrive]") continue;
if (args[0] == "[/MegaDrive]") break;
if (args[0] == "MainRAM")
Ram.ReadFromHex(args[1]);
else if (args[0] == "Z80RAM")
Z80Ram.ReadFromHex(args[1]);
else if (args[0] == "[Main68K]")
MainCPU.LoadStateText(reader, "Main68K");
else if (args[0] == "[Z80]")
SoundCPU.LoadStateText(reader);
else if (args[0] == "Frame")
Frame = int.Parse(args[1]);
else if (args[0] == "Lag")
_lagcount = int.Parse(args[1]);
else if (args[0] == "IsLag")
islag = bool.Parse(args[1]);
else if (args[0] == "[PSG]")
PSG.LoadStateText(reader);
else if (args[0] == "[VDP]")
VDP.LoadStateText(reader);
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}*/
}
public void SaveStateBinary(BinaryWriter writer)
{
Musashi.SaveStateBinary(writer); // 124
//SoundCPU.SaveStateBinary(writer); // 46 TODO fix this, there is no way to invoke this core from the UI for testing anyway.
//PSG.SaveStateBinary(writer); // 15
VDP.SaveStateBinary(writer); // 65781
YM2612.SaveStateBinary(writer); // 1785
writer.Write(Ram); // 65535
writer.Write(Z80Ram); // 8192
writer.Write(Frame); // 4
writer.Write(M68000HasZ80Bus); // 1
writer.Write(Z80Reset); // 1
writer.Write(BankRegion); // 4
for (int i = 0; i < 3; i++)
{
writer.Write(IOPorts[i].Data);
writer.Write(IOPorts[i].TxData);
writer.Write(IOPorts[i].RxData);
writer.Write(IOPorts[i].SCtrl);
}
if (SaveRAM.Length > 0)
writer.Write(SaveRAM);
// TODO: EEPROM/cart HW state
// TODO: lag counter crap
}
public void LoadStateBinary(BinaryReader reader)
{
Musashi.LoadStateBinary(reader);
//SoundCPU.LoadStateBinary(reader);
//PSG.LoadStateBinary(reader);
VDP.LoadStateBinary(reader);
YM2612.LoadStateBinary(reader);
Ram = reader.ReadBytes(Ram.Length);
Z80Ram = reader.ReadBytes(Z80Ram.Length);
Frame = reader.ReadInt32();
M68000HasZ80Bus = reader.ReadBoolean();
Z80Reset = reader.ReadBoolean();
BankRegion = reader.ReadInt32();
for (int i = 0; i < 3; i++)
{
IOPorts[i].Data = reader.ReadByte();
IOPorts[i].TxData = reader.ReadByte();
IOPorts[i].RxData = reader.ReadByte();
IOPorts[i].SCtrl = reader.ReadByte();
}
if (SaveRAM.Length > 0)
SaveRAM = reader.ReadBytes(SaveRAM.Length);
}
public byte[] SaveStateBinary()
{
var buf = new byte[141501 + SaveRAM.Length];
var stream = new MemoryStream(buf);
var writer = new BinaryWriter(stream);
SaveStateBinary(writer);
//Console.WriteLine("buf len = {0}", stream.Position);
writer.Close();
return buf;
}
public bool BinarySaveStatesPreferred { get { return false; } }
//MemoryDomainList memoryDomains;
void SetupMemoryDomains()
{
/*
var domains = new List<MemoryDomain>(3);
var MainMemoryDomain = new MemoryDomain("Main RAM", Ram.Length, MemoryDomain.Endian.Big,
addr => Ram[addr & 0xFFFF],
(addr, value) => Ram[addr & 0xFFFF] = value);
var Z80Domain = new MemoryDomain("Z80 RAM", Z80Ram.Length, MemoryDomain.Endian.Little,
addr => Z80Ram[addr & 0x1FFF],
(addr, value) => { Z80Ram[addr & 0x1FFF] = value; });
var VRamDomain = new MemoryDomain("Video RAM", VDP.VRAM.Length, MemoryDomain.Endian.Big,
addr => VDP.VRAM[addr & 0xFFFF],
(addr, value) => VDP.VRAM[addr & 0xFFFF] = value);
var RomDomain = new MemoryDomain("MD CART", RomData.Length, MemoryDomain.Endian.Big,
addr => RomData[addr], //adelikat: For speed considerations, I didn't mask this, every tool that uses memory domains is smart enough not to overflow, if I'm wrong let me know!
(addr, value) => RomData[addr & (RomData.Length - 1)] = value);
var SystemBusDomain = new MemoryDomain("System Bus", 0x1000000, MemoryDomain.Endian.Big,
addr => (byte)ReadByte((int)addr),
(addr, value) => Write8((uint)addr, (uint)value));
domains.Add(MainMemoryDomain);
domains.Add(Z80Domain);
domains.Add(VRamDomain);
domains.Add(RomDomain);
domains.Add(SystemBusDomain);
memoryDomains = new MemoryDomainList(domains);
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(memoryDomains);
*/
throw new NotImplementedException();
}
public void Dispose() { }
}
}

View File

@ -1,254 +0,0 @@
using System;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
partial class Genesis
{
public sbyte ReadByte(int address)
{
address &= 0x00FFFFFF;
if (address < 0x200000)
return (sbyte)RomData[address];
if (address < 0x400000)
{
if (SaveRamEnabled && address >= SaveRamStartOffset && address < SaveRamEndOffset)
{
if (SaveRamEveryOtherByte)
return (sbyte)SaveRAM[(address - SaveRamStartOffset) >> 1];
else
return (sbyte)SaveRAM[address - SaveRamStartOffset];
}
return (sbyte)RomData[address];
}
if (address >= 0xE00000)
return (sbyte)Ram[address & 0xFFFF];
if (address == 0xA11100) // Z80 BUS status
{
//Console.WriteLine("QUERY z80 bus status. 68000 can access? " + (M68000HasZ80Bus && Z80Reset == false));
return (sbyte)(M68000HasZ80Bus && Z80Reset == false ? 0 : 1);
}
if (address >= 0xA10000 && address <= 0xA1001F)
return (sbyte)ReadIO(address);
if ((address & 0xFF0000) == 0xA00000)
return (sbyte)ReadMemoryZ80((ushort)(address & 0x7FFF));
if (address >= 0xC00000 && address < 0xC00010)
{
//Console.WriteLine("byte-reading the VDP. someone is probably stupid.");
ushort value = VDP.ReadVdp(address & 0x0E);
if ((address & 1) == 0) // read MSB
return (sbyte)(value >> 8);
return (sbyte)value; // read LSB
}
Console.WriteLine("UNHANDLED READB {0:X6}", address);
return 0x7D;
}
public short ReadWord(int address)
{
address &= 0x00FFFFFF;
int maskedAddr;
if (address < 0x400000)
return (short)((RomData[address] << 8) | RomData[address + 1]);
if (address >= 0xE00000) // Work RAM
{
maskedAddr = address & 0xFFFE;
return (short)((Ram[maskedAddr] << 8) | Ram[maskedAddr + 1]);
}
if (address >= 0xC00000 && address < 0xC00010)
return (short)VDP.ReadVdp(address & 0x0E);
if (address >= 0xA10000 && address <= 0xA1001F)
return (sbyte)ReadIO(address);
if (address == 0xA11100) // Z80 BUS status
return (short)(M68000HasZ80Bus && Z80Reset == false ? 0x0000 : 0x0100);
Console.WriteLine("UNHANDLED READW {0:X6}", address);
return 0x7DCD;
}
public int ReadLong(int address)
{
address &= 0x00FFFFFF;
int maskedAddr;
if (address < 0x400000) // Cartridge ROM
return (RomData[address] << 24) | (RomData[address + 1] << 16) | (RomData[address + 2] << 8) | RomData[address + 3];
if (address >= 0xE00000) // Work RAM
{
maskedAddr = address & 0xFFFF;
return (Ram[maskedAddr] << 24) | (Ram[maskedAddr + 1] << 16) | (Ram[maskedAddr + 2] << 8) | Ram[maskedAddr + 3];
}
if (address >= 0xC00000)
{
//Console.WriteLine("long-read from VDP");
short msw = ReadWord(address);
short msl = ReadWord(address + 2);
return (msw << 16) | (ushort)msl;
}
// try to handle certain things separate if they need to be separate? otherwise handle as 2x readwords?
{
return ((ushort)ReadWord(address) | (ushort)(ReadWord(address + 2) << 16));
}
//if (address == 0xA10008) return 0; // FIXME HACK for tg-sync.
//Console.WriteLine("UNHANDLED READL {0:X6}", address);
//return 0x7DCDCDCD;
}
public void WriteByte(int address, sbyte value)
{
address &= 0x00FFFFFF;
if (address >= 0xE00000) // Work RAM
{
//Console.WriteLine("MEM[{0:X4}] change from {1:X2} to {2:X2}", address & 0xFFFF, Ram[address & 0xFFFF], value);
Ram[address & 0xFFFF] = (byte)value;
return;
}
if ((address & 0xFF0000) == 0xA00000)
{
WriteMemoryZ80((ushort)(address & 0x7FFF), (byte)value);
return;
}
if (address >= 0xA10000 && address <= 0xA1001F)
{
WriteIO(address, value);
return;
}
if (address == 0xA11100)
{
M68000HasZ80Bus = (value & 1) != 0;
//Console.WriteLine("68000 has the z80 bus: " + M68000HasZ80Bus);
return;
}
if (address == 0xA11200) // Z80 RESET
{
Z80Reset = (value & 1) == 0;
if (Z80Reset)
SoundCPU.SoftReset();
//Console.WriteLine("z80 reset: " + Z80Reset);
return;
}
if (address >= 0xC00000 && address < 0xC00010)
{
// when writing to VDP in byte mode, the LSB is duplicated into the MSB
VDP.WriteVdp(address & 0x1E, (ushort)((ushort)value | ((ushort)value << 8)));
return;
}
if (address >= 0xC00011 && address <= 0xC00017 && (address & 1) != 0)
{
PSG.WritePsgData((byte)value, SoundCPU.TotalExecutedCycles);
return;
}
if (SaveRamEnabled && address >= SaveRamStartOffset && address < SaveRamEndOffset)
{
if (SaveRamEveryOtherByte)
SaveRAM[(address - SaveRamStartOffset) >> 1] = (byte)value;
else
SaveRAM[address - SaveRamStartOffset] = (byte)value;
SaveRamModified = true;
return;
}
if (EepromEnabled && (address == SclAddr || address == SdaInAddr))
{
WriteByteEeprom(address, (byte)value);
return;
}
Console.WriteLine("UNHANDLED WRITEB {0:X6}:{1:X2}", address, value);
}
public void WriteWord(int address, short value)
{
address &= 0x00FFFFFF;
if (address >= 0xE00000) // Work RAM
{
//Console.WriteLine("MEM[{0:X4}] change to {1:X4}", address & 0xFFFF, value);
Ram[(address & 0xFFFF) + 0] = (byte)(value >> 8);
Ram[(address & 0xFFFF) + 1] = (byte)value;
return;
}
if (address >= 0xC00000)
{
switch (address & 0x1F)
{
case 0x00:
case 0x02:
VDP.WriteVdpData((ushort)value);
return;
case 0x04:
case 0x06:
VDP.WriteVdpControl((ushort)value);
return;
}
}
if (address == 0xA11100) // Z80 BUSREQ
{
M68000HasZ80Bus = (value & 0x100) != 0;
//Console.WriteLine("68000 has the z80 bus: " + M68000HasZ80Bus);
return;
}
if (address == 0xA11200) // Z80 RESET
{
Z80Reset = (value & 0x100) == 0;
if (Z80Reset)
SoundCPU.SoftReset();
//Console.WriteLine("z80 reset: " + Z80Reset);
return;
}
Console.WriteLine("UNHANDLED WRITEW {0:X6}:{1:X4}", address, value);
}
public void WriteLong(int address, int value)
{
address &= 0x00FFFFFF;
if (address >= 0xE00000) // Work RAM
{
//Console.WriteLine("MEM[{0:X4}] change to {1:X8}", address & 0xFFFF, value);
Ram[(address & 0xFFFF) + 0] = (byte)(value >> 24);
Ram[(address & 0xFFFF) + 1] = (byte)(value >> 16);
Ram[(address & 0xFFFF) + 2] = (byte)(value >> 8);
Ram[(address & 0xFFFF) + 3] = (byte)value;
return;
}
if (address >= 0xC00000)
{
WriteWord(address, (short)(value >> 16));
WriteWord(address + 2, (short)value);
return;
}
Console.WriteLine("UNHANDLED WRITEL {0:X6}:{1:X8}", address, value);
}
// Mushashi interop test stuff. TODO kill this when we're ready to ditch musashi.
public uint Read8(uint a) { /*Console.WriteLine("read8 {0:X}", a);*/ return (uint)ReadByte((int)a) & 0xFF; }
public uint Read16(uint a) { /*Console.WriteLine("read16 {0:X}", a);*/ return (uint)ReadWord((int)a) & 0xFFFF; }
public uint Read32(uint a) { /*Console.WriteLine("read32 {0:X}", a);*/ return (uint)ReadLong((int)a); }
public void Write8(uint a, uint v) { /*Console.WriteLine("write8 {0:X}:{1:X2}", a, v);*/ WriteByte((int)a, (sbyte)v); }
public void Write16(uint a, uint v) { /*Console.WriteLine("write16 {0:X}:{1:X4}", a, v);*/ WriteWord((int)a, (short)v); }
public void Write32(uint a, uint v) { /*Console.WriteLine("write32 {0:X}:{1:X8}", a, v);*/ WriteLong((int)a, (int)v); }
}
}

View File

@ -1,84 +0,0 @@
using System;
namespace BizHawk.Emulation.Cores.Sega.Genesis
{
partial class Genesis
{
private int BankRegion;
public byte ReadMemoryZ80(ushort address)
{
if (address < 0x4000)
{
//Console.WriteLine("read z80 memory {0:X4}: {1:X2}",address, Z80Ram[address & 0x1FFF]);
return Z80Ram[address & 0x1FFF];
}
if (address >= 0x4000 && address < 0x6000)
{
//Console.WriteLine(" === Z80 READS FM STATUS ===");
return YM2612.ReadStatus(SoundCPU.TotalExecutedCycles); // TODO: more than 1 read port probably?
}
if (address >= 0x8000)
{
// 68000 Bank region
return (byte)ReadByte(BankRegion | (address & 0x7FFF));
}
if (address <= 0x6100) // read from bank address register - returns FF
return 0xFF;
Console.WriteLine("UNHANDLED Z80 READ {0:X4}", address);
return 0xCD;
}
public void WriteMemoryZ80(ushort address, byte value)
{
if (address < 0x4000)
{
//Console.WriteLine("write z80 memory {0:X4}: {1:X2}",address, value);
Z80Ram[address & 0x1FFF] = value;
return;
}
if (address >= 0x4000 && address < 0x6000)
{
//Console.WriteLine(" === Z80 WRITES YM2612 {0:X4}:{1:X2} ===",address, value);
YM2612.Write(address & 3, value, SoundCPU.TotalExecutedCycles);
return;
}
if (address < 0x6100)
{
BankRegion >>= 1;
BankRegion |= (value & 1) << 23;
BankRegion &= 0x00FF8000;
//Console.WriteLine("Bank pointing at {0:X8}",BankRegion);
return;
}
if (address >= 0x7F00 && address < 0x7F20)
{
switch (address & 0x1F)
{
case 0x00:
case 0x02:
VDP.WriteVdpData((ushort)((value << 8) | value));
return;
case 0x04:
case 0x06:
VDP.WriteVdpControl((ushort)((value << 8) | value));
return;
case 0x11:
case 0x13:
case 0x15:
case 0x17:
PSG.WritePsgData(value, SoundCPU.TotalExecutedCycles);
return;
}
}
if (address >= 0x8000)
{
WriteByte(BankRegion | (address & 0x7FFF), (sbyte)value);
return;
}
Console.WriteLine("UNHANDLED Z80 WRITE {0:X4}:{1:X2}", address, value);
}
}
}

View File

@ -1,92 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.IO;
namespace Native68000
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int VdpCallback(int i);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate uint ReadCallback(uint a);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void WriteCallback(uint a, uint v);
public class Musashi
{
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RegisterVdpCallback(IntPtr callback);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RegisterRead8(IntPtr callback);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RegisterRead16(IntPtr callback);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RegisterRead32(IntPtr callback);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RegisterWrite8(IntPtr callback);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RegisterWrite16(IntPtr callback);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RegisterWrite32(IntPtr callback);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Init();
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Reset();
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetIRQ(int level);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Execute(int cycles);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int QueryCpuState(int regcode);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetCpuState(int regcode, int value);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetCyclesRemaining();
public static int D0 { get { return QueryCpuState(0); } }
public static int D1 { get { return QueryCpuState(1); } }
public static int D2 { get { return QueryCpuState(2); } }
public static int D3 { get { return QueryCpuState(3); } }
public static int D4 { get { return QueryCpuState(4); } }
public static int D5 { get { return QueryCpuState(5); } }
public static int D6 { get { return QueryCpuState(6); } }
public static int D7 { get { return QueryCpuState(7); } }
public static int A0 { get { return QueryCpuState(8); } }
public static int A1 { get { return QueryCpuState(9); } }
public static int A2 { get { return QueryCpuState(10); } }
public static int A3 { get { return QueryCpuState(11); } }
public static int A4 { get { return QueryCpuState(12); } }
public static int A5 { get { return QueryCpuState(13); } }
public static int A6 { get { return QueryCpuState(14); } }
public static int A7 { get { return QueryCpuState(15); } }
public static int PC { get { return QueryCpuState(16); } }
public static int SR { get { return QueryCpuState(17); } }
public static int SP { get { return QueryCpuState(18); } }
public static void SaveStateBinary(BinaryWriter writer)
{
for (int i=0; i<31; i++)
writer.Write(QueryCpuState(i));
}
public static void LoadStateBinary(BinaryReader reader)
{
for (int i = 0; i < 31; i++)
SetCpuState(i, reader.ReadInt32());
}
}
}