gen: implement H-ints

gen: implement Vram/Vram DMA copy (badly)
gen: fix dumb sprite rendering bug
gen: fix crash bug with certain WINDOW settings
This commit is contained in:
beirich 2012-09-01 18:40:52 +00:00
parent 35ec42073f
commit d586876f40
4 changed files with 138 additions and 60 deletions

View File

@ -24,9 +24,9 @@ namespace BizHawk.Emulation.Consoles.Sega
bool DmaFillModePending;
void ExecuteDmaFill(ushort data)
void ExecuteVramFill(ushort data)
{
//Log.Error("VDP","DMA FILL REQD, WRITE {0:X4}, {1:X4} times, at {2:X4}", data, DmaLength, VdpDataAddr);
Log.Note("VDP", "DMA VRAM FILL REQD, WRITE {0:X4}, {1:X4} times, at {2:X4}", data, DmaLength, VdpDataAddr);
// TODO: It should spread this out, not do it all at once.
// TODO: DMA can go to places besides just VRAM (eg CRAM, VSRAM) ??? can it?
@ -50,7 +50,7 @@ namespace BizHawk.Emulation.Consoles.Sega
void Execute68000VramCopy()
{
//Log.Error("VDP", "DMA 68000 -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);
Log.Note("VDP", "DMA 68000 -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);
int length = DmaLength;
if (length == 0)
@ -68,5 +68,26 @@ namespace BizHawk.Emulation.Consoles.Sega
// TODO: find correct number of 68k cycles to burn
}
void ExecuteVramVramCopy()
{
Log.Note("VDP", "DMA VRAM -> VRAM COPY REQ'D. LENGTH {0:X4}, SOURCE {1:X4}", DmaLength, DmaSource);
int length = DmaLength;
if (length == 0)
length = 0x10000;
int source = DmaSource;
do
{
ushort value = (ushort)ReadVdpData();
source += 2;
// TODO funky source behavior
WriteVdpData(value);
} while (--length > 0);
// TODO: find correct number of 68k cycles to burn
}
}
}

View File

@ -18,17 +18,22 @@ namespace BizHawk.Emulation.Consoles.Sega
public void RenderLine()
{
Array.Clear(PriorityBuffer, 0, 320);
if (DisplayEnabled)
{
Array.Clear(PriorityBuffer, 0, 320);
RenderScrollA();
RenderScrollB();
RenderSpritesScanline();
}
else
{
// If display is disabled, fill in with background color.
for (int i = 0; i < FrameWidth; i++)
FrameBuffer[(ScanLine * FrameWidth) + i] = BackgroundColor;
}
//if (ScanLine == 223) // shrug
//RenderPalette();
// RenderPalette();
}
void RenderPalette()
@ -106,7 +111,7 @@ namespace BizHawk.Emulation.Consoles.Sega
int data = Registers[0x11];
int windowHPosition = (data & 31) * 2; // Window H position is set in 2-cell increments
bool fromLeft = (data & 0x80) == 0;
if (windowHPosition == 0)
{
startPixel = -1;
@ -118,11 +123,18 @@ namespace BizHawk.Emulation.Consoles.Sega
{
startPixel = 0;
endPixel = (windowHPosition * 8);
if (endPixel > FrameWidth)
endPixel = FrameWidth;
}
else
{
startPixel = windowHPosition * 8;
endPixel = FrameWidth;
if (startPixel > FrameWidth)
{
startPixel = -1;
endPixel = -1;
}
}
}
@ -263,6 +275,7 @@ namespace BizHawk.Emulation.Consoles.Sega
continue;
if (sprite.Priority == false && PriorityBuffer[sprite.X + xi] >= 3) continue;
if (PriorityBuffer[sprite.X + xi] == 9) continue;
int pixel = PatternBuffer[((pattern + ((-xi / 8) * sprite.HeightCells)) * 64) + ((yline & 7) * 8) + (7 - (xi & 7))];
if (pixel != 0)

View File

@ -57,6 +57,8 @@ namespace BizHawk.Emulation.Consoles.Sega
public const int StatusSpriteOverflow = 0x40;
public const int StatusVerticalInterruptPending = 0x80;
public bool VdpDebug = false;
public GenVDP()
{
WriteVdpRegister(00, 0x04);
@ -140,14 +142,13 @@ namespace BizHawk.Emulation.Consoles.Sega
switch (Registers[23] >> 6)
{
case 2:
Log.Note("VDP", "VRAM FILL");
DmaFillModePending = true;
break;
case 3:
Log.Error("VDP", "VRAM COPY **** UNIMPLEMENTED ***");
Log.Error("VDP", "VRAM COPY **** UNIMPLEMENTED ***");
ExecuteVramVramCopy();
break;
default:
Log.Note("VDP", "68k->VRAM COPY");
Execute68000VramCopy();
break;
}
@ -179,7 +180,7 @@ namespace BizHawk.Emulation.Consoles.Sega
if (DmaFillModePending)
{
ExecuteDmaFill(data);
ExecuteVramFill(data);
}
switch (VdpDataCode & 7)
@ -187,21 +188,23 @@ namespace BizHawk.Emulation.Consoles.Sega
case CommandVramWrite: // VRAM Write
VRAM[VdpDataAddr & 0xFFFE] = (byte) data;
VRAM[(VdpDataAddr & 0xFFFE) + 1] = (byte) (data >> 8);
//Log.Error("VDP", "VRAM[{0:X4}] = {1:X4}", VdpDataAddr, data);
if (VdpDebug)
Log.Note("VDP", "VRAM[{0:X4}] = {1:X4}", VdpDataAddr, data);
UpdatePatternBuffer(VdpDataAddr & 0xFFFE);
UpdatePatternBuffer((VdpDataAddr & 0xFFFE) + 1);
//Console.WriteLine("Wrote VRAM[{0:X4}] = {1:X4}", VdpDataAddr, data);
VdpDataAddr += Registers[0x0F];
break;
case CommandCramWrite: // CRAM write
CRAM[(VdpDataAddr / 2) % 64] = data;
//Console.WriteLine("Wrote CRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 64, data);
if (VdpDebug)
Log.Note("VDP", "Wrote CRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 64, data);
ProcessPalette((VdpDataAddr/2)%64);
VdpDataAddr += Registers[0x0F];
break;
case CommandVsramWrite: // VSRAM write
VSRAM[(VdpDataAddr / 2) % 40] = data;
//Console.WriteLine("Wrote VSRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 40, data);
if (VdpDebug)
Log.Note("VDP", "Wrote VSRAM[{0:X2}] = {1:X4}", (VdpDataAddr / 2) % 40, data);
VdpDataAddr += Registers[0x0F];
break;
default:
@ -243,7 +246,8 @@ namespace BizHawk.Emulation.Consoles.Sega
// TODO dont tie this to musashi cycle count.
// Figure out a "clean" way to get cycle counter information available to VDP.
// Oh screw that. The VDP and the cpu cycle counters are going to be intertwined pretty tightly.
int hcounter = (487 - Native68000.Musashi.GetCyclesRemaining()) * 255 / 487;
int hcounter = (488 - Native68000.Musashi.GetCyclesRemaining()) * 255 / 488;
// FIXME: totally utterly wrong.
ushort res = (ushort) ((vcounter << 8) | (hcounter & 0xFF));
//Console.WriteLine("READ HVC: V={0:X2} H={1:X2} ret={2:X4}", vcounter, hcounter, res);
@ -253,59 +257,72 @@ namespace BizHawk.Emulation.Consoles.Sega
public void WriteVdpRegister(int register, byte data)
{
if (VdpDebug)
Log.Note("VDP", "Register {0}: {1:X2}", register, data);
switch (register)
{
case 0x00: // Mode Set Register 1
Registers[register] = data;
Log.Error("VDP", "HINT enabled: " + HInterruptsEnabled);
//if (VdpDebug)
Log.Note("VDP", "HINT enabled: " + HInterruptsEnabled);
break;
case 0x01: // Mode Set Register 2
Registers[register] = data;
//Log.Note("VDP", "DisplayEnabled: " + DisplayEnabled);
//Log.Note("VDP", "DmaEnabled: " + DmaEnabled);
//Log.Note("VDP", "VINT enabled: " + VInterruptEnabled);
if (VdpDebug)
{
Registers[register] = data;
Log.Note("VDP", "DisplayEnabled: " + DisplayEnabled);
Log.Note("VDP", "DmaEnabled: " + DmaEnabled);
Log.Note("VDP", "VINT enabled: " + VInterruptEnabled);
}
break;
case 0x02: // Name Table Address for Layer A
NameTableAddrA = (ushort) ((data & 0x38) << 10);
//Log.Error("VDP", "SET NTa A = {0:X4}", NameTableAddrA);
if (VdpDebug)
Log.Note("VDP", "SET NTa A = {0:X4}", NameTableAddrA);
break;
case 0x03: // Name Table Address for Window
NameTableAddrWindow = (ushort) ((data & 0x3E) << 10);
//Log.Error("VDP", "SET NTa W = {0:X4}", NameTableAddrWindow);
if (VdpDebug)
Log.Note("VDP", "SET NTa W = {0:X4}", NameTableAddrWindow);
break;
case 0x04: // Name Table Address for Layer B
NameTableAddrB = (ushort) (data << 13);
//Log.Error("VDP", "SET NTa B = {0:X4}", NameTableAddrB);
if (VdpDebug)
Log.Note("VDP", "SET NTa B = {0:X4}", NameTableAddrB);
break;
case 0x05: // Sprite Attribute Table Address
SpriteAttributeTableAddr = (ushort) (data << 9);
//Log.Error("VDP", "SET SAT attr = {0:X4}", SpriteAttributeTableAddr);
if (VdpDebug)
Log.Note("VDP", "SET SAT attr = {0:X4}", SpriteAttributeTableAddr);
break;
case 0x0A: // H Interrupt Register
//Log.Note("VDP", "HInt occurs every {0} lines.", data);
if (VdpDebug)
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");*/
if (VdpDebug)
{
if ((data & 4) != 0)
Log.Note("VDP", "VSCroll Every 2 Cells Enabled");
else
Log.Note("VDP", "Full Screen VScroll");
int hscrollmode = data & 3;
//switch (hscrollmode)
//{
// case 0: Log.Note("VDP", "Full Screen HScroll"); break;
// case 1: Log.Note("VDP", "Prohibited HSCROLL mode!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); break;
// case 2: Log.Note("VDP", "HScroll every 1 cell"); break;
// case 3: Log.Note("VDP", "HScroll every 2 cell"); break;
//}
int hscrollmode = data & 3;
switch (hscrollmode)
{
case 0: Log.Note("VDP", "Full Screen HScroll"); break;
case 1: Log.Note("VDP", "Prohibited HSCROLL mode!!! But it'll work."); break;
case 2: Log.Note("VDP", "HScroll every 1 cell"); break;
case 3: Log.Note("VDP", "HScroll every line"); break;
}
}
break;
case 0x0C: // Mode Set #4
@ -317,7 +334,7 @@ namespace BizHawk.Emulation.Consoles.Sega
{
FrameBuffer = new int[256*224];
FrameWidth = 256;
//Log.Note("VDP", "SWITCH TO 32 CELL WIDE MODE");
Log.Note("VDP", "SWITCH TO 32 CELL WIDE MODE");
}
} else {
// Display is 40 cells wide
@ -325,18 +342,20 @@ namespace BizHawk.Emulation.Consoles.Sega
{
FrameBuffer = new int[320*224];
FrameWidth = 320;
//Log.Note("VDP", "SWITCH TO 40 CELL WIDE MODE");
Log.Note("VDP", "SWITCH TO 40 CELL WIDE MODE");
}
}
break;
case 0x0D: // H Scroll Table Address
HScrollTableAddr = (ushort) (data << 10);
//Log.Note("VDP", "SET HScrollTab attr = {0:X4}", HScrollTableAddr);
if (VdpDebug)
Log.Note("VDP", "SET HScrollTab attr = {0:X4}", HScrollTableAddr);
break;
case 0x0F: // Auto Address Register Increment
//Log.Note("VDP", "Set Data Increment to " + data);
//if (VdpDebug)
Log.Note("VDP", "Set Data Increment to " + data);
break;
case 0x10: // Nametable Dimensions
@ -359,36 +378,38 @@ namespace BizHawk.Emulation.Consoles.Sega
case 0x11: // Window H Position
int whp = data & 31;
bool fromright = (data & 0x80) != 0;
//Log.Error("VDP", "Window H is {0} units from {1}", whp, fromright ? "right" : "left");
if (VdpDebug)
Log.Note("VDP", "Window H is {0} units from {1}", whp, fromright ? "right" : "left");
break;
case 0x12: // Window V
//whp = data & 31;
//fromright = (data & 0x80) != 0;
//Log.Error("VDP", "Window V is {0} units from {1}", whp, fromright ? "lower" : "upper");
whp = data & 31;
fromright = (data & 0x80) != 0;
if (VdpDebug)
Log.Note("VDP", "Window V is {0} units from {1}", whp, fromright ? "lower" : "upper");
break;
case 0x13: // DMA Length Low
Registers[register] = data;
//Log.Note("VDP", "DMA Length = {0:X4}", DmaLength);
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);
Log.Note("VDP", "DMA Length = {0:X4}", DmaLength);
break;
case 0x15: // DMA Source Low
Registers[register] = data;
//Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
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);
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);
Log.Note("VDP", "DMA Source = {0:X6}", DmaSource);
break;
}

View File

@ -147,18 +147,31 @@ namespace BizHawk.Emulation.Consoles.Sega
{
//Log.Error("VDP","FRAME {0}, SCANLINE {1}", Frame, VDP.ScanLine);
if (VDP.ScanLine < 224)
if (VDP.ScanLine < VDP.FrameHeight)
VDP.RenderLine();
Exec68k(487);
if (Z80Runnable)
Exec68k(365);
RunZ80(171);
// H-Int now?
VDP.HIntLineCounter--;
if (VDP.HIntLineCounter < 0)
{
SoundCPU.ExecuteCycles(228);
SoundCPU.Interrupt = false;
} else {
SoundCPU.TotalExecutedCycles += 228; // I emulate the YM2612 synced to Z80 clock, for better or worse. Keep the timer going even if Z80 isn't running.
VDP.HIntLineCounter = VDP.Registers[10];
VDP.VdpStatusWord |= GenVDP.StatusHorizBlanking;
if (VDP.HInterruptsEnabled)
{
Set68kIrq(4);
//Console.WriteLine("Fire hint!");
}
}
Exec68k(488 - 365);
RunZ80(228 - 171);
if (VDP.ScanLine == 224)
{
VDP.VdpStatusWord |= GenVDP.StatusVerticalInterruptPending;
@ -168,8 +181,7 @@ namespace BizHawk.Emulation.Consoles.Sega
if (VDP.VInterruptEnabled)
Set68kIrq(6);
if (Z80Runnable)
SoundCPU.Interrupt = true;
SoundCPU.Interrupt = true;
//The INT output is asserted every frame for exactly one scanline, and it can't be disabled. A very short Z80 interrupt routine would be triggered multiple times if it finishes within 228 Z80 clock cycles. I think (but cannot recall the specifics) that some games have delay loops in the interrupt handler for this very reason.
}
}
@ -196,6 +208,17 @@ namespace BizHawk.Emulation.Consoles.Sega
#endif
}
void RunZ80(int cycles)
{
// I emulate the YM2612 synced to Z80 clock, for better or worse.
// So we still need to keep the Z80 cycle count accurate even if the Z80 isn't running.
if (Z80Runnable)
SoundCPU.ExecuteCycles(cycles);
else
SoundCPU.TotalExecutedCycles += cycles;
}
void Set68kIrq(int irq)
{
#if MUSASHI