gen: fix dumb tile priority bug (primarily affects Ghouls n Ghosts, which use the layers backwards from normal)
This commit is contained in:
parent
ff84855f2a
commit
e24f0962fc
|
@ -48,6 +48,9 @@ namespace Native68000
|
||||||
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("MusashiDLL.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern int QueryCpuState(int regcode);
|
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 D0 { get { return QueryCpuState(0); } }
|
||||||
public static int D1 { get { return QueryCpuState(1); } }
|
public static int D1 { get { return QueryCpuState(1); } }
|
||||||
public static int D2 { get { return QueryCpuState(2); } }
|
public static int D2 { get { return QueryCpuState(2); } }
|
||||||
|
|
|
@ -6,3 +6,26 @@ Timings:
|
||||||
- How many cycles does TRAP take to execute?
|
- How many cycles does TRAP take to execute?
|
||||||
- How many cycles does it take to accept an interrupt?
|
- How many cycles does it take to accept an interrupt?
|
||||||
- AND has some funky timings when it comes to immediates?
|
- 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
|
|
@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
int palette = (nameTableEntry >> 13) & 3;
|
int palette = (nameTableEntry >> 13) & 3;
|
||||||
|
|
||||||
if (priority && PriorityBuffer[x] >= highPriority) continue;
|
if (priority && PriorityBuffer[x] >= highPriority) continue;
|
||||||
if (PriorityBuffer[x] >= lowPriority) continue;
|
if (!priority && PriorityBuffer[x] >= lowPriority) continue;
|
||||||
|
|
||||||
if (vFlip) yOfs = 7 - yOfs;
|
if (vFlip) yOfs = 7 - yOfs;
|
||||||
if (hFlip) xOfs = 7 - xOfs;
|
if (hFlip) xOfs = 7 - xOfs;
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
public int FrameWidth = 256;
|
public int FrameWidth = 256;
|
||||||
|
|
||||||
public int ScanLine;
|
public int ScanLine;
|
||||||
|
public int HIntLineCounter;
|
||||||
|
|
||||||
public bool HInterruptsEnabled { get { return (Registers[0] & 0x10) != 0; } }
|
public bool HInterruptsEnabled { get { return (Registers[0] & 0x10) != 0; } }
|
||||||
public bool DisplayEnabled { get { return (Registers[1] & 0x40) != 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 CellBasedVertScroll { get { return (Registers[11] & 0x08) != 0; } }
|
||||||
public bool Display40Mode { get { return (Registers[12] & 0x81) != 0; } }
|
public bool Display40Mode { get { return (Registers[12] & 0x81) != 0; } }
|
||||||
|
|
||||||
|
public bool InDisplayPeriod { get { return ScanLine < 224 && DisplayEnabled; } }
|
||||||
|
|
||||||
ushort NameTableAddrA;
|
ushort NameTableAddrA;
|
||||||
ushort NameTableAddrB;
|
ushort NameTableAddrB;
|
||||||
ushort NameTableAddrWindow;
|
ushort NameTableAddrWindow;
|
||||||
|
@ -46,12 +49,12 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
const int CommandCramRead = 7;
|
const int CommandCramRead = 7;
|
||||||
|
|
||||||
public ushort VdpStatusWord = 0x3400;
|
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 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)
|
public ushort ReadVdp(int addr)
|
||||||
{
|
{
|
||||||
|
@ -64,8 +67,7 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
case 6:
|
case 6:
|
||||||
return ReadVdpControl();
|
return ReadVdpControl();
|
||||||
default:
|
default:
|
||||||
//throw new Exception("HV Counter read....");
|
return ReadHVCounter();
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +144,11 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
|
|
||||||
public ushort ReadVdpControl()
|
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;
|
return VdpStatusWord;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,9 +157,11 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
ControlWordPending = false;
|
ControlWordPending = false;
|
||||||
|
|
||||||
// byte-swap incoming data when A0 is set
|
// byte-swap incoming data when A0 is set
|
||||||
// TODO what is the reason for thinking that this happens?
|
if ((VdpDataAddr & 1) != 0)
|
||||||
//if ((VdpDataAddr & 1) != 0)
|
{
|
||||||
// data = (ushort) ((data >> 8) | (data << 8));
|
data = (ushort)((data >> 8) | (data << 8));
|
||||||
|
Console.WriteLine("VRAM byte-swap is happening because A0 is not 0");
|
||||||
|
}
|
||||||
|
|
||||||
if (DmaFillModePending)
|
if (DmaFillModePending)
|
||||||
{
|
{
|
||||||
|
@ -182,7 +190,7 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
VdpDataAddr += Registers[0x0F];
|
VdpDataAddr += Registers[0x0F];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
//Console.WriteLine("VDP DATA WRITE WITH UNHANDLED CODE!!!");
|
Console.WriteLine("VDP DATA WRITE WITH UNHANDLED CODE!!!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -203,11 +211,30 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
throw new Exception("VSRAM read");
|
throw new Exception("VSRAM read");
|
||||||
case CommandCramRead:
|
case CommandCramRead:
|
||||||
throw new Exception("CRAM read");
|
throw new Exception("CRAM read");
|
||||||
|
default:
|
||||||
|
throw new Exception("VRAM read with unexpected code!!!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval;
|
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)
|
public void WriteVdpRegister(int register, byte data)
|
||||||
{
|
{
|
||||||
Log.Note("VDP", "Register {0}: {1:X2}", register, data);
|
Log.Note("VDP", "Register {0}: {1:X2}", register, data);
|
||||||
|
@ -317,13 +344,13 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
case 0x11: // Window H Position
|
case 0x11: // Window H Position
|
||||||
int whp = data & 31;
|
int whp = data & 31;
|
||||||
bool fromright = (data & 0x80) != 0;
|
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;
|
break;
|
||||||
|
|
||||||
case 0x12: // Window V
|
case 0x12: // Window V
|
||||||
whp = data & 31;
|
whp = data & 31;
|
||||||
fromright = (data & 0x80) != 0;
|
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;
|
break;
|
||||||
|
|
||||||
case 0x13: // DMA Length Low
|
case 0x13: // DMA Length Low
|
||||||
|
|
|
@ -137,6 +137,12 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
Controller.UpdateControls(Frame++);
|
Controller.UpdateControls(Frame++);
|
||||||
PSG.BeginFrame(SoundCPU.TotalExecutedCycles);
|
PSG.BeginFrame(SoundCPU.TotalExecutedCycles);
|
||||||
YM2612.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++)
|
for (VDP.ScanLine = 0; VDP.ScanLine < 262; VDP.ScanLine++)
|
||||||
{
|
{
|
||||||
//Log.Error("VDP","FRAME {0}, SCANLINE {1}", Frame, VDP.ScanLine);
|
//Log.Error("VDP","FRAME {0}, SCANLINE {1}", Frame, VDP.ScanLine);
|
||||||
|
@ -164,12 +170,13 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
|
|
||||||
if (Z80Runnable)
|
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.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PSG.EndFrame(SoundCPU.TotalExecutedCycles);
|
PSG.EndFrame(SoundCPU.TotalExecutedCycles);
|
||||||
YM2612.EndFrame(SoundCPU.TotalExecutedCycles);
|
YM2612.EndFrame(SoundCPU.TotalExecutedCycles);
|
||||||
|
|
||||||
unchecked { VDP.VdpStatusWord &= (ushort)~GenVDP.StatusVerticalBlanking; }
|
|
||||||
|
|
||||||
if (lagged)
|
if (lagged)
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,7 +28,7 @@ namespace BizHawk.Emulation.Consoles.Sega
|
||||||
|
|
||||||
if (address >= 0xC00000 && address < 0xC00010)
|
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);
|
ushort value = VDP.ReadVdp(address & 0x0E);
|
||||||
if ((address & 1) == 0) // read MSB
|
if ((address & 1) == 0) // read MSB
|
||||||
return (sbyte) (value >> 8);
|
return (sbyte) (value >> 8);
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue