initial ColecoVision commit. Needs BIOS ROM & input wired up to client (plus more emulation work)
This commit is contained in:
parent
11feb0d33a
commit
4c83970b2b
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; } }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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":
|
||||
|
|
Loading…
Reference in New Issue