BizHawk/BizHawk.Emulation/Consoles/Sega/Genesis/GenVDP.cs

330 lines
13 KiB
C#
Raw Normal View History

2011-01-11 02:55:51 +00:00
using System;
namespace BizHawk.Emulation.Consoles.Sega
{
public sealed partial class GenVDP : IVideoProvider
{
// Memory
public byte[] VRAM = new byte[0x10000];
public ushort[] CRAM = new ushort[64];
public ushort[] VSRAM = new ushort[40];
public byte[] Registers = new byte[0x20];
public byte[] PatternBuffer = new byte[0x20000];
public int[] Palette = new int[64];
public int[] FrameBuffer = new int[256*224];
public int FrameWidth = 256;
public int ScanLine;
public bool HInterruptsEnabled { get { return (Registers[0] & 0x10) != 0; } }
public bool DisplayEnabled { get { return (Registers[1] & 0x40) != 0; } }
public bool VInterruptEnabled { get { return (Registers[1] & 0x20) != 0; } }
public bool DmaEnabled { get { return (Registers[1] & 0x10) != 0; } }
public bool CellBasedVertScroll { get { return (Registers[11] & 0x08) != 0; } }
public bool Display40Mode { get { return (Registers[12] & 0x81) != 0; } }
ushort NameTableAddrA;
ushort NameTableAddrB;
ushort NameTableAddrWindow;
ushort SpriteAttributeTableAddr;
ushort HScrollTableAddr;
byte NameTableWidth;
byte NameTableHeight;
2011-01-11 02:55:51 +00:00
bool ControlWordPending;
ushort VdpDataAddr;
byte VdpDataCode;
2011-01-11 02:55:51 +00:00
static readonly byte[] PalXlatTable = { 0, 0, 36, 36, 73, 73, 109, 109, 145, 145, 182, 182, 219, 219, 255, 255 };
2011-01-11 02:55:51 +00:00
public void WriteVdpControl(ushort data)
{
Log.Note("VDP", "VDP: Control Write {0:X4}", data);
2011-01-11 02:55:51 +00:00
if (ControlWordPending == false)
{
if ((data & 0xC000) == 0x8000)
{
int reg = (data >> 8) & 0x1F;
byte value = (byte) (data & 0xFF);
WriteVdpRegister(reg, value);
VdpDataCode = 0;
2011-01-11 02:55:51 +00:00
} else {
ControlWordPending = true;
VdpDataAddr &= 0xC000;
VdpDataAddr |= (ushort) (data & 0x3FFF);
VdpDataCode &= 0x3C;
VdpDataCode |= (byte) (data >> 14);
//Console.WriteLine("Address = {0:X4}", VdpDataAddr);
//Console.WriteLine("Code = {0:X2}", VdpDataCode);
2011-01-11 02:55:51 +00:00
}
} else {
ControlWordPending = false;
// Update data address and code
VdpDataAddr &= 0x3FFF;
VdpDataAddr |= (ushort) ((data & 0x03) << 14);
//Console.WriteLine("Address = {0:X4}", VdpDataAddr);
2011-01-11 02:55:51 +00:00
VdpDataCode &= 0x03;
VdpDataCode |= (byte) ((data >> 2) & 0x3C);
//Console.WriteLine("Code = {0:X2}", VdpDataCode);
2011-01-11 02:55:51 +00:00
if ((VdpDataCode & 0x20) != 0 && DmaEnabled) // DMA triggered
{
//Console.WriteLine("DMA TIME!");
2011-01-11 02:55:51 +00:00
// what type of DMA?
switch (Registers[23] >> 6)
{
case 2:
Log.Note("VDP", "VRAM FILL");
2011-01-11 02:55:51 +00:00
DmaFillModePending = true;
break;
case 3:
Log.Note("VDP", "VRAM COPY **** UNIMPLEMENTED ***");
2011-01-11 02:55:51 +00:00
break;
default:
Log.Note("VDP", "68k->VRAM COPY");
2011-01-11 02:55:51 +00:00
Execute68000VramCopy();
break;
}
Log.Note("VDP", "DMA LEN = " + DmaLength);
2011-01-11 02:55:51 +00:00
}
}
}
public ushort ReadVdpControl()
{
ushort value = 0x3400; // fixed bits per genvdp.txt TODO test on everdrive, I guess.
value |= 0x0200; // Fifo empty
Log.Note("VDP", "VDP: Control Read {0:X4}", value);
2011-01-11 02:55:51 +00:00
return value;
}
public void WriteVdpData(ushort data)
{
ControlWordPending = false;
// byte-swap incoming data when A0 is set
if ((VdpDataAddr & 1) != 0)
data = (ushort) ((data >> 8) | (data << 8));
if (DmaFillModePending)
{
ExecuteDmaFill(data);
}
switch (VdpDataCode & 7)
{
case 1: // VRAM Write
VRAM[VdpDataAddr & 0xFFFE] = (byte) data;
VRAM[(VdpDataAddr & 0xFFFE) + 1] = (byte) (data >> 8);
UpdatePatternBuffer(VdpDataAddr & 0xFFFE);
UpdatePatternBuffer((VdpDataAddr & 0xFFFE) + 1);
//Console.WriteLine("Wrote VRAM[{0:X4}] = {1:X4}", VdpDataAddr, data);
2011-01-11 02:55:51 +00:00
VdpDataAddr += Registers[0x0F];
break;
case 3: // CRAM write
CRAM[(VdpDataAddr / 2) % 64] = data;
//Console.WriteLine("Wrote CRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 64, data);
2011-01-11 02:55:51 +00:00
ProcessPalette((VdpDataAddr/2)%64);
VdpDataAddr += Registers[0x0F];
break;
case 5: // VSRAM write
VSRAM[(VdpDataAddr / 2) % 40] = data;
//Console.WriteLine("Wrote VSRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 40, data);
2011-01-11 02:55:51 +00:00
VdpDataAddr += Registers[0x0F];
break;
default:
Console.WriteLine("VDP DATA WRITE WITH UNHANDLED CODE!!!");
break;
}
}
public ushort ReadVdpData()
{
Console.WriteLine("VDP: Data Read");
2011-01-11 02:55:51 +00:00
return 0;
}
public void WriteVdpRegister(int register, byte data)
{
Log.Note("VDP", "Register {0}: {1:X2}", register, data);
2011-01-11 02:55:51 +00:00
switch (register)
{
case 0x00: // Mode Set Register 1
2011-01-11 02:55:51 +00:00
Registers[register] = data;
//Log.Note("VDP", "HINT enabled: " + HInterruptsEnabled);
2011-01-11 02:55:51 +00:00
break;
case 0x01: // Mode Set Register 2
2011-01-11 02:55:51 +00:00
Registers[register] = data;
//Log.Note("VDP", "DisplayEnabled: " + DisplayEnabled);
//Log.Note("VDP", "DmaEnabled: " + DmaEnabled);
//Log.Note("VDP", "VINT enabled: " + VInterruptEnabled);
2011-01-11 02:55:51 +00:00
break;
2011-01-11 02:55:51 +00:00
case 0x02: // Name Table Address for Layer A
NameTableAddrA = (ushort) ((data & 0x38) << 10);
//Log.Note("VDP", "SET NTa A = {0:X4}", NameTableAddrA);
2011-01-11 02:55:51 +00:00
break;
2011-01-11 02:55:51 +00:00
case 0x03: // Name Table Address for Window
NameTableAddrWindow = (ushort) ((data & 0x3E) << 10);
//Log.Note("VDP", "SET NTa W = {0:X4}", NameTableAddrWindow);
2011-01-11 02:55:51 +00:00
break;
2011-01-11 02:55:51 +00:00
case 0x04: // Name Table Address for Layer B
NameTableAddrB = (ushort) (data << 13);
//Log.Note("VDP", "SET NTa B = {0:X4}", NameTableAddrB);
2011-01-11 02:55:51 +00:00
break;
2011-01-11 02:55:51 +00:00
case 0x05: // Sprite Attribute Table Address
SpriteAttributeTableAddr = (ushort) (data << 9);
//Log.Note("VDP", "SET SAT attr = {0:X4}", SpriteAttributeTableAddr);
2011-01-11 02:55:51 +00:00
break;
case 0x0A: // H Interrupt Register
//Log.Note("VDP", "HInt occurs every {0} lines.", data);
break;
case 0x0B: // VScroll/HScroll modes
/*if ((data & 4) != 0)
Log.Note("VDP", "VSCroll Every 2 Cells Enabled");
else
Log.Note("VDP", "Full Screen VScroll");*/
int hscrollmode = data & 3;
switch (hscrollmode)
{
//case 0: Log.Note("VDP", "Full Screen HScroll"); break;
//case 1: Log.Note("VDP", "Prohibited HSCROLL mode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); break;
//case 2: Log.Note("VDP", "HScroll every 1 cell"); break;
//case 3: Log.Note("VDP", "HScroll every 2 cell"); break;
}
break;
2011-01-11 02:55:51 +00:00
case 0x0C: // Mode Set #4
// TODO interlaced modes
if ((data & 0x81) == 0)
{
// Display is 32 cells wide
if (FrameWidth != 256)
{
FrameBuffer = new int[256*224];
FrameWidth = 256;
//Log.Note("VDP", "SWITCH TO 32 CELL WIDE MODE");
2011-01-11 02:55:51 +00:00
}
} else {
// Display is 40 cells wide
if (FrameWidth != 320)
{
FrameBuffer = new int[320*224];
FrameWidth = 320;
//Log.Note("VDP", "SWITCH TO 40 CELL WIDE MODE");
2011-01-11 02:55:51 +00:00
}
}
break;
2011-01-11 02:55:51 +00:00
case 0x0D: // H Scroll Table Address
HScrollTableAddr = (ushort) (data << 10);
//Log.Note("VDP", "SET HScrollTab attr = {0:X4}", HScrollTableAddr);
2011-01-11 02:55:51 +00:00
break;
case 0x0F: // Auto Address Register Increment
//Log.Note("VDP", "Set Data Increment to " + data);
2011-01-11 02:55:51 +00:00
break;
case 0x10: // Nametable Dimensions
2011-01-11 02:55:51 +00:00
switch (data & 0x03)
{
case 0: NameTableWidth = 32; break;
case 1: NameTableWidth = 64; break;
case 2: NameTableWidth = 32; break; // invalid setting
case 3: NameTableWidth = 128; break;
}
switch ((data>>4) & 0x03)
{
case 0: NameTableHeight = 32; break;
case 1: NameTableHeight = 64; break;
case 2: NameTableHeight = 32; break; // invalid setting
case 3: NameTableHeight = 128; break;
}
//Log.Note("VDP", "Name Table Dimensions set to {0}x{1}", NameTableWidth, NameTableHeight);
break;
case 0x11: // Window H Position
int whp = data & 31;
bool fromright = (data & 0x80) != 0;
//Log.Note("VDP", "Window H is {0} units from {1}", whp, fromright ? "right" : "left");
break;
case 0x12: // Window V
whp = data & 31;
fromright = (data & 0x80) != 0;
//Log.Note("VDP", "Window V is {0} units from {1}", whp, fromright ? "lower" : "upper");
break;
case 0x13: // DMA Length Low
Registers[register] = data;
//Log.Note("VDP", "DMA Length = {0:X4}", DmaLength);
break;
case 0x14: // DMA Length High
Registers[register] = data;
//Log.Note("VDP", "DMA Length = {0:X4}", DmaLength);
2011-01-11 02:55:51 +00:00
break;
case 0x15: // DMA Source Low
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
case 0x16: // DMA Source Mid
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
case 0x17: // DMA Source High
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
2011-01-11 02:55:51 +00:00
}
Registers[register] = data;
}
void ProcessPalette(int slot)
2011-01-11 02:55:51 +00:00
{
byte r = PalXlatTable[(CRAM[slot] & 0x000F) >> 0];
byte g = PalXlatTable[(CRAM[slot] & 0x00F0) >> 4];
byte b = PalXlatTable[(CRAM[slot] & 0x0F00) >> 8];
Palette[slot] = Colors.ARGB(r, g, b);
}
void UpdatePatternBuffer(int addr)
2011-01-11 02:55:51 +00:00
{
PatternBuffer[(addr*2) + 1] = (byte) (VRAM[addr^1] & 0x0F);
PatternBuffer[(addr*2) + 0] = (byte) (VRAM[addr^1] >> 4);
}
public int[] GetVideoBuffer()
{
return FrameBuffer;
}
public int BufferWidth
{
get { return FrameWidth; }
}
public int BufferHeight
{
get { return 224; }
}
public int BackgroundColor
{
get { return Palette[Registers[7] & 0x3F]; }
}
}
}