gen: fix dumb tile priority bug (primarily affects Ghouls n Ghosts, which use the layers backwards from normal)

This commit is contained in:
beirich 2012-08-30 04:29:33 +00:00
parent ff84855f2a
commit e24f0962fc
7 changed files with 79 additions and 19 deletions

View File

@ -48,6 +48,9 @@ namespace Native68000
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int QueryCpuState(int regcode);
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetCyclesRemaining();
public static int D0 { get { return QueryCpuState(0); } }
public static int D1 { get { return QueryCpuState(1); } }
public static int D2 { get { return QueryCpuState(2); } }

View File

@ -6,3 +6,26 @@ Timings:
- How many cycles does TRAP take to execute?
- How many cycles does it take to accept an interrupt?
- AND has some funky timings when it comes to immediates?
GAMES:
Monster World 4 - Music is messed up now. Timing is all off. It used to work.
Quackshot doesn't boot.
Moonwalker doesn't boot.
Altered Beast: start with 0 health, 0 lives???
Contra Hard Corps: Scrolling is messed up in level 1... used to work.
After Burner 2: No music
MUSHA: Intro music starts too soon
MUSHA: uses unimplemented VRAM copy
MUSHA: Some sprites have messed up left/right symmetry
Landstalker: freezes during new game sequence, very early
Things that read from VRAM work like 50%-90%, but not 100%. It's frustrating. Kid Chameleon and Eternal Champions are examples.
Some games flicker in the rightmost columns. Is this caused by mid-frame mode shifting(32/40 col modes?) Alisia Dragoon is one example.
TODO: non-instant DMA emulation
TODO: Add 68000/VDP interrupt enable delay (one instruction, similar to After Burner/PCE)
TODO: freaking H-interrupts
TODO: Test DMA/ VDP command words.... I'm not at all convinced that VRAM is always correct

View File

@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Consoles.Sega
int palette = (nameTableEntry >> 13) & 3;
if (priority && PriorityBuffer[x] >= highPriority) continue;
if (PriorityBuffer[x] >= lowPriority) continue;
if (!priority && PriorityBuffer[x] >= lowPriority) continue;
if (vFlip) yOfs = 7 - yOfs;
if (hFlip) xOfs = 7 - xOfs;

View File

@ -18,6 +18,7 @@ namespace BizHawk.Emulation.Consoles.Sega
public int FrameWidth = 256;
public int ScanLine;
public int HIntLineCounter;
public bool HInterruptsEnabled { get { return (Registers[0] & 0x10) != 0; } }
public bool DisplayEnabled { get { return (Registers[1] & 0x40) != 0; } }
@ -26,6 +27,8 @@ namespace BizHawk.Emulation.Consoles.Sega
public bool CellBasedVertScroll { get { return (Registers[11] & 0x08) != 0; } }
public bool Display40Mode { get { return (Registers[12] & 0x81) != 0; } }
public bool InDisplayPeriod { get { return ScanLine < 224 && DisplayEnabled; } }
ushort NameTableAddrA;
ushort NameTableAddrB;
ushort NameTableAddrWindow;
@ -46,12 +49,12 @@ namespace BizHawk.Emulation.Consoles.Sega
const int CommandCramRead = 7;
public ushort VdpStatusWord = 0x3400;
public const int StatusVerticalInterruptPending = 0x80;
public const int StatusSpriteOverflow = 0x40;
public const int StatusSpriteCollision = 0x20;
public const int StatusOddFrame = 0x10;
public const int StatusVerticalBlanking = 0x08;
public const int StatusHorizBlanking = 0x04;
public const int StatusVerticalBlanking = 0x08;
public const int StatusOddFrame = 0x10;
public const int StatusSpriteCollision = 0x20;
public const int StatusSpriteOverflow = 0x40;
public const int StatusVerticalInterruptPending = 0x80;
public ushort ReadVdp(int addr)
{
@ -64,8 +67,7 @@ namespace BizHawk.Emulation.Consoles.Sega
case 6:
return ReadVdpControl();
default:
//throw new Exception("HV Counter read....");
return 0;
return ReadHVCounter();
}
}
@ -142,7 +144,11 @@ namespace BizHawk.Emulation.Consoles.Sega
public ushort ReadVdpControl()
{
VdpStatusWord |= 0x0200; // Fifo empty
VdpStatusWord |= 0x0200; // Fifo empty // TODO kill this, emulating the damn FIFO.
// sprite overflow flag should clear.
// sprite collision flag should clear.
return VdpStatusWord;
}
@ -151,9 +157,11 @@ namespace BizHawk.Emulation.Consoles.Sega
ControlWordPending = false;
// byte-swap incoming data when A0 is set
// TODO what is the reason for thinking that this happens?
//if ((VdpDataAddr & 1) != 0)
// data = (ushort) ((data >> 8) | (data << 8));
if ((VdpDataAddr & 1) != 0)
{
data = (ushort)((data >> 8) | (data << 8));
Console.WriteLine("VRAM byte-swap is happening because A0 is not 0");
}
if (DmaFillModePending)
{
@ -182,7 +190,7 @@ namespace BizHawk.Emulation.Consoles.Sega
VdpDataAddr += Registers[0x0F];
break;
default:
//Console.WriteLine("VDP DATA WRITE WITH UNHANDLED CODE!!!");
Console.WriteLine("VDP DATA WRITE WITH UNHANDLED CODE!!!");
break;
}
}
@ -203,11 +211,30 @@ namespace BizHawk.Emulation.Consoles.Sega
throw new Exception("VSRAM read");
case CommandCramRead:
throw new Exception("CRAM read");
default:
throw new Exception("VRAM read with unexpected code!!!");
}
return retval;
}
ushort ReadHVCounter()
{
int vcounter = ScanLine;
if (vcounter > 0xEA)
vcounter -= 7;
// TODO generalize this across multiple video modes and stuff.
// TODO dont tie this to musashi cycle count.
// Figure out a "clean" way to get cycle counter information available to VDP.
int hcounter = (487 - Native68000.Musashi.GetCyclesRemaining()) * 255 / 487;
ushort res = (ushort) ((vcounter << 8) | (hcounter & 0xFF));
Console.WriteLine("READ HVC: V={0:X2} H={1:X2} ret={2:X4}", vcounter, hcounter, res);
return res;
}
public void WriteVdpRegister(int register, byte data)
{
Log.Note("VDP", "Register {0}: {1:X2}", register, data);
@ -317,13 +344,13 @@ 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");
//Log.Error("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");
//Log.Error("VDP", "Window V is {0} units from {1}", whp, fromright ? "lower" : "upper");
break;
case 0x13: // DMA Length Low

View File

@ -137,6 +137,12 @@ namespace BizHawk.Emulation.Consoles.Sega
Controller.UpdateControls(Frame++);
PSG.BeginFrame(SoundCPU.TotalExecutedCycles);
YM2612.BeginFrame(SoundCPU.TotalExecutedCycles);
// Do start-of-frame events
VDP.HIntLineCounter = VDP.Registers[10];
//VDP.VdpStatusWord &=
unchecked { VDP.VdpStatusWord &= (ushort)~GenVDP.StatusVerticalBlanking; }
for (VDP.ScanLine = 0; VDP.ScanLine < 262; VDP.ScanLine++)
{
//Log.Error("VDP","FRAME {0}, SCANLINE {1}", Frame, VDP.ScanLine);
@ -164,12 +170,13 @@ namespace BizHawk.Emulation.Consoles.Sega
if (Z80Runnable)
SoundCPU.Interrupt = true;
//The INT output is asserted every frame for exactly one scanline, and it can't be disabled. A very short Z80 interrupt routine would be triggered multiple times if it finishes within 228 Z80 clock cycles. I think (but cannot recall the specifics) that some games have delay loops in the interrupt handler for this very reason.
}
}
PSG.EndFrame(SoundCPU.TotalExecutedCycles);
YM2612.EndFrame(SoundCPU.TotalExecutedCycles);
unchecked { VDP.VdpStatusWord &= (ushort)~GenVDP.StatusVerticalBlanking; }
if (lagged)
{

View File

@ -28,7 +28,7 @@ namespace BizHawk.Emulation.Consoles.Sega
if (address >= 0xC00000 && address < 0xC00010)
{
Console.WriteLine("byte-reading the VDP. someone is probably stupid.");
//Console.WriteLine("byte-reading the VDP. someone is probably stupid.");
ushort value = VDP.ReadVdp(address & 0x0E);
if ((address & 1) == 0) // read MSB
return (sbyte) (value >> 8);