216 lines
9.2 KiB
C++
216 lines
9.2 KiB
C++
|
#include "stdafx.h"
|
||
|
#include "VideoInterfaceHandler.h"
|
||
|
#include <Project64-core\N64System\Mips\MemoryVirtualMem.h>
|
||
|
#include <Project64-core\N64System\Mips\SystemTiming.h>
|
||
|
#include <Project64-core\N64System\Mips\Register.h>
|
||
|
#include <Project64-core\Plugin.h>
|
||
|
#include <Project64-core\N64System\SystemGlobals.h>
|
||
|
|
||
|
VideoInterfaceReg::VideoInterfaceReg(uint32_t * VideoInterface) :
|
||
|
VI_STATUS_REG(VideoInterface[0]),
|
||
|
VI_CONTROL_REG(VideoInterface[0]),
|
||
|
VI_ORIGIN_REG(VideoInterface[1]),
|
||
|
VI_DRAM_ADDR_REG(VideoInterface[1]),
|
||
|
VI_WIDTH_REG(VideoInterface[2]),
|
||
|
VI_H_WIDTH_REG(VideoInterface[2]),
|
||
|
VI_INTR_REG(VideoInterface[3]),
|
||
|
VI_V_INTR_REG(VideoInterface[3]),
|
||
|
VI_CURRENT_REG(VideoInterface[4]),
|
||
|
VI_V_CURRENT_LINE_REG(VideoInterface[4]),
|
||
|
VI_BURST_REG(VideoInterface[5]),
|
||
|
VI_TIMING_REG(VideoInterface[5]),
|
||
|
VI_V_SYNC_REG(VideoInterface[6]),
|
||
|
VI_H_SYNC_REG(VideoInterface[7]),
|
||
|
VI_LEAP_REG(VideoInterface[8]),
|
||
|
VI_H_SYNC_LEAP_REG(VideoInterface[8]),
|
||
|
VI_H_START_REG(VideoInterface[9]),
|
||
|
VI_H_VIDEO_REG(VideoInterface[9]),
|
||
|
VI_V_START_REG(VideoInterface[10]),
|
||
|
VI_V_VIDEO_REG(VideoInterface[10]),
|
||
|
VI_V_BURST_REG(VideoInterface[11]),
|
||
|
VI_X_SCALE_REG(VideoInterface[12]),
|
||
|
VI_Y_SCALE_REG(VideoInterface[13])
|
||
|
{
|
||
|
}
|
||
|
|
||
|
VideoInterfaceHandler::VideoInterfaceHandler(CMipsMemoryVM & MMU, CPlugins * Plugins, CRegisters & Reg, CSystemTimer & SystemTimer, int32_t & NextTimer) :
|
||
|
VideoInterfaceReg(Reg.m_Video_Interface),
|
||
|
m_MMU(MMU),
|
||
|
m_Plugins(Plugins),
|
||
|
m_Reg(Reg),
|
||
|
m_SystemTimer(SystemTimer),
|
||
|
m_NextTimer(NextTimer),
|
||
|
m_PC(Reg.m_PROGRAM_COUNTER),
|
||
|
m_FieldSerration(0),
|
||
|
m_HalfLine(0),
|
||
|
m_HalfLineCheck(false)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
bool VideoInterfaceHandler::Read32(uint32_t Address, uint32_t & Value)
|
||
|
{
|
||
|
switch (Address & 0x1FFFFFFF)
|
||
|
{
|
||
|
case 0x04400000: Value = VI_STATUS_REG; break;
|
||
|
case 0x04400004: Value = VI_ORIGIN_REG; break;
|
||
|
case 0x04400008: Value = VI_WIDTH_REG; break;
|
||
|
case 0x0440000C: Value = VI_INTR_REG; break;
|
||
|
case 0x04400010:
|
||
|
UpdateHalfLine();
|
||
|
Value = m_HalfLine;
|
||
|
break;
|
||
|
case 0x04400014: Value = VI_BURST_REG; break;
|
||
|
case 0x04400018: Value = VI_V_SYNC_REG; break;
|
||
|
case 0x0440001C: Value = VI_H_SYNC_REG; break;
|
||
|
case 0x04400020: Value = VI_LEAP_REG; break;
|
||
|
case 0x04400024: Value = VI_H_START_REG; break;
|
||
|
case 0x04400028: Value = VI_V_START_REG; break;
|
||
|
case 0x0440002C: Value = VI_V_BURST_REG; break;
|
||
|
case 0x04400030: Value = VI_X_SCALE_REG; break;
|
||
|
case 0x04400034: Value = VI_Y_SCALE_REG; break;
|
||
|
default:
|
||
|
Value = 0;
|
||
|
if (HaveDebugger())
|
||
|
{
|
||
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (LogVideoInterface())
|
||
|
{
|
||
|
switch (Address & 0x1FFFFFFF)
|
||
|
{
|
||
|
case 0x04400000: LogMessage("%08X: read from VI_STATUS_REG/VI_CONTROL_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400004: LogMessage("%08X: read from VI_ORIGIN_REG/VI_DRAM_ADDR_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400008: LogMessage("%08X: read from VI_WIDTH_REG/VI_H_WIDTH_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x0440000C: LogMessage("%08X: read from VI_INTR_REG/VI_V_INTR_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400010: LogMessage("%08X: read from VI_CURRENT_REG/VI_V_CURRENT_LINE_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400014: LogMessage("%08X: read from VI_BURST_REG/VI_TIMING_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400018: LogMessage("%08X: read from VI_V_SYNC_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x0440001C: LogMessage("%08X: read from VI_H_SYNC_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400020: LogMessage("%08X: read from VI_LEAP_REG/VI_H_SYNC_LEAP_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400024: LogMessage("%08X: read from VI_H_START_REG/VI_H_VIDEO_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400028: LogMessage("%08X: read from VI_V_START_REG/VI_V_VIDEO_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x0440002C: LogMessage("%08X: read from VI_V_BURST_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400030: LogMessage("%08X: read from VI_X_SCALE_REG (%08X)", m_PC, Value); break;
|
||
|
case 0x04400034: LogMessage("%08X: read from VI_Y_SCALE_REG (%08X)", m_PC, Value); break;
|
||
|
default:
|
||
|
if (HaveDebugger())
|
||
|
{
|
||
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool VideoInterfaceHandler::Write32(uint32_t Address, uint32_t Value, uint32_t Mask)
|
||
|
{
|
||
|
if (LogVideoInterface())
|
||
|
{
|
||
|
switch (Address & 0x1FFFFFFF)
|
||
|
{
|
||
|
case 0x04400000: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_STATUS_REG/VI_CONTROL_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400004: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_ORIGIN_REG/VI_DRAM_ADDR_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400008: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_WIDTH_REG/VI_H_WIDTH_REG", m_PC, Value, Mask); break;
|
||
|
case 0x0440000C: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_INTR_REG/VI_V_INTR_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400010: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_CURRENT_REG/VI_V_CURRENT_LINE_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400014: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_BURST_REG/VI_TIMING_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400018: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_V_SYNC_REG", m_PC, Value, Mask); break;
|
||
|
case 0x0440001C: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_H_SYNC_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400020: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_LEAP_REG/VI_H_SYNC_LEAP_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400024: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_H_START_REG/VI_H_VIDEO_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400028: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_V_START_REG/VI_V_VIDEO_REG", m_PC, Value, Mask); break;
|
||
|
case 0x0440002C: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_V_BURST_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400030: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_X_SCALE_REG", m_PC, Value, Mask); break;
|
||
|
case 0x04400034: LogMessage("%08X: Writing 0x%08X (Mask: 0x%08X) to VI_Y_SCALE_REG", m_PC, Value, Mask); break;
|
||
|
default:
|
||
|
if (HaveDebugger())
|
||
|
{
|
||
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint32_t MaskedValue = Value & Mask;
|
||
|
switch (Address & 0xFFFFFFF)
|
||
|
{
|
||
|
case 0x04400000:
|
||
|
if (VI_STATUS_REG != ((VI_STATUS_REG & ~Mask) | (MaskedValue)))
|
||
|
{
|
||
|
VI_STATUS_REG = (VI_STATUS_REG & ~Mask) | (MaskedValue);
|
||
|
if (m_Plugins->Gfx()->ViStatusChanged != nullptr)
|
||
|
{
|
||
|
m_Plugins->Gfx()->ViStatusChanged();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 0x04400004:
|
||
|
VI_ORIGIN_REG = ((VI_ORIGIN_REG & ~Mask) | (MaskedValue)) & 0xFFFFFF;
|
||
|
break;
|
||
|
case 0x04400008:
|
||
|
if (VI_WIDTH_REG != ((VI_WIDTH_REG & ~Mask) | (MaskedValue)))
|
||
|
{
|
||
|
VI_WIDTH_REG = (VI_WIDTH_REG & ~Mask) | (MaskedValue);
|
||
|
if (m_Plugins->Gfx()->ViWidthChanged != nullptr)
|
||
|
{
|
||
|
m_Plugins->Gfx()->ViWidthChanged();
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 0x0440000C: VI_INTR_REG = (VI_INTR_REG & ~Mask) | (MaskedValue); break;
|
||
|
case 0x04400010:
|
||
|
m_Reg.MI_INTR_REG &= ~MI_INTR_VI;
|
||
|
m_Reg.CheckInterrupts();
|
||
|
break;
|
||
|
case 0x04400014: VI_BURST_REG = (VI_BURST_REG & ~Mask) | (MaskedValue); break;
|
||
|
case 0x04400018: VI_V_SYNC_REG = (VI_V_SYNC_REG & ~Mask) | (MaskedValue); break;
|
||
|
case 0x0440001C: VI_H_SYNC_REG = (VI_H_SYNC_REG & ~Mask) | (MaskedValue); break;
|
||
|
case 0x04400020: VI_LEAP_REG = (VI_LEAP_REG & ~Mask) | (MaskedValue); break;
|
||
|
case 0x04400024: VI_H_START_REG = (VI_H_START_REG & ~Mask) | (MaskedValue); break;
|
||
|
case 0x04400028: VI_V_START_REG = (VI_V_START_REG & ~Mask) | (MaskedValue); break;
|
||
|
case 0x0440002C: VI_V_BURST_REG = (VI_V_BURST_REG & ~Mask) | (MaskedValue); break;
|
||
|
case 0x04400030: VI_X_SCALE_REG = (VI_X_SCALE_REG & ~Mask) | (MaskedValue); break;
|
||
|
case 0x04400034: VI_Y_SCALE_REG = (VI_Y_SCALE_REG & ~Mask) | (MaskedValue); break;
|
||
|
default:
|
||
|
if (HaveDebugger())
|
||
|
{
|
||
|
g_Notify->BreakPoint(__FILE__, __LINE__);
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void VideoInterfaceHandler::UpdateFieldSerration(uint32_t interlaced)
|
||
|
{
|
||
|
m_FieldSerration ^= 1;
|
||
|
m_FieldSerration &= interlaced;
|
||
|
}
|
||
|
|
||
|
void VideoInterfaceHandler::UpdateHalfLine()
|
||
|
{
|
||
|
uint32_t NextViTimer = m_SystemTimer.GetTimer(CSystemTimer::ViTimer);
|
||
|
|
||
|
if (m_NextTimer < 0)
|
||
|
{
|
||
|
m_HalfLine = 0;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int32_t check_value = (int32_t)(m_HalfLineCheck - NextViTimer);
|
||
|
if (check_value > 0 && check_value < 40)
|
||
|
{
|
||
|
m_NextTimer -= ViRefreshRate();
|
||
|
if (m_NextTimer < 0)
|
||
|
{
|
||
|
m_NextTimer = 0 - CountPerOp();
|
||
|
}
|
||
|
m_SystemTimer.UpdateTimers();
|
||
|
NextViTimer = m_SystemTimer.GetTimer(CSystemTimer::ViTimer);
|
||
|
}
|
||
|
m_HalfLine = (uint32_t)(m_NextTimer / ViRefreshRate());
|
||
|
m_HalfLine &= ~1;
|
||
|
m_HalfLine |= m_FieldSerration;
|
||
|
VI_V_CURRENT_LINE_REG = m_HalfLine;
|
||
|
m_HalfLineCheck = NextViTimer;
|
||
|
}
|