initial ColecoVision commit. Needs BIOS ROM & input wired up to client (plus more emulation work)

This commit is contained in:
beirich 2012-11-17 17:39:33 +00:00
parent 11feb0d33a
commit 4c83970b2b
11 changed files with 527 additions and 1317 deletions

View File

@ -203,12 +203,10 @@
<Compile Include="Consoles\Atari\7800\EMU7800\TIASound.cs" />
<Compile Include="Consoles\Atari\7800\EMU7800\TIATables.cs" />
<Compile Include="Consoles\Calculator\TI83.cs" />
<Compile Include="Consoles\Coleco\ColecoVision.Core.cs" />
<Compile Include="Consoles\Coleco\ColecoVision.cs" />
<Compile Include="Consoles\Coleco\VDP.cs" />
<Compile Include="Consoles\Coleco\VDP.Mode4.cs" />
<Compile Include="Consoles\Coleco\VDP.ModeTMS.cs" />
<Compile Include="Consoles\Coleco\VDP.Tables.cs" />
<Compile Include="Consoles\Coleco\Input.cs" />
<Compile Include="Consoles\Coleco\MemoryMap.cs" />
<Compile Include="Consoles\Coleco\TMS9918A.cs" />
<Compile Include="Consoles\Intellivision\Cartridge.cs" />
<Compile Include="Consoles\Intellivision\ICart.cs" />
<Compile Include="Consoles\Intellivision\Intellicart.cs" />

View File

@ -1,104 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.CPUs.Z80;
using BizHawk.Emulation.Sound;
using BizHawk.Emulation.Consoles.Sega;
namespace BizHawk.Emulation.Consoles.Coleco
{
public partial class ColecoVision : IEmulator
{
public byte[] rom = new byte[2048];
public byte[] expansion = new byte[0x4000];
public byte[] cartridgeslot = new byte[0xFFFF]; //TODO: how big should this be?
public Z80A cpu;
public VDP Vdp; //adelikat: Using the SMS one for now
public byte ReadMemory(ushort addr)
{
byte ret;
if (addr < 0x2000)
{
ret = rom[addr];
}
else if (addr >= 0x2000 && addr < 0x6000)
{
ret = expansion[addr];
}
else if (addr >= 0x6000 && addr < 0x8000)
{
ret = ram[addr & 1023];
}
else if (addr >= 0x8000)
{
ret = cartridgeslot[addr];
}
else
{
ret = 0xFF;
}
if (CoreInputComm.MemoryCallbackSystem.HasRead)
{
CoreInputComm.MemoryCallbackSystem.TriggerRead(addr);
}
return ret;
}
public void WriteMemory(ushort addr, byte value)
{
if (addr >= 0x6000 && addr < 0x8000)
{
ram[addr] = value;
}
if (CoreInputComm.MemoryCallbackSystem.HasWrite)
{
CoreInputComm.MemoryCallbackSystem.TriggerWrite(addr);
}
}
public void HardReset()
{
_lagcount = 0;
cpu = new Z80A();
Vdp = new VDP(this, cpu, VdpMode.SMS, DisplayType);
cpu.ReadMemory = ReadMemory;
cpu.WriteMemory = WriteMemory;
}
public void FrameAdvance(bool render, bool rendersound)
{
_frame++;
_islag = true;
Vdp.ExecFrame(render);
//if (render == false) return;
//for (int i = 0; i < 256 * 192; i++)
// frameBuffer[i] = 0; //black
if (_islag)
_lagcount++;
}
public byte ReadControls()
{
if (CoreInputComm.InputCallback != null) CoreInputComm.InputCallback();
byte value = 0xFF;
if (Controller["P1 Up"]) value &= 0xFF; //TODO;
if (Controller["P1 Down"]) value &= 0xFF; //TODO;
if (Controller["P1 Left"]) value &= 0xFF; //TODO;
if (Controller["P1 Right"]) value &= 0xFF; //TODO;
//TODO: remaining buttons
_islag = false;
return value;
}
}
}

View File

@ -1,77 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using BizHawk.Emulation.CPUs.Z80;
using BizHawk.Emulation.Sound;
using BizHawk.Emulation.Consoles.Sega;
namespace BizHawk.Emulation.Consoles.Coleco
{
public partial class ColecoVision : IEmulator, IVideoProvider, ISoundProvider
public sealed partial class ColecoVision : IEmulator
{
public string SystemId { get { return "Coleco"; } }
public GameInfo game;
public int[] frameBuffer = new int[256 * 192];
public CoreInputComm CoreInputComm { get; set; }
public CoreOutputComm CoreOutputComm { get; private set; }
public IVideoProvider VideoProvider { get { return this; } }
public ISoundProvider SoundProvider { get { return this; } }
public ISyncSoundProvider SyncSoundProvider { get { return new FakeSyncSound(this, 735); } }
public bool StartAsyncSound() { return true; }
public void EndAsyncSound() { }
public byte[] ram = new byte[2048];
// ROM
public byte[] RomData;
public int RomLength;
public DisplayType DisplayType { get; set; } //TOOD: delete me
public byte[] BiosRom;
// Machine
public Z80A Cpu;
public TMS9918A VDP;
public SN76489 PSG;
public byte[] Ram = new byte[1024];
public ColecoVision(GameInfo game, byte[] rom)
{
cpu = new Z80A();
Vdp = new VDP(this, cpu, VdpMode.SMS, DisplayType);
Cpu = new Z80A();
Cpu.ReadMemory = ReadMemory;
Cpu.WriteMemory = WriteMemory;
Cpu.ReadHardware = ReadPort;
Cpu.WriteHardware = WritePort;
Cpu.Logger = (s) => Console.WriteLine(s);
//Cpu.Debug = true;
VDP = new TMS9918A(Cpu);
PSG = new SN76489();
// TODO: bios rom not hardcoded
// TODO: hack to allow bios-less operation would be nice, no idea if its feasible
BiosRom = File.ReadAllBytes("D:/coleco.rom");
var domains = new List<MemoryDomain>(1);
domains.Add(new MemoryDomain("Main RAM", 1024, Endian.Little, addr => ram[1023], (addr, value) => ram[addr & 1023] = value));
memoryDomains = domains.AsReadOnly();
CoreOutputComm = new CoreOutputComm();
CoreInputComm = new CoreInputComm();
this.rom = rom;
this.game = game;
HardReset();
LoadRom(rom);
this.game = game;
Reset();
}
public void ResetFrameCounter() { _frame = 0; }
public void FrameAdvance(bool render, bool renderSound)
{
PSG.BeginFrame(Cpu.TotalExecutedCycles);
VDP.ExecuteFrame();
PSG.EndFrame(Cpu.TotalExecutedCycles);
}
public static readonly ControllerDefinition ColecoVisionControllerDefinition = new ControllerDefinition
{
Name = "ColecoVision Basic Controller",
BoolButtons =
{
"P1 Up", "P1 Down", "P1 Left", "P1 Right",
"P1 L1", "P1 L2", "P1 R1", "P1 R2",
"P1 Key1", "P1 Key2", "P1 Key3", "P1 Key4", "P1 Key5",
"P1 Key6", "P1 Key7", "P1 Key8", "P1 Key9", "P1 Star", "P1 Pound" //adelikat: TODO: can there be multiple controllers?
}
};
void LoadRom(byte[] rom)
{
RomData = new byte[0x8000];
for (int i = 0; i < 0x8000; i++)
RomData[i] = rom[i % rom.Length];
}
void SyncState(Serializer ser)
{
//cpu.SyncState(ser); //TODO: z80 does not have this, do it the SMS way?
ser.Sync("ram", ref ram, false);
ser.Sync("Lag", ref _lagcount);
ser.Sync("Frame", ref _frame);
ser.Sync("IsLag", ref _islag);
}
void Reset()
{
/*Cpu.RegisterPC = Cpu.ReadWord(0x800A);
Console.WriteLine("code start vector = {0:X4}", Cpu.RegisterPC);*/
}
public ControllerDefinition ControllerDefinition { get { return ColecoVisionControllerDefinition; } }
public IController Controller { get; set; }
byte ReadPort(ushort port)
{
port &= 0xFF;
//Console.WriteLine("Read port {0:X2}", port);
public int Frame { get { return _frame; } set { _frame = value; } }
public int LagCount { get { return _lagcount; } set { _lagcount = value; } }
public bool IsLagFrame { get { return _islag; } }
private bool _islag = true;
private int _lagcount = 0;
private int _frame = 0;
if (port >= 0xA0 && port < 0xC0)
{
if ((port & 1) == 0)
return VDP.ReadData();
return VDP.ReadVdpStatus();
}
return 0xFF;
}
void WritePort(ushort port, byte value)
{
port &= 0xFF;
if (port >= 0xA0 && port < 0xC0)
{
if ((port & 1) == 0)
VDP.WriteVdpData(value);
else
VDP.WriteVdpControl(value);
return;
}
if (port >= 0xE0)
{
PSG.WritePsgData(value, Cpu.TotalExecutedCycles);
return;
}
//Console.WriteLine("Write port {0:X2}:{1:X2}", port, value);
}
public byte[] ReadSaveRam() { return null; }
public void StoreSaveRam(byte[] data) { }
@ -79,34 +110,31 @@ namespace BizHawk.Emulation.Consoles.Coleco
public bool SaveRamModified { get; set; }
public bool DeterministicEmulation { get { return true; } }
public void SaveStateText(TextWriter writer) { SyncState(Serializer.CreateTextWriter(writer)); }
public void LoadStateText(TextReader reader) { SyncState(Serializer.CreateTextReader(reader)); }
public void SaveStateBinary(BinaryWriter bw) { SyncState(Serializer.CreateBinaryWriter(bw)); }
public void LoadStateBinary(BinaryReader br) { SyncState(Serializer.CreateBinaryReader(br)); }
public void SaveStateText(TextWriter writer) { }
public void LoadStateText(TextReader reader) { }
public void SaveStateBinary(BinaryWriter bw) { }
public void LoadStateBinary(BinaryReader br) { }
public byte[] SaveStateBinary()
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
return new byte[0];
}
public int[] GetVideoBuffer() { return frameBuffer; }
public int VirtualWidth { get { return 256; } }
public int BufferWidth { get { return 256; } }
public int BufferHeight { get { return 192; } }
public int BackgroundColor { get { return 0; } }
public void GetSamples(short[] samples)
{
}
public void Dispose() { }
public void ResetFrameCounter() { }
public void DiscardSamples() { }
public int MaxVolume { get; set; }
private IList<MemoryDomain> memoryDomains;
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
public void Dispose() { }
public string SystemId { get { return "ColecoVision"; } }
public GameInfo game;
public CoreInputComm CoreInputComm { get; set; }
public CoreOutputComm CoreOutputComm { get; private set; }
public IVideoProvider VideoProvider { get { return VDP; } }
public ISoundProvider SoundProvider { get { return PSG; } }
public ISyncSoundProvider SyncSoundProvider { get { return null; } }
public bool StartAsyncSound() { return true; }
public void EndAsyncSound() { }
public IList<MemoryDomain> MemoryDomains { get { return null; } }
public MemoryDomain MainMemory { get { return null; } }
}
}

View File

@ -0,0 +1,32 @@
namespace BizHawk.Emulation.Consoles.Coleco
{
public partial class ColecoVision
{
public static readonly ControllerDefinition ColecoVisionControllerDefinition = new ControllerDefinition
{
Name = "ColecoVision Basic Controller",
BoolButtons =
{
"P1 Up", "P1 Down", "P1 Left", "P1 Right",
"P1 L1", "P1 L2", "P1 R1", "P1 R2",
"P1 Key1", "P1 Key2", "P1 Key3", "P1 Key4", "P1 Key5",
"P1 Key6", "P1 Key7", "P1 Key8", "P1 Key9", "P1 Star", "P1 Pound",
"P2 Up", "P2 Down", "P2 Left", "P2 Right",
"P2 L1", "P2 L2", "P2 R1", "P2 R2",
"P2 Key1", "P2 Key2", "P2 Key3", "P2 Key4", "P2 Key5",
"P2 Key6", "P2 Key7", "P2 Key8", "P2 Key9", "P2 Star", "P2 Pound"
}
};
public ControllerDefinition ControllerDefinition { get { return ColecoVisionControllerDefinition; } }
public IController Controller { get; set; }
public int Frame { get { return _frame; } /*set { _frame = value; }*/ }
public int LagCount { get { return _lagcount; } set { _lagcount = value; } }
public bool IsLagFrame { get { return _islag; } }
private bool _islag = true;
private int _lagcount = 0;
private int _frame = 0;
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.IO;
namespace BizHawk.Emulation.Consoles.Coleco
{
public partial class ColecoVision
{
public byte ReadMemory(ushort addr)
{
if (addr >= 0x8000)
return RomData[addr & 0x7FFF];
if (addr >= 0x6000)
return Ram[addr & 1023];
if (addr < 0x2000)
return BiosRom[addr];
//Console.WriteLine("Unhandled read at {0:X4}", addr);
return 0xFF;
}
public void WriteMemory(ushort addr, byte value)
{
if (addr >= 0x6000 && addr < 0x8000)
{
Ram[addr & 1023] = value;
return;
}
//Console.WriteLine("Unhandled write at {0:X4}:{1:X2}", addr, value);
}
}
}

View File

@ -0,0 +1,355 @@
using System;
using BizHawk.Emulation.CPUs.Z80;
namespace BizHawk.Emulation.Consoles.Coleco
{
public sealed class TMS9918A : IVideoProvider
{
byte[] VRAM = new byte[0x4000];
byte[] Registers = new byte[8];
byte StatusByte;
bool VdpWaitingForLatchByte;
byte VdpLatch;
ushort VdpAddress;
byte VdpBuffer;
VdpCommand vdpCommand; // TODO remove?
int TmsMode;
public bool Mode1Bit { get { return (Registers[1] & 16) > 0; } }
public bool Mode2Bit { get { return (Registers[0] & 2) > 0; } }
public bool Mode3Bit { get { return (Registers[1] & 8) > 0; } }
public bool EnableDoubledSprites { get { return (Registers[1] & 1) > 0; } }
public bool EnableLargeSprites { get { return (Registers[1] & 2) > 0; } }
public bool EnableInterrupts { get { return (Registers[1] & 32) > 0; } }
public bool DisplayOn { get { return (Registers[1] & 64) > 0; } }
// TODO, is 4/16K bit used?
//int NameTableBase;
int ColorTableBase;
int PatternGeneratorBase;
int SpritePatternGeneratorBase;
int TmsPatternNameTableBase;
int TmsSpriteAttributeBase;
public void ExecuteFrame()
{
for (int scanLine = 0; scanLine < 262; scanLine++)
{
RenderScanline(scanLine);
if (scanLine == 192)
{
// TODO interrupt pending thing
if (EnableInterrupts)
Cpu.NonMaskableInterrupt = true;
//Console.WriteLine("Set NMI / VSYNC");
}
Cpu.ExecuteCycles(228);
}
}
public void WriteVdpControl(byte value)
{
if (VdpWaitingForLatchByte)
{
VdpLatch = value;
VdpWaitingForLatchByte = false;
VdpAddress = (ushort)((VdpAddress & 0xFF00) | value);
return;
}
VdpWaitingForLatchByte = true;
VdpAddress = (ushort)(((value & 63) << 8) | VdpLatch);
switch (value & 0xC0)
{
case 0x00: // read VRAM
vdpCommand = VdpCommand.VramRead;
VdpBuffer = VRAM[VdpAddress & 0x3FFF];
VdpAddress++;
break;
case 0x40: // write VRAM
vdpCommand = VdpCommand.VramWrite;
break;
case 0x80: // VDP register write
vdpCommand = VdpCommand.RegisterWrite;
int reg = value & 0x0F;
WriteRegister(reg, VdpLatch);
break;
}
}
public void WriteVdpData(byte value)
{
VdpWaitingForLatchByte = true;
VdpBuffer = value;
// Write VRAM and update pre-computed pattern buffer.
//UpdatePatternBuffer((ushort)(VdpAddress & 0x3FFF), value);
//Console.WriteLine("VRAM[{0:X4}] = {1:X2}", VdpAddress & 0x3FFF, value);
VRAM[VdpAddress & 0x3FFF] = value;
VdpAddress++;
}
void WriteRegister(int reg, byte data)
{
if (reg >= 8) return;
Console.WriteLine("Write register {0} : {1:X2}", reg, data);
Registers[reg] = data;
switch (reg)
{
case 0: // Mode Control Register 1
CheckVideoMode();
break;
case 1: // Mode Control Register 2
CheckVideoMode();
//Cpu.NonMaskableInterrupt = (EnableInterrupts && VIntPending);
break;
case 2: // Name Table Base Address
//NameTableBase = CalcNameTableBase();
TmsPatternNameTableBase = (Registers[2] << 10) & 0x3C00;
break;
case 3: // Color Table Base Address
ColorTableBase = (Registers[3] << 6) & 0x3FC0;
break;
case 4: // Pattern Generator Base Address
PatternGeneratorBase = (Registers[4] << 11) & 0x3800;
break;
case 5: // Sprite Attribute Table Base Address
// ??? should I move from my property to precalculated?
TmsSpriteAttributeBase = (Registers[5] << 7) & 0x3F80;
break;
case 6: // Sprite Pattern Generator Base Adderss
SpritePatternGeneratorBase = (Registers[6] << 11) & 0x3800;
break;
}
}
public byte ReadVdpStatus()
{
VdpWaitingForLatchByte = true;
byte returnValue = StatusByte;
StatusByte &= 0x1F;
//VIntPending = false;
Cpu.NonMaskableInterrupt = false;
//Console.WriteLine("Clear NMI / read status");
return returnValue;
}
public byte ReadData()
{
VdpWaitingForLatchByte = true;
byte value = VdpBuffer;
VdpBuffer = VRAM[VdpAddress & 0x3FFF];
VdpAddress++;
return value;
}
void CheckVideoMode()
{
if (Mode1Bit) TmsMode = 1;
else if (Mode2Bit) TmsMode = 2;
else if (Mode3Bit) TmsMode = 3;
else TmsMode = 0;
Console.WriteLine("video mode {0}", TmsMode);
}
void RenderScanline(int scanLine)
{
if (scanLine >= 192)
return;
if (TmsMode == 2)
{
RenderBackgroundM2(scanLine);
RenderTmsSprites(scanLine);
}
else if (TmsMode == 0)
{
RenderBackgroundM0(scanLine);
RenderTmsSprites(scanLine);
}
}
void RenderBackgroundM0(int scanLine)
{
if (DisplayOn == false)
{
Array.Clear(FrameBuffer, scanLine * 256, 256);
return;
}
int yc = scanLine / 8;
int yofs = scanLine % 8;
int FrameBufferOffset = scanLine * 256;
int PatternNameOffset = TmsPatternNameTableBase + (yc * 32);
int ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (int xc = 0; xc < 32; xc++)
{
int pn = VRAM[PatternNameOffset++];
int pv = VRAM[PatternGeneratorBase + (pn * 8) + yofs];
int colorEntry = VRAM[ColorTableBase + (pn / 8)];
int fgIndex = (colorEntry >> 4) & 0x0F;
int bgIndex = colorEntry & 0x0F;
int fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
int bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x20) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x10) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x08) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x04) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x02) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x01) > 0) ? fgColor : bgColor;
}
}
void RenderBackgroundM2(int scanLine)
{
if (DisplayOn == false)
{
Array.Clear(FrameBuffer, scanLine * 256, 256);
return;
}
int yrow = scanLine / 8;
int yofs = scanLine % 8;
int FrameBufferOffset = scanLine * 256;
int PatternNameOffset = TmsPatternNameTableBase + (yrow * 32);
int PatternGeneratorOffset = (((Registers[4] & 4) << 11) & 0x2000);// +((yrow / 8) * 0x100);
int ColorOffset = (ColorTableBase & 0x2000);// +((yrow / 8) * 0x100);
int ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (int xc = 0; xc < 32; xc++)
{
int pn = VRAM[PatternNameOffset++] + ((yrow / 8) * 0x100);
int pv = VRAM[PatternGeneratorOffset + (pn * 8) + yofs];
int colorEntry = VRAM[ColorOffset + (pn * 8) + yofs];
int fgIndex = (colorEntry >> 4) & 0x0F;
int bgIndex = colorEntry & 0x0F;
int fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
int bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = ((pv & 0x80) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x40) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x20) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x10) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x08) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x04) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x02) > 0) ? fgColor : bgColor;
FrameBuffer[FrameBufferOffset++] = ((pv & 0x01) > 0) ? fgColor : bgColor;
}
}
byte[] ScanlinePriorityBuffer = new byte[256];
byte[] SpriteCollisionBuffer = new byte[256];
void RenderTmsSprites(int scanLine)
{
if (DisplayOn == false) return;
Array.Clear(ScanlinePriorityBuffer, 0, 256);
Array.Clear(SpriteCollisionBuffer, 0, 256);
bool Double = EnableDoubledSprites;
bool LargeSprites = EnableLargeSprites;
int SpriteSize = 8;
if (LargeSprites) SpriteSize *= 2;
if (Double) SpriteSize *= 2;
int OneCellSize = Double ? 16 : 8;
int NumSpritesOnScanline = 0;
for (int i = 0; i < 32; i++)
{
int SpriteBase = TmsSpriteAttributeBase + (i * 4);
int y = VRAM[SpriteBase++];
int x = VRAM[SpriteBase++];
int Pattern = VRAM[SpriteBase++];
int Color = VRAM[SpriteBase];
if (y == 208) break; // terminator sprite
if (y > 224) y -= 256; // sprite Y wrap
y++; // inexplicably, sprites start on Y+1
if (y > scanLine || y + SpriteSize <= scanLine) continue; // sprite is not on this scanline
if ((Color & 0x80) > 0) x -= 32; // Early Clock adjustment
if (++NumSpritesOnScanline == 5)
{
StatusByte |= (byte)i; // set 5th sprite index
StatusByte |= 0x40; // set overflow bit
break;
}
if (LargeSprites) Pattern &= 0xFC; // 16x16 sprites forced to 4-byte alignment
int SpriteLine = scanLine - y;
if (Double) SpriteLine /= 2;
byte pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine];
for (int xp = 0; xp < SpriteSize && x + xp < 256; xp++)
{
if (x + xp < 0) continue;
if (LargeSprites && xp == OneCellSize)
pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine + 16];
if ((pv & (1 << (7 - (xp & 7)))) > 0)
{
// todo sprite collision
if (Color != 0 && ScanlinePriorityBuffer[x + xp] == 0)
{
ScanlinePriorityBuffer[x + xp] = 1;
FrameBuffer[(scanLine * 256) + x + xp] = PaletteTMS9918[Color & 0x0F];
}
}
}
}
}
Z80A Cpu;
public TMS9918A(Z80A cpu)
{
this.Cpu = cpu;
}
public int[] FrameBuffer = new int[256 * 192];
public int[] GetVideoBuffer() { return FrameBuffer; }
public int VirtualWidth { get { return 256; } }
public int BufferWidth { get { return 256; } }
public int BufferHeight { get { return 192; } }
public int BackgroundColor { get { return 0; } }
enum VdpCommand { VramRead, VramWrite, RegisterWrite }
int[] PaletteTMS9918 = new int[]
{
unchecked((int)0x00000000),
unchecked((int)0xFF000000),
unchecked((int)0xFF47B73B),
unchecked((int)0xFF7CCF6F),
unchecked((int)0xFF5D4EFF),
unchecked((int)0xFF8072FF),
unchecked((int)0xFFB66247),
unchecked((int)0xFF5DC8ED),
unchecked((int)0xFFD76B48),
unchecked((int)0xFFFB8F6C),
unchecked((int)0xFFC3CD41),
unchecked((int)0xFFD3DA76),
unchecked((int)0xFF3E9F2F),
unchecked((int)0xFFB664C7),
unchecked((int)0xFFCCCCCC),
unchecked((int)0xFFFFFFFF)
};
}
}

View File

@ -1,299 +0,0 @@
using System;
// Contains rendering functions for TMS9918 Mode 4.
namespace BizHawk.Emulation.Consoles.Coleco
{
public partial class VDP
{
internal void RenderBackgroundCurrentLine(bool show)
{
if (DisplayOn == false)
{
for (int x = 0; x < 256; x++)
FrameBuffer[(ScanLine*256) + x] = Palette[BackdropColor];
return;
}
// Clear the priority buffer for this scanline
Array.Clear(ScanlinePriorityBuffer, 0, 256);
int mapBase = NameTableBase;
int vertOffset = ScanLine + Registers[9];
if (FrameHeight == 192)
{
if (vertOffset >= 224)
vertOffset -= 224;
}
else
{
if (vertOffset >= 256)
vertOffset -= 256;
}
byte horzOffset = (HorizScrollLock && ScanLine < 16) ? (byte)0 : Registers[8];
int yTile = vertOffset / 8;
for (int xTile = 0; xTile < 32; xTile++)
{
if (xTile == 24 && VerticalScrollLock)
{
vertOffset = ScanLine;
yTile = vertOffset / 8;
}
byte PaletteBase = 0;
int tileInfo = VRAM[mapBase + ((yTile * 32) + xTile) * 2] | (VRAM[mapBase + (((yTile * 32) + xTile) * 2) + 1] << 8);
int tileNo = tileInfo & 0x01FF;
if ((tileInfo & 0x800) != 0)
PaletteBase = 16;
bool Priority = (tileInfo & 0x1000) != 0;
bool VFlip = (tileInfo & 0x400) != 0;
bool HFlip = (tileInfo & 0x200) != 0;
int yOfs = vertOffset & 7;
if (VFlip)
yOfs = 7 - yOfs;
if (HFlip == false)
{
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 0] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 1] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 2] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 3] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 4] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 5] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 6] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 7] + PaletteBase] : Palette[BackdropColor];
if (Priority)
{
horzOffset -= 8;
for (int k = 0; k < 8; k++)
{
if (PatternBuffer[(tileNo * 64) + (yOfs * 8) + k] != 0)
ScanlinePriorityBuffer[horzOffset] = 1;
horzOffset++;
}
}
}
else // Flipped Horizontally
{
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 7] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 6] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 5] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 4] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 3] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 2] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 1] + PaletteBase] : Palette[BackdropColor];
FrameBuffer[(ScanLine * 256) + horzOffset++] = show ? Palette[PatternBuffer[(tileNo * 64) + (yOfs * 8) + 0] + PaletteBase] : Palette[BackdropColor];
if (Priority)
{
horzOffset -= 8;
for (int k = 7; k >= 0; k--)
{
if (PatternBuffer[(tileNo * 64) + (yOfs * 8) + k] != 0)
ScanlinePriorityBuffer[horzOffset] = 1;
horzOffset++;
}
}
}
}
}
internal void RenderSpritesCurrentLine(bool show)
{
if (DisplayOn == false) return;
int SpriteBase = SpriteAttributeTableBase;
int SpriteHeight = EnableLargeSprites ? 16 : 8;
// Clear the sprite collision buffer for this scanline
Array.Clear(SpriteCollisionBuffer, 0, 256);
// Loop through these sprites and render the current scanline
int SpritesDrawnThisScanline = 0;
for (int i=0; i<64; i++)
{
if (SpritesDrawnThisScanline >= 8)
{
StatusByte |= 0x40; // Set Overflow bit
if (SpriteLimit) break;
}
int x = VRAM[SpriteBase + 0x80 + (i * 2)];
if (ShiftSpritesLeft8Pixels)
x -= 8;
int y = VRAM[SpriteBase + i] + 1;
if (y == 209 && FrameHeight == 192) break; // 208 is special terminator sprite (in 192-line mode)
if (y >= (EnableLargeSprites ? 240 : 248)) y -= 256;
if (y + SpriteHeight <= ScanLine || y > ScanLine)
continue;
int tileNo = VRAM[SpriteBase + 0x80 + (i * 2) + 1];
if (EnableLargeSprites)
tileNo &= 0xFE;
tileNo += SpriteTileBase;
int ys = ScanLine - y;
for (int xs = 0; xs < 8 && x + xs < 256; xs++)
{
byte color = PatternBuffer[(tileNo * 64) + (ys * 8) + xs];
if (color != 0 && x + xs >= 0)
{
if (SpriteCollisionBuffer[x + xs] != 0)
StatusByte |= 0x20; // Set Collision bit
else if (ScanlinePriorityBuffer[x + xs] == 0)
{
if (show) FrameBuffer[(ys + y) * 256 + x + xs] = Palette[(color + 16)];
SpriteCollisionBuffer[x + xs] = 1;
}
}
}
SpritesDrawnThisScanline++;
}
}
internal void RenderSpritesCurrentLineDoubleSize(bool show)
{
if (DisplayOn == false) return;
int SpriteBase = SpriteAttributeTableBase;
int SpriteHeight = EnableLargeSprites ? 16 : 8;
// Clear the sprite collision buffer for this scanline
Array.Clear(SpriteCollisionBuffer, 0, 256);
// Loop through these sprites and render the current scanline
int SpritesDrawnThisScanline = 0;
for (int i = 0; i <64; i++)
{
if (SpritesDrawnThisScanline >= 8)
{
StatusByte |= 0x40; // Set Overflow bit
if (SpriteLimit) break;
}
int x = VRAM[SpriteBase + 0x80 + (i * 2)];
if (ShiftSpritesLeft8Pixels)
x -= 8;
int y = VRAM[SpriteBase + i] + 1;
if (y == 209 && FrameHeight == 192) break; // terminator sprite
if (y >= (EnableLargeSprites ? 240 : 248)) y -= 256;
if (y + (SpriteHeight*2) <= ScanLine || y > ScanLine)
continue;
int tileNo = VRAM[SpriteBase + 0x80 + (i * 2) + 1];
if (EnableLargeSprites)
tileNo &= 0xFE;
tileNo += SpriteTileBase;
int ys = ScanLine - y;
for (int xs = 0; xs < 16 && x + xs < 256; xs++)
{
byte color = PatternBuffer[(tileNo * 64) + ((ys/2) * 8) + (xs/2)];
if (color != 0 && x + xs >= 0 && ScanlinePriorityBuffer[x + xs] == 0)
{
if (SpriteCollisionBuffer[x + xs] != 0)
StatusByte |= 0x20; // Set Collision bit
else
{
if (show) FrameBuffer[(ys + y) * 256 + x + xs] = Palette[(color + 16)];
SpriteCollisionBuffer[x + xs] = 1;
}
}
}
SpritesDrawnThisScanline++;
}
}
internal void ProcessSpriteCollisionForFrameskip()
{
if (DisplayOn == false) return;
int SpriteBase = SpriteAttributeTableBase;
int SpriteHeight = EnableLargeSprites ? 16 : 8;
// Clear the sprite collision buffer for this scanline
Array.Clear(SpriteCollisionBuffer, 0, 256);
// 208 is a special terminator sprite (in 192-line mode). Lets find it...
int TerminalSprite = 64;
if (FrameHeight == 192)
for (int i = 0; i < 64; i++)
{
if (VRAM[SpriteBase + i] == 208)
{
TerminalSprite = i;
break;
}
}
// Loop through these sprites and render the current scanline
int SpritesDrawnThisScanline = 0;
for (int i = TerminalSprite - 1; i >= 0; i--)
{
if (SpritesDrawnThisScanline >= 8)
StatusByte |= 0x40; // Set Overflow bit
int x = VRAM[SpriteBase + 0x80 + (i * 2)];
if (ShiftSpritesLeft8Pixels)
x -= 8;
int y = VRAM[SpriteBase + i] + 1;
if (y >= (EnableLargeSprites ? 240 : 248)) y -= 256;
if (y + SpriteHeight <= ScanLine || y > ScanLine)
continue;
int tileNo = VRAM[SpriteBase + 0x80 + (i * 2) + 1];
if (EnableLargeSprites)
tileNo &= 0xFE;
tileNo += SpriteTileBase;
int ys = ScanLine - y;
for (int xs = 0; xs < 8 && x + xs < 256; xs++)
{
byte color = PatternBuffer[(tileNo * 64) + (ys * 8) + xs];
if (color != 0 && x + xs >= 0)
{
if (SpriteCollisionBuffer[x + xs] != 0)
StatusByte |= 0x20; // Set Collision bit
SpriteCollisionBuffer[x + xs] = 1;
}
}
SpritesDrawnThisScanline++;
}
}
// Performs render buffer blanking. This includes the left-column blanking as well as Game Gear blanking if requested.
// Should be called at the end of the frame.
internal void RenderBlankingRegions()
{
int blankingColor = Palette[BackdropColor];
if (LeftBlanking)
{
for (int y = 0; y < FrameHeight; y++)
{
for (int x = 0; x < 8; x++)
FrameBuffer[(y * 256) + x] = blankingColor;
}
}
if (mode == VdpMode.GameGear)
{
int yStart = (FrameHeight - 144)/2;
for (int y = 0; y < 144; y++)
for (int x = 0; x < 160; x++)
GameGearFrameBuffer[(y * 160) + x] = FrameBuffer[((y + yStart) * 256) + x + 48];
}
}
}
}

View File

@ -1,163 +0,0 @@
// Contains rendering functions for legacy TMS9918 modes.
using System;
namespace BizHawk.Emulation.Consoles.Coleco
{
public partial class VDP
{
int[] PaletteTMS9918 = new int[]
{
unchecked((int)0x00000000),
unchecked((int)0xFF000000),
unchecked((int)0xFF47B73B),
unchecked((int)0xFF7CCF6F),
unchecked((int)0xFF5D4EFF),
unchecked((int)0xFF8072FF),
unchecked((int)0xFFB66247),
unchecked((int)0xFF5DC8ED),
unchecked((int)0xFFD76B48),
unchecked((int)0xFFFB8F6C),
unchecked((int)0xFFC3CD41),
unchecked((int)0xFFD3DA76),
unchecked((int)0xFF3E9F2F),
unchecked((int)0xFFB664C7),
unchecked((int)0xFFCCCCCC),
unchecked((int)0xFFFFFFFF)
};
void RenderBackgroundM0(bool show)
{
if (DisplayOn == false)
{
Array.Clear(FrameBuffer, ScanLine * 256, 256);
return;
}
int yc = ScanLine/8;
int yofs = ScanLine%8;
int FrameBufferOffset = ScanLine*256;
int PatternNameOffset = TmsPatternNameTableBase + (yc*32);
int ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (int xc=0; xc<32; xc++)
{
int pn = VRAM[PatternNameOffset++];
int pv = VRAM[PatternGeneratorBase + (pn*8) + yofs];
int colorEntry = VRAM[ColorTableBase + (pn/8)];
int fgIndex = (colorEntry >> 4) & 0x0F;
int bgIndex = colorEntry & 0x0F;
int fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
int bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x80) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x40) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x20) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x10) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x08) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x04) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x02) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x01) > 0) ? fgColor : bgColor) : 0;
}
}
void RenderBackgroundM2(bool show)
{
if (DisplayOn == false)
{
Array.Clear(FrameBuffer, ScanLine * 256, 256);
return;
}
int yrow = ScanLine/8;
int yofs = ScanLine%8;
int FrameBufferOffset = ScanLine*256;
int PatternNameOffset = TmsPatternNameTableBase + (yrow*32);
int PatternGeneratorOffset = (((Registers[4] & 4) << 11) & 0x2000);// +((yrow / 8) * 0x100);
int ColorOffset = (ColorTableBase & 0x2000);// +((yrow / 8) * 0x100);
int ScreenBGColor = PaletteTMS9918[Registers[7] & 0x0F];
for (int xc=0; xc<32; xc++)
{
int pn = VRAM[PatternNameOffset++] + ((yrow/8)*0x100);
int pv = VRAM[PatternGeneratorOffset + (pn * 8) + yofs];
int colorEntry = VRAM[ColorOffset + (pn * 8) + yofs];
int fgIndex = (colorEntry >> 4) & 0x0F;
int bgIndex = colorEntry & 0x0F;
int fgColor = fgIndex == 0 ? ScreenBGColor : PaletteTMS9918[fgIndex];
int bgColor = bgIndex == 0 ? ScreenBGColor : PaletteTMS9918[bgIndex];
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x80) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x40) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x20) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x10) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x08) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x04) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x02) > 0) ? fgColor : bgColor) : 0;
FrameBuffer[FrameBufferOffset++] = show ? (((pv & 0x01) > 0) ? fgColor : bgColor) : 0;
}
}
void RenderTmsSprites(bool show)
{
if (DisplayOn == false) return;
Array.Clear(ScanlinePriorityBuffer, 0, 256);
Array.Clear(SpriteCollisionBuffer, 0, 256);
bool Double = EnableDoubledSprites;
bool LargeSprites = EnableLargeSprites;
int SpriteSize = 8;
if (LargeSprites) SpriteSize *= 2;
if (Double) SpriteSize *= 2;
int OneCellSize = Double ? 16 : 8;
int NumSpritesOnScanline = 0;
for (int i=0; i<32; i++)
{
int SpriteBase = TmsSpriteAttributeBase + (i*4);
int y = VRAM[SpriteBase++];
int x = VRAM[SpriteBase++];
int Pattern = VRAM[SpriteBase++];
int Color = VRAM[SpriteBase];
if (y == 208) break; // terminator sprite
if (y > 224) y -= 256; // sprite Y wrap
y++; // inexplicably, sprites start on Y+1
if (y > ScanLine || y + SpriteSize <= ScanLine) continue; // sprite is not on this scanline
if ((Color & 0x80) > 0) x -= 32; // Early Clock adjustment
if (++NumSpritesOnScanline == 5)
{
StatusByte |= (byte) i; // set 5th sprite index
StatusByte |= 0x40; // set overflow bit
break;
}
if (LargeSprites) Pattern &= 0xFC; // 16x16 sprites forced to 4-byte alignment
int SpriteLine = ScanLine - y;
if (Double) SpriteLine /= 2;
byte pv = VRAM[SpritePatternGeneratorBase + (Pattern*8) + SpriteLine];
for (int xp = 0; xp < SpriteSize && x + xp < 256; xp++)
{
if (x+xp < 0) continue;
if (LargeSprites && xp == OneCellSize)
pv = VRAM[SpritePatternGeneratorBase + (Pattern * 8) + SpriteLine + 16];
if ((pv & (1 << (7 - (xp & 7)))) > 0)
{
// todo sprite collision
if (Color != 0 && ScanlinePriorityBuffer[x+xp] == 0)
{
ScanlinePriorityBuffer[x + xp] = 1;
if (show) FrameBuffer[(ScanLine*256) + x + xp] = PaletteTMS9918[Color & 0x0F];
}
}
}
}
}
}
}

View File

@ -1,143 +0,0 @@
namespace BizHawk.Emulation.Consoles.Coleco
{
public partial class VDP
{
// TODO: HCounter
readonly byte[] VLineCounterTableNTSC192 =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
};
readonly byte[] VLineCounterTableNTSC224 =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA,
0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
};
readonly byte[] VLineCounterTableNTSC240 =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05
};
readonly byte[] VLineCounterTablePAL192 =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2,
0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
readonly byte[] VLineCounterTablePAL224 =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
0x00, 0x01, 0x02,
0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
readonly byte[] VLineCounterTablePAL240 =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
}
}

View File

@ -1,526 +0,0 @@
using System;
using System.Globalization;
using System.IO;
using BizHawk.Emulation.CPUs.Z80;
namespace BizHawk.Emulation.Consoles.Coleco
{
public enum VdpMode { SMS, GameGear } //TODO: delete me
// Emulates the Texas Instruments TMS9918 VDP.
public sealed partial class VDP : IVideoProvider
{
// VDP State
public byte[] VRAM = new byte[0x4000]; //16kb video RAM
public byte[] CRAM; // SMS = 32 bytes, GG = 64 bytes CRAM
public byte[] Registers = new byte[] { 0x06, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xF0, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00 };
public byte StatusByte;
bool VdpWaitingForLatchByte = true;
byte VdpLatch;
byte VdpBuffer;
ushort VdpAddress;
VdpCommand vdpCommand;
int TmsMode = 4;
bool VIntPending;
bool HIntPending;
ColecoVision Coleco;
VdpMode mode;
DisplayType DisplayType = DisplayType.NTSC;
Z80A Cpu;
public bool SpriteLimit;
public int IPeriod = 228;
public VdpMode VdpMode { get { return mode; } }
int FrameHeight = 192;
public int ScanLine;
public int[] FrameBuffer = new int[256 * 192];
public int[] GameGearFrameBuffer = new int[160 * 144];
public bool Mode1Bit { get { return (Registers[1] & 16) > 0; } }
public bool Mode2Bit { get { return (Registers[0] & 2) > 0; } }
public bool Mode3Bit { get { return (Registers[1] & 8) > 0; } }
public bool Mode4Bit { get { return (Registers[0] & 4) > 0; } }
public bool ShiftSpritesLeft8Pixels { get { return (Registers[0] & 8) > 0; } }
public bool EnableLineInterrupts { get { return (Registers[0] & 16) > 0; } }
public bool LeftBlanking { get { return (Registers[0] & 32) > 0; } }
public bool HorizScrollLock { get { return (Registers[0] & 64) > 0; } }
public bool VerticalScrollLock { get { return (Registers[0] & 128) > 0; } }
public bool EnableDoubledSprites { get { return (Registers[1] & 1) > 0; } }
public bool EnableLargeSprites { get { return (Registers[1] & 2) > 0; } }
public bool EnableFrameInterrupts { get { return (Registers[1] & 32) > 0; } }
public bool DisplayOn { get { return (Registers[1] & 64) > 0; } }
public int SpriteAttributeTableBase { get { return ((Registers[5] >> 1) << 8) & 0x3FFF; } }
public int SpriteTileBase { get { return (Registers[6] & 4) > 0 ? 256 : 0; } }
public byte BackdropColor { get { return (byte)(16 + (Registers[7] & 15)); } }
int NameTableBase;
int ColorTableBase;
int PatternGeneratorBase;
int SpritePatternGeneratorBase;
int TmsPatternNameTableBase;
int TmsSpriteAttributeBase;
// preprocessed state assist stuff.
public int[] Palette = new int[32];
public byte[] PatternBuffer = new byte[0x8000];
byte[] ScanlinePriorityBuffer = new byte[256];
byte[] SpriteCollisionBuffer = new byte[256];
static readonly byte[] SMSPalXlatTable = { 0, 85, 170, 255 };
static readonly byte[] GGPalXlatTable = { 0, 17, 34, 51, 68, 85, 102, 119, 136, 153, 170, 187, 204, 221, 238, 255 };
public VDP(ColecoVision sms, Z80A cpu, VdpMode mode, DisplayType displayType)
{
Coleco = sms;
Cpu = cpu;
this.mode = mode;
if (mode == VdpMode.SMS) CRAM = new byte[32];
if (mode == VdpMode.GameGear) CRAM = new byte[64];
DisplayType = displayType;
NameTableBase = CalcNameTableBase();
}
public byte ReadData()
{
VdpWaitingForLatchByte = true;
byte value = VdpBuffer;
VdpBuffer = VRAM[VdpAddress & 0x3FFF];
VdpAddress++;
return value;
}
public byte ReadVdpStatus()
{
VdpWaitingForLatchByte = true;
byte returnValue = StatusByte;
StatusByte &= 0x1F;
HIntPending = false;
VIntPending = false;
Cpu.Interrupt = false;
return returnValue;
}
public byte ReadVLineCounter()
{
if (DisplayType == DisplayType.NTSC)
{
if (FrameHeight == 192)
return VLineCounterTableNTSC192[ScanLine];
if (FrameHeight == 224)
return VLineCounterTableNTSC224[ScanLine];
return VLineCounterTableNTSC240[ScanLine];
}
else
{ // PAL
if (FrameHeight == 192)
return VLineCounterTablePAL192[ScanLine];
if (FrameHeight == 224)
return VLineCounterTablePAL224[ScanLine];
return VLineCounterTablePAL240[ScanLine];
}
}
public void WriteVdpControl(byte value)
{
if (VdpWaitingForLatchByte)
{
VdpLatch = value;
VdpWaitingForLatchByte = false;
VdpAddress = (ushort)((VdpAddress & 0xFF00) | value);
return;
}
VdpWaitingForLatchByte = true;
VdpAddress = (ushort)(((value & 63) << 8) | VdpLatch);
switch (value & 0xC0)
{
case 0x00: // read VRAM
vdpCommand = VdpCommand.VramRead;
VdpBuffer = VRAM[VdpAddress & 0x3FFF];
VdpAddress++;
break;
case 0x40: // write VRAM
vdpCommand = VdpCommand.VramWrite;
break;
case 0x80: // VDP register write
vdpCommand = VdpCommand.RegisterWrite;
int reg = value & 0x0F;
WriteRegister(reg, VdpLatch);
break;
case 0xC0: // write CRAM / modify palette
vdpCommand = VdpCommand.CramWrite;
break;
}
}
public void WriteVdpData(byte value)
{
VdpWaitingForLatchByte = true;
VdpBuffer = value;
if (vdpCommand == VdpCommand.CramWrite)
{
// Write Palette / CRAM
int mask = VdpMode == VdpMode.SMS ? 0x1F : 0x3F;
CRAM[VdpAddress & mask] = value;
UpdatePrecomputedPalette();
}
else
{
// Write VRAM and update pre-computed pattern buffer.
UpdatePatternBuffer((ushort)(VdpAddress & 0x3FFF), value);
VRAM[VdpAddress & 0x3FFF] = value;
}
VdpAddress++;
}
public void UpdatePrecomputedPalette()
{
if (mode == VdpMode.SMS)
{
for (int i = 0; i < 32; i++)
{
byte value = CRAM[i];
byte r = SMSPalXlatTable[(value & 0x03)];
byte g = SMSPalXlatTable[(value & 0x0C) >> 2];
byte b = SMSPalXlatTable[(value & 0x30) >> 4];
Palette[i] = Colors.ARGB(r, g, b);
}
}
else
{ // GameGear
for (int i = 0; i < 32; i++)
{
ushort value = (ushort)((CRAM[(i * 2) + 1] << 8) | CRAM[(i * 2) + 0]);
byte r = GGPalXlatTable[(value & 0x000F)];
byte g = GGPalXlatTable[(value & 0x00F0) >> 4];
byte b = GGPalXlatTable[(value & 0x0F00) >> 8];
Palette[i] = Colors.ARGB(r, g, b);
}
}
}
public int CalcNameTableBase()
{
if (FrameHeight == 192)
return 1024 * (Registers[2] & 0x0E);
return (1024 * (Registers[2] & 0x0C)) + 0x0700;
}
void CheckVideoMode()
{
if (Mode4Bit == false) // check old TMS modes
{
if (Mode1Bit) TmsMode = 1;
else if (Mode2Bit) TmsMode = 2;
else if (Mode3Bit) TmsMode = 3;
else TmsMode = 0;
}
else if (Mode4Bit && Mode2Bit) // if Mode4 and Mode2 set, then check extension modes
{
TmsMode = 4;
switch (Registers[1] & 0x18)
{
case 0x00:
case 0x18: // 192-line mode
if (FrameHeight != 192)
{
FrameHeight = 192;
FrameBuffer = new int[256 * 192];
NameTableBase = CalcNameTableBase();
}
break;
case 0x10: // 224-line mode
if (FrameHeight != 224)
{
FrameHeight = 224;
FrameBuffer = new int[256 * 224];
NameTableBase = CalcNameTableBase();
}
break;
case 0x08: // 240-line mode
if (FrameHeight != 240)
{
FrameHeight = 240;
FrameBuffer = new int[256 * 240];
NameTableBase = CalcNameTableBase();
}
break;
}
}
else
{ // default to standard 192-line mode4
TmsMode = 4;
if (FrameHeight != 192)
{
FrameHeight = 192;
FrameBuffer = new int[256 * 192];
NameTableBase = CalcNameTableBase();
}
}
}
void WriteRegister(int reg, byte data)
{
Registers[reg] = data;
switch (reg)
{
case 0: // Mode Control Register 1
CheckVideoMode();
Cpu.Interrupt = (EnableLineInterrupts && HIntPending);
break;
case 1: // Mode Control Register 2
CheckVideoMode();
Cpu.Interrupt = (EnableFrameInterrupts && VIntPending);
break;
case 2: // Name Table Base Address
NameTableBase = CalcNameTableBase();
TmsPatternNameTableBase = (Registers[2] << 10) & 0x3C00;
break;
case 3: // Color Table Base Address
ColorTableBase = (Registers[3] << 6) & 0x3FC0;
break;
case 4: // Pattern Generator Base Address
PatternGeneratorBase = (Registers[4] << 11) & 0x3800;
break;
case 5: // Sprite Attribute Table Base Address
// ??? should I move from my property to precalculated?
TmsSpriteAttributeBase = (Registers[5] << 7) & 0x3F80;
break;
case 6: // Sprite Pattern Generator Base Adderss
SpritePatternGeneratorBase = (Registers[6] << 11) & 0x3800;
break;
}
}
static readonly byte[] pow2 = { 1, 2, 4, 8, 16, 32, 64, 128 };
void UpdatePatternBuffer(ushort address, byte value)
{
// writing one byte affects 8 pixels due to stupid planar storage.
for (int i = 0; i < 8; i++)
{
byte colorBit = pow2[address % 4];
byte sourceBit = pow2[7 - i];
ushort dest = (ushort)(((address & 0xFFFC) * 2) + i);
if ((value & sourceBit) > 0) // setting bit
PatternBuffer[dest] |= colorBit;
else // clearing bit
PatternBuffer[dest] &= (byte)~colorBit;
}
}
int lineIntLinesRemaining;
void ProcessFrameInterrupt()
{
if (ScanLine == FrameHeight + 1)
{
StatusByte |= 0x80;
VIntPending = true;
}
if (VIntPending && EnableFrameInterrupts)
Cpu.Interrupt = true;
}
void ProcessLineInterrupt()
{
if (ScanLine <= FrameHeight)
{
if (lineIntLinesRemaining-- <= 0)
{
HIntPending = true;
if (EnableLineInterrupts)
Cpu.Interrupt = true;
lineIntLinesRemaining = Registers[0x0A];
}
return;
}
// else we're outside the active display period
lineIntLinesRemaining = Registers[0x0A];
}
public void ExecFrame(bool render)
{
int scanlinesPerFrame = DisplayType == DisplayType.NTSC ? 262 : 313;
for (ScanLine = 0; ScanLine < scanlinesPerFrame; ScanLine++)
{
RenderCurrentScanline(render);
ProcessFrameInterrupt();
ProcessLineInterrupt();
Cpu.ExecuteCycles(IPeriod);
if (ScanLine == scanlinesPerFrame - 1)
RenderBlankingRegions();
}
}
internal void RenderCurrentScanline(bool render)
{
if (ScanLine >= FrameHeight)
return;
if (TmsMode == 4)
{
if (render == false)
{
ProcessSpriteCollisionForFrameskip();
return;
}
RenderBackgroundCurrentLine(Coleco.CoreInputComm.SMS_ShowBG);
if (EnableDoubledSprites)
RenderSpritesCurrentLineDoubleSize(Coleco.CoreInputComm.SMS_ShowOBJ);
else
RenderSpritesCurrentLine(Coleco.CoreInputComm.SMS_ShowOBJ);
}
else if (TmsMode == 2)
{
if (render == false)
return;
RenderBackgroundM2(Coleco.CoreInputComm.SMS_ShowBG);
RenderTmsSprites(Coleco.CoreInputComm.SMS_ShowOBJ);
}
else if (TmsMode == 0)
{
if (render == false)
return;
RenderBackgroundM0(Coleco.CoreInputComm.SMS_ShowBG);
RenderTmsSprites(Coleco.CoreInputComm.SMS_ShowOBJ);
}
}
public void SaveStateText(TextWriter writer)
{
writer.WriteLine("[VDP]");
writer.WriteLine("StatusByte {0:X2}", StatusByte);
writer.WriteLine("WaitingForLatchByte {0}", VdpWaitingForLatchByte);
writer.WriteLine("Latch {0:X2}", VdpLatch);
writer.WriteLine("ReadBuffer {0:X2}", VdpBuffer);
writer.WriteLine("VdpAddress {0:X4}", VdpAddress);
writer.WriteLine("Command " + Enum.GetName(typeof(VdpCommand), vdpCommand));
writer.Write("Registers ");
Registers.SaveAsHex(writer);
writer.Write("CRAM ");
CRAM.SaveAsHex(writer);
writer.Write("VRAM ");
VRAM.SaveAsHex(writer);
writer.WriteLine("[/VDP]");
writer.WriteLine();
}
public void LoadStateText(TextReader reader)
{
while (true)
{
string[] args = reader.ReadLine().Split(' ');
if (args[0].Trim() == "") continue;
if (args[0] == "[/VDP]") break;
if (args[0] == "StatusByte")
StatusByte = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "WaitingForLatchByte")
VdpWaitingForLatchByte = bool.Parse(args[1]);
else if (args[0] == "Latch")
VdpLatch = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "ReadBuffer")
VdpBuffer = byte.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "VdpAddress")
VdpAddress = ushort.Parse(args[1], NumberStyles.HexNumber);
else if (args[0] == "Command")
vdpCommand = (VdpCommand)Enum.Parse(typeof(VdpCommand), args[1]);
else if (args[0] == "Registers")
Registers.ReadFromHex(args[1]);
else if (args[0] == "CRAM")
{
CRAM.ReadFromHex(args[1]);
UpdatePrecomputedPalette();
}
else if (args[0] == "VRAM")
{
VRAM.ReadFromHex(args[1]);
for (ushort i = 0; i < VRAM.Length; i++)
UpdatePatternBuffer(i, VRAM[i]);
}
else
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
}
for (int i = 0; i < Registers.Length; i++)
WriteRegister(i, Registers[i]);
}
public void SaveStateBinary(BinaryWriter writer)
{
writer.Write(StatusByte);
writer.Write(VdpWaitingForLatchByte);
writer.Write(VdpLatch);
writer.Write(VdpBuffer);
writer.Write(VdpAddress);
writer.Write((byte)vdpCommand);
writer.Write(Registers);
writer.Write(CRAM);
writer.Write(VRAM);
}
public void LoadStateBinary(BinaryReader reader)
{
StatusByte = reader.ReadByte();
VdpWaitingForLatchByte = reader.ReadBoolean();
VdpLatch = reader.ReadByte();
VdpBuffer = reader.ReadByte();
VdpAddress = reader.ReadUInt16();
vdpCommand = (VdpCommand)Enum.ToObject(typeof(VdpCommand), reader.ReadByte());
Registers = reader.ReadBytes(Registers.Length);
CRAM = reader.ReadBytes(CRAM.Length);
VRAM = reader.ReadBytes(VRAM.Length);
UpdatePrecomputedPalette();
for (ushort i = 0; i < VRAM.Length; i++)
UpdatePatternBuffer(i, VRAM[i]);
for (int i = 0; i < Registers.Length; i++)
WriteRegister(i, Registers[i]);
}
public int[] GetVideoBuffer()
{
return mode == VdpMode.SMS ? FrameBuffer : GameGearFrameBuffer;
}
public int VirtualWidth { get { return BufferWidth; } }
public int BufferWidth
{
get { return mode == VdpMode.SMS ? 256 : 160; }
}
public int BufferHeight
{
get { return mode == VdpMode.SMS ? FrameHeight : 144; }
}
public int BackgroundColor
{
get { return Palette[BackdropColor]; }
}
enum VdpCommand
{
VramRead,
VramWrite,
RegisterWrite,
CramWrite
}
}
}

View File

@ -1486,7 +1486,7 @@ namespace BizHawk.MultiClient
if (path == null) return false;
using (var file = new HawkFile())
{
string[] romExtensions = new string[] { "SMS", "SMC", "SFC", "PCE", "SGX", "GG", "SG", "BIN", "GEN", "MD", "SMD", "GB", "NES", "FDS", "ROM", "INT", "GBC", "UNF", "A78", "CRT" };
string[] romExtensions = new string[] { "SMS", "SMC", "SFC", "PCE", "SGX", "GG", "SG", "BIN", "GEN", "MD", "SMD", "GB", "NES", "FDS", "ROM", "INT", "GBC", "UNF", "A78", "CRT", "COL" };
//lets not use this unless we need to
//file.NonArchiveExtensions = romExtensions;
@ -1758,7 +1758,7 @@ namespace BizHawk.MultiClient
}
break;
case "COLV":
SMS c = new SMS(game, rom.RomData);//new ColecoVision(game, rom.FileData);
ColecoVision c = new ColecoVision(game, rom.RomData);
nextEmulator = c;
break;
case "INTV":