Fixed per scanline interrupts. No artifacts in MMCOI
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Core.Interfaces;
|
||||
using System.IO;
|
||||
|
||||
namespace Core.Audio
|
||||
{
|
||||
@@ -196,50 +197,29 @@ namespace Core.Audio
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveState(BinaryWriter bw)
|
||||
{
|
||||
for (int i = 0; i < 8; i++) bw.Write(Registers[i]);
|
||||
bw.Write(_latchedRegister);
|
||||
bw.Write(_sampleCycleTracker);
|
||||
bw.Write(_psgCycleTracker);
|
||||
for (int i = 0; i < 4; i++) { bw.Write(_counters[i]); bw.Write(_polarities[i]); }
|
||||
bw.Write(_lfsr);
|
||||
bw.Write(_previousSample);
|
||||
bw.Write(_previousFiltered);
|
||||
}
|
||||
|
||||
public void LoadState(BinaryReader br)
|
||||
{
|
||||
for (int i = 0; i < 8; i++) Registers[i] = br.ReadUInt16();
|
||||
_latchedRegister = br.ReadInt32();
|
||||
_sampleCycleTracker = br.ReadDouble();
|
||||
_psgCycleTracker = br.ReadInt32();
|
||||
for (int i = 0; i < 4; i++) { _counters[i] = br.ReadInt32(); _polarities[i] = br.ReadInt32(); }
|
||||
_lfsr = br.ReadUInt16();
|
||||
_previousSample = br.ReadSingle();
|
||||
_previousFiltered = br.ReadSingle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//using System;
|
||||
|
||||
//namespace Core.Audio
|
||||
//{
|
||||
// public class SmsApu
|
||||
// {
|
||||
// // The 8 internal registers of the PSG
|
||||
// // 0: Tone 0 Frequency (10 bits)
|
||||
// // 1: Tone 0 Volume (4 bits)
|
||||
// // 2: Tone 1 Frequency (10 bits)
|
||||
// // 3: Tone 1 Volume (4 bits)
|
||||
// // 4: Tone 2 Frequency (10 bits)
|
||||
// // 5: Tone 2 Volume (4 bits)
|
||||
// // 6: Noise Control (3 bits)
|
||||
// // 7: Noise Volume (4 bits)
|
||||
// public ushort[] Registers { get; private set; } = new ushort[8];
|
||||
|
||||
// // Remembers which register the CPU is currently talking to
|
||||
// private int _latchedRegister = 0;
|
||||
|
||||
// public SmsApu()
|
||||
// {
|
||||
// // Volumes default to 0x0F (Silence! 0 = max volume, 15 = off)
|
||||
// Registers[1] = 0x0F;
|
||||
// Registers[3] = 0x0F;
|
||||
// Registers[5] = 0x0F;
|
||||
// Registers[7] = 0x0F;
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
//}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Core.Interfaces;
|
||||
using Core.Io;
|
||||
|
||||
@@ -97,6 +98,32 @@ namespace Core.Cpu
|
||||
TotalTStates = 0;
|
||||
}
|
||||
|
||||
public void SaveState(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(TotalTStates);
|
||||
bw.Write(InterruptMode);
|
||||
bw.Write(IFF1);
|
||||
bw.Write(IFF2);
|
||||
bw.Write(InterruptRequested);
|
||||
bw.Write(AF.Word); bw.Write(BC.Word); bw.Write(DE.Word); bw.Write(HL.Word);
|
||||
bw.Write(AF_Prime.Word); bw.Write(BC_Prime.Word); bw.Write(DE_Prime.Word); bw.Write(HL_Prime.Word);
|
||||
bw.Write(IX.Word); bw.Write(IY.Word);
|
||||
bw.Write(PC); bw.Write(SP); bw.Write(I); bw.Write(R);
|
||||
}
|
||||
|
||||
public void LoadState(BinaryReader br)
|
||||
{
|
||||
TotalTStates = br.ReadInt64();
|
||||
InterruptMode = br.ReadInt32();
|
||||
IFF1 = br.ReadBoolean();
|
||||
IFF2 = br.ReadBoolean();
|
||||
InterruptRequested = br.ReadBoolean();
|
||||
AF.Word = br.ReadUInt16(); BC.Word = br.ReadUInt16(); DE.Word = br.ReadUInt16(); HL.Word = br.ReadUInt16();
|
||||
AF_Prime.Word = br.ReadUInt16(); BC_Prime.Word = br.ReadUInt16(); DE_Prime.Word = br.ReadUInt16(); HL_Prime.Word = br.ReadUInt16();
|
||||
IX.Word = br.ReadUInt16(); IY.Word = br.ReadUInt16();
|
||||
PC = br.ReadUInt16(); SP = br.ReadUInt16(); I = br.ReadByte(); R = br.ReadByte();
|
||||
}
|
||||
|
||||
private void ApplyWaitStates(ushort address)
|
||||
{
|
||||
// If a system (like a ULA) is attached and listening, ask it for the delay
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Core.Interfaces;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Core.Memory
|
||||
{
|
||||
@@ -225,6 +226,28 @@ namespace Core.Memory
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveState(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(_workRam);
|
||||
bw.Write(_cartridgeRam);
|
||||
bw.Write(SramUsed);
|
||||
bw.Write(_mapperControl);
|
||||
bw.Write(_romBank0);
|
||||
bw.Write(_romBank1);
|
||||
bw.Write(_romBank2);
|
||||
}
|
||||
|
||||
public void LoadState(BinaryReader br)
|
||||
{
|
||||
Array.Copy(br.ReadBytes(_workRam.Length), _workRam, _workRam.Length);
|
||||
Array.Copy(br.ReadBytes(_cartridgeRam.Length), _cartridgeRam, _cartridgeRam.Length);
|
||||
SramUsed = br.ReadBoolean();
|
||||
_mapperControl = br.ReadByte();
|
||||
_romBank0 = br.ReadInt32();
|
||||
_romBank1 = br.ReadInt32();
|
||||
_romBank2 = br.ReadInt32();
|
||||
}
|
||||
|
||||
public void CleanRAMData()
|
||||
{
|
||||
Array.Clear(_workRam, 0, _workRam.Length);
|
||||
|
||||
@@ -73,5 +73,30 @@ namespace Core
|
||||
}
|
||||
}
|
||||
}
|
||||
public void SaveState(string filePath)
|
||||
{
|
||||
using (var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
|
||||
using (var bw = new System.IO.BinaryWriter(fs))
|
||||
{
|
||||
Cpu.SaveState(bw);
|
||||
MemoryBus.SaveState(bw);
|
||||
VideoProcessor.SaveState(bw);
|
||||
AudioProcessor.SaveState(bw);
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadState(string filePath)
|
||||
{
|
||||
if (!System.IO.File.Exists(filePath)) return;
|
||||
|
||||
using (var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Open))
|
||||
using (var br = new System.IO.BinaryReader(fs))
|
||||
{
|
||||
Cpu.LoadState(br);
|
||||
MemoryBus.LoadState(br);
|
||||
VideoProcessor.LoadState(br);
|
||||
AudioProcessor.LoadState(br);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Core.Video
|
||||
{
|
||||
@@ -11,6 +12,10 @@ namespace Core.Video
|
||||
public int[] FrameBuffer { get; private set; } = new int[256 * 192];
|
||||
private bool[] _priorityBuffer = new bool[256 * 192]; // Tracks priority pixels!
|
||||
|
||||
// Hardware Latches
|
||||
private int _latchedHScroll = 0;
|
||||
private int _latchedVScroll = 0;
|
||||
|
||||
// The Control Port State Machine (Port 0xBF)
|
||||
private bool _isSecondControlByte = false;
|
||||
private ushort _controlWord = 0;
|
||||
@@ -109,12 +114,20 @@ namespace Core.Video
|
||||
{
|
||||
_tStateCounter += tStates;
|
||||
|
||||
// 228 T-States per scanline
|
||||
if (_tStateCounter >= 228)
|
||||
{
|
||||
_tStateCounter -= 228;
|
||||
|
||||
// --- MISSING LINE INTERRUPT COUNTDOWN ---
|
||||
// 1. RENDER THE CURRENT LINE FIRST!
|
||||
// The CPU just finished spending 228 cycles on this exact line.
|
||||
// We draw it now using whatever scroll values the CPU set during that time.
|
||||
if (_currentScanline < 192)
|
||||
{
|
||||
RenderScanline(_currentScanline);
|
||||
}
|
||||
|
||||
// 2. CHECK LINE INTERRUPTS
|
||||
// Now that the line is drawn, we check if we need to alert the CPU for the NEXT line.
|
||||
if (_currentScanline <= 192)
|
||||
{
|
||||
_lineCounter--;
|
||||
@@ -128,24 +141,21 @@ namespace Core.Video
|
||||
{
|
||||
_lineCounter = Registers[10]; // Reload outside active display
|
||||
}
|
||||
// ----------------------------------------
|
||||
|
||||
// 3. MOVE TO THE NEXT LINE
|
||||
_currentScanline++;
|
||||
|
||||
if (_currentScanline < 192)
|
||||
{
|
||||
RenderScanline(_currentScanline);
|
||||
}
|
||||
else if (_currentScanline == 192)
|
||||
{
|
||||
_statusRegister |= 0x80; // Set VBlank Flag
|
||||
}
|
||||
|
||||
// End of the NTSC frame (262 lines)
|
||||
if (_currentScanline > 261)
|
||||
{
|
||||
_currentScanline = 0;
|
||||
}
|
||||
|
||||
// 4. TRIGGER VBLANK
|
||||
// The Master System sets the VBlank flag at the exact start of scanline 192.
|
||||
if (_currentScanline == 192)
|
||||
{
|
||||
_statusRegister |= 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,8 +170,8 @@ namespace Core.Video
|
||||
|
||||
// --- 1. RENDER BACKGROUND LINE ---
|
||||
ushort nameTableBase = (ushort)((Registers[2] & 0x0E) << 10);
|
||||
byte scrollX = Registers[8];
|
||||
byte scrollY = Registers[9];
|
||||
int scrollX = Registers[8];
|
||||
int scrollY = Registers[9];
|
||||
|
||||
// THE FIX: The bits are now in the correct order!
|
||||
bool lockColScroll = (Registers[0] & 0x80) != 0; // Bit 7: Locks right 8 columns (Fixes R-Type!)
|
||||
@@ -303,5 +313,40 @@ namespace Core.Video
|
||||
}
|
||||
}
|
||||
}
|
||||
public void SaveState(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(VRAM);
|
||||
bw.Write(CRAM);
|
||||
bw.Write(Registers);
|
||||
bw.Write(_isSecondControlByte);
|
||||
bw.Write(_controlWord);
|
||||
bw.Write(_readBuffer);
|
||||
bw.Write(_tStateCounter);
|
||||
bw.Write(_currentScanline);
|
||||
bw.Write(_lineCounter);
|
||||
bw.Write(_statusRegister);
|
||||
|
||||
// ADD THESE:
|
||||
bw.Write(_latchedHScroll);
|
||||
bw.Write(_latchedVScroll);
|
||||
}
|
||||
|
||||
public void LoadState(BinaryReader br)
|
||||
{
|
||||
Array.Copy(br.ReadBytes(VRAM.Length), VRAM, VRAM.Length);
|
||||
Array.Copy(br.ReadBytes(CRAM.Length), CRAM, CRAM.Length);
|
||||
Array.Copy(br.ReadBytes(Registers.Length), Registers, Registers.Length);
|
||||
_isSecondControlByte = br.ReadBoolean();
|
||||
_controlWord = br.ReadUInt16();
|
||||
_readBuffer = br.ReadByte();
|
||||
_tStateCounter = br.ReadInt32();
|
||||
_currentScanline = br.ReadInt32();
|
||||
_lineCounter = br.ReadInt32();
|
||||
_statusRegister = br.ReadByte();
|
||||
|
||||
// ADD THESE:
|
||||
_latchedHScroll = br.ReadInt32();
|
||||
_latchedVScroll = br.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user