|
|
@ -28,6 +28,7 @@ bool GPU::Initialize(HostDisplay* host_display, System* system, DMA* dma, Interr
|
|
|
|
m_interrupt_controller = interrupt_controller;
|
|
|
|
m_interrupt_controller = interrupt_controller;
|
|
|
|
m_timers = timers;
|
|
|
|
m_timers = timers;
|
|
|
|
m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan;
|
|
|
|
m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan;
|
|
|
|
|
|
|
|
m_force_ntsc_timings = m_system->GetSettings().gpu_force_ntsc_timings;
|
|
|
|
m_tick_event =
|
|
|
|
m_tick_event =
|
|
|
|
m_system->CreateTimingEvent("GPU Tick", 1, 1, std::bind(&GPU::Execute, this, std::placeholders::_1), true);
|
|
|
|
m_system->CreateTimingEvent("GPU Tick", 1, 1, std::bind(&GPU::Execute, this, std::placeholders::_1), true);
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
@ -37,6 +38,12 @@ void GPU::UpdateSettings()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan;
|
|
|
|
m_force_progressive_scan = m_system->GetSettings().display_force_progressive_scan;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (m_force_ntsc_timings != m_system->GetSettings().gpu_force_ntsc_timings)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_force_ntsc_timings = m_system->GetSettings().gpu_force_ntsc_timings;
|
|
|
|
|
|
|
|
UpdateCRTCConfig();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Crop mode calls this, so recalculate the display area
|
|
|
|
// Crop mode calls this, so recalculate the display area
|
|
|
|
UpdateCRTCDisplayParameters();
|
|
|
|
UpdateCRTCDisplayParameters();
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -342,11 +349,6 @@ void GPU::UpdateCRTCConfig()
|
|
|
|
cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE;
|
|
|
|
cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total;
|
|
|
|
|
|
|
|
const float vertical_frequency =
|
|
|
|
|
|
|
|
static_cast<float>(static_cast<double>((u64(MASTER_CLOCK) * 11) / 7) / static_cast<double>(ticks_per_frame));
|
|
|
|
|
|
|
|
m_system->SetThrottleFrequency(vertical_frequency);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const u8 horizontal_resolution_index = m_GPUSTAT.horizontal_resolution_1 | (m_GPUSTAT.horizontal_resolution_2 << 2);
|
|
|
|
const u8 horizontal_resolution_index = m_GPUSTAT.horizontal_resolution_1 | (m_GPUSTAT.horizontal_resolution_2 << 2);
|
|
|
|
cs.dot_clock_divider = dot_clock_dividers[horizontal_resolution_index];
|
|
|
|
cs.dot_clock_divider = dot_clock_dividers[horizontal_resolution_index];
|
|
|
|
cs.horizontal_display_start = std::min<u16>(cs.regs.X1, cs.horizontal_total);
|
|
|
|
cs.horizontal_display_start = std::min<u16>(cs.regs.X1, cs.horizontal_total);
|
|
|
@ -354,6 +356,30 @@ void GPU::UpdateCRTCConfig()
|
|
|
|
cs.vertical_display_start = std::min<u16>(cs.regs.Y1, cs.vertical_total);
|
|
|
|
cs.vertical_display_start = std::min<u16>(cs.regs.Y1, cs.vertical_total);
|
|
|
|
cs.vertical_display_end = std::min<u16>(cs.regs.Y2, cs.vertical_total);
|
|
|
|
cs.vertical_display_end = std::min<u16>(cs.regs.Y2, cs.vertical_total);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (m_GPUSTAT.pal_mode && m_force_ntsc_timings)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// scale to NTSC parameters
|
|
|
|
|
|
|
|
cs.horizontal_display_start =
|
|
|
|
|
|
|
|
static_cast<u16>((static_cast<u32>(cs.horizontal_display_start) * NTSC_TICKS_PER_LINE) / PAL_TICKS_PER_LINE);
|
|
|
|
|
|
|
|
cs.horizontal_display_end = static_cast<u16>(
|
|
|
|
|
|
|
|
((static_cast<u32>(cs.horizontal_display_end) * NTSC_TICKS_PER_LINE) + (PAL_TICKS_PER_LINE - 1)) /
|
|
|
|
|
|
|
|
PAL_TICKS_PER_LINE);
|
|
|
|
|
|
|
|
cs.vertical_display_start =
|
|
|
|
|
|
|
|
static_cast<u16>((static_cast<u32>(cs.vertical_display_start) * NTSC_TOTAL_LINES) / PAL_TOTAL_LINES);
|
|
|
|
|
|
|
|
cs.vertical_display_end = static_cast<u16>(
|
|
|
|
|
|
|
|
((static_cast<u32>(cs.vertical_display_end) * NTSC_TOTAL_LINES) + (PAL_TOTAL_LINES - 1)) / PAL_TOTAL_LINES);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cs.vertical_total = NTSC_TOTAL_LINES;
|
|
|
|
|
|
|
|
cs.current_scanline %= NTSC_TOTAL_LINES;
|
|
|
|
|
|
|
|
cs.horizontal_total = NTSC_TICKS_PER_LINE;
|
|
|
|
|
|
|
|
cs.current_tick_in_scanline %= NTSC_TICKS_PER_LINE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const TickCount ticks_per_frame = cs.horizontal_total * cs.vertical_total;
|
|
|
|
|
|
|
|
const float vertical_frequency =
|
|
|
|
|
|
|
|
static_cast<float>(static_cast<double>((u64(MASTER_CLOCK) * 11) / 7) / static_cast<double>(ticks_per_frame));
|
|
|
|
|
|
|
|
m_system->SetThrottleFrequency(vertical_frequency);
|
|
|
|
|
|
|
|
|
|
|
|
UpdateCRTCDisplayParameters();
|
|
|
|
UpdateCRTCDisplayParameters();
|
|
|
|
UpdateSliceTicks();
|
|
|
|
UpdateSliceTicks();
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -363,33 +389,40 @@ void GPU::UpdateCRTCDisplayParameters()
|
|
|
|
CRTCState& cs = m_crtc_state;
|
|
|
|
CRTCState& cs = m_crtc_state;
|
|
|
|
const DisplayCropMode crop_mode = m_system->GetSettings().display_crop_mode;
|
|
|
|
const DisplayCropMode crop_mode = m_system->GetSettings().display_crop_mode;
|
|
|
|
|
|
|
|
|
|
|
|
u16 horizontal_display_start_tick, horizontal_display_end_tick;
|
|
|
|
const u16 horizontal_total = m_GPUSTAT.pal_mode ? PAL_TICKS_PER_LINE : NTSC_TICKS_PER_LINE;
|
|
|
|
u16 vertical_display_start_line, vertical_display_end_line;
|
|
|
|
const u16 vertical_total = m_GPUSTAT.pal_mode ? PAL_TOTAL_LINES : NTSC_TOTAL_LINES;
|
|
|
|
|
|
|
|
const u16 horizontal_display_start = std::min<u16>(cs.regs.X1, horizontal_total);
|
|
|
|
|
|
|
|
const u16 horizontal_display_end = std::min<u16>(cs.regs.X2, horizontal_total);
|
|
|
|
|
|
|
|
const u16 vertical_display_start = std::min<u16>(cs.regs.Y1, vertical_total);
|
|
|
|
|
|
|
|
const u16 vertical_display_end = std::min<u16>(cs.regs.Y2, vertical_total);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
u16 horizontal_visible_start_tick, horizontal_visible_end_tick;
|
|
|
|
|
|
|
|
u16 vertical_visible_start_line, vertical_visible_end_line;
|
|
|
|
if (m_GPUSTAT.pal_mode)
|
|
|
|
if (m_GPUSTAT.pal_mode)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// TODO: Verify PAL numbers.
|
|
|
|
// TODO: Verify PAL numbers.
|
|
|
|
switch (crop_mode)
|
|
|
|
switch (crop_mode)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
case DisplayCropMode::None:
|
|
|
|
case DisplayCropMode::None:
|
|
|
|
horizontal_display_start_tick = 487;
|
|
|
|
horizontal_visible_start_tick = 487;
|
|
|
|
horizontal_display_end_tick = 3282;
|
|
|
|
horizontal_visible_end_tick = 3282;
|
|
|
|
vertical_display_start_line = 20;
|
|
|
|
vertical_visible_start_line = 20;
|
|
|
|
vertical_display_end_line = 308;
|
|
|
|
vertical_visible_end_line = 308;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DisplayCropMode::Overscan:
|
|
|
|
case DisplayCropMode::Overscan:
|
|
|
|
horizontal_display_start_tick = 628;
|
|
|
|
horizontal_visible_start_tick = 628;
|
|
|
|
horizontal_display_end_tick = 3188;
|
|
|
|
horizontal_visible_end_tick = 3188;
|
|
|
|
vertical_display_start_line = 30;
|
|
|
|
vertical_visible_start_line = 30;
|
|
|
|
vertical_display_end_line = 298;
|
|
|
|
vertical_visible_end_line = 298;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DisplayCropMode::Borders:
|
|
|
|
case DisplayCropMode::Borders:
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
horizontal_display_start_tick = m_crtc_state.horizontal_display_start;
|
|
|
|
horizontal_visible_start_tick = horizontal_display_start;
|
|
|
|
horizontal_display_end_tick = m_crtc_state.horizontal_display_end;
|
|
|
|
horizontal_visible_end_tick = horizontal_display_end;
|
|
|
|
vertical_display_start_line = m_crtc_state.vertical_display_start;
|
|
|
|
vertical_visible_start_line = vertical_display_start;
|
|
|
|
vertical_display_end_line = m_crtc_state.vertical_display_end;
|
|
|
|
vertical_visible_end_line = vertical_display_end;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -398,25 +431,25 @@ void GPU::UpdateCRTCDisplayParameters()
|
|
|
|
switch (crop_mode)
|
|
|
|
switch (crop_mode)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
case DisplayCropMode::None:
|
|
|
|
case DisplayCropMode::None:
|
|
|
|
horizontal_display_start_tick = 488;
|
|
|
|
horizontal_visible_start_tick = 488;
|
|
|
|
horizontal_display_end_tick = 3288;
|
|
|
|
horizontal_visible_end_tick = 3288;
|
|
|
|
vertical_display_start_line = 16;
|
|
|
|
vertical_visible_start_line = 16;
|
|
|
|
vertical_display_end_line = 256;
|
|
|
|
vertical_visible_end_line = 256;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DisplayCropMode::Overscan:
|
|
|
|
case DisplayCropMode::Overscan:
|
|
|
|
horizontal_display_start_tick = 608;
|
|
|
|
horizontal_visible_start_tick = 608;
|
|
|
|
horizontal_display_end_tick = 3168;
|
|
|
|
horizontal_visible_end_tick = 3168;
|
|
|
|
vertical_display_start_line = 24;
|
|
|
|
vertical_visible_start_line = 24;
|
|
|
|
vertical_display_end_line = 248;
|
|
|
|
vertical_visible_end_line = 248;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case DisplayCropMode::Borders:
|
|
|
|
case DisplayCropMode::Borders:
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
horizontal_display_start_tick = m_crtc_state.horizontal_display_start;
|
|
|
|
horizontal_visible_start_tick = horizontal_display_start;
|
|
|
|
horizontal_display_end_tick = m_crtc_state.horizontal_display_end;
|
|
|
|
horizontal_visible_end_tick = horizontal_display_end;
|
|
|
|
vertical_display_start_line = m_crtc_state.vertical_display_start;
|
|
|
|
vertical_visible_start_line = vertical_display_start;
|
|
|
|
vertical_display_end_line = m_crtc_state.vertical_display_end;
|
|
|
|
vertical_visible_end_line = vertical_display_end;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -425,69 +458,67 @@ void GPU::UpdateCRTCDisplayParameters()
|
|
|
|
|
|
|
|
|
|
|
|
// Determine screen size.
|
|
|
|
// Determine screen size.
|
|
|
|
cs.display_width = std::max<u16>(
|
|
|
|
cs.display_width = std::max<u16>(
|
|
|
|
((horizontal_display_end_tick - horizontal_display_start_tick) + (cs.dot_clock_divider - 1)) / cs.dot_clock_divider,
|
|
|
|
((horizontal_visible_end_tick - horizontal_visible_start_tick) + (cs.dot_clock_divider - 1)) / cs.dot_clock_divider,
|
|
|
|
1u);
|
|
|
|
1u);
|
|
|
|
cs.display_height = std::max<u16>((vertical_display_end_line - vertical_display_start_line) << height_shift, 1u);
|
|
|
|
cs.display_height = std::max<u16>((vertical_visible_end_line - vertical_visible_start_line) << height_shift, 1u);
|
|
|
|
|
|
|
|
|
|
|
|
// Determine if we need to adjust the VRAM rectangle (because the display is starting outside the visible area) or add
|
|
|
|
// Determine if we need to adjust the VRAM rectangle (because the display is starting outside the visible area) or add
|
|
|
|
// padding.
|
|
|
|
// padding.
|
|
|
|
if (cs.horizontal_display_start >= horizontal_display_start_tick)
|
|
|
|
if (horizontal_display_start >= horizontal_visible_start_tick)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cs.display_origin_left = (cs.horizontal_display_start - horizontal_display_start_tick) / cs.dot_clock_divider;
|
|
|
|
cs.display_origin_left = (horizontal_display_start - horizontal_visible_start_tick) / cs.dot_clock_divider;
|
|
|
|
cs.display_vram_left = m_crtc_state.regs.X;
|
|
|
|
cs.display_vram_left = m_crtc_state.regs.X;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cs.display_origin_left = 0;
|
|
|
|
cs.display_origin_left = 0;
|
|
|
|
cs.display_vram_left = std::min<u16>(
|
|
|
|
cs.display_vram_left = std::min<u16>(
|
|
|
|
m_crtc_state.regs.X + ((horizontal_display_start_tick - cs.horizontal_display_start) / cs.dot_clock_divider),
|
|
|
|
m_crtc_state.regs.X + ((horizontal_visible_start_tick - horizontal_display_start) / cs.dot_clock_divider),
|
|
|
|
VRAM_WIDTH - 1);
|
|
|
|
VRAM_WIDTH - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cs.horizontal_display_end <= horizontal_display_end_tick)
|
|
|
|
if (horizontal_display_end <= horizontal_visible_end_tick)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cs.display_vram_width = std::min<u16>(
|
|
|
|
cs.display_vram_width = std::min<u16>(
|
|
|
|
std::max<u16>(
|
|
|
|
std::max<u16>((((horizontal_display_end - std::max(horizontal_display_start, horizontal_visible_start_tick)) +
|
|
|
|
(((cs.horizontal_display_end - std::max(cs.horizontal_display_start, horizontal_display_start_tick)) +
|
|
|
|
(cs.dot_clock_divider - 1)) /
|
|
|
|
(cs.dot_clock_divider - 1)) /
|
|
|
|
cs.dot_clock_divider),
|
|
|
|
cs.dot_clock_divider),
|
|
|
|
1u),
|
|
|
|
1u),
|
|
|
|
|
|
|
|
VRAM_WIDTH - cs.display_vram_left);
|
|
|
|
VRAM_WIDTH - cs.display_vram_left);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cs.display_vram_width = std::min<u16>(
|
|
|
|
cs.display_vram_width = std::min<u16>(
|
|
|
|
std::max<u16>(
|
|
|
|
std::max<u16>(
|
|
|
|
(((horizontal_display_end_tick - std::max(cs.horizontal_display_start, horizontal_display_start_tick)) +
|
|
|
|
(((horizontal_visible_end_tick - std::max(horizontal_display_start, horizontal_visible_start_tick)) +
|
|
|
|
(cs.dot_clock_divider - 1)) /
|
|
|
|
(cs.dot_clock_divider - 1)) /
|
|
|
|
cs.dot_clock_divider),
|
|
|
|
cs.dot_clock_divider),
|
|
|
|
1u),
|
|
|
|
1u),
|
|
|
|
VRAM_WIDTH - cs.display_vram_left);
|
|
|
|
VRAM_WIDTH - cs.display_vram_left);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cs.vertical_display_start >= vertical_display_start_line)
|
|
|
|
if (vertical_display_start >= vertical_visible_start_line)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cs.display_origin_top = (cs.vertical_display_start - vertical_display_start_line) << height_shift;
|
|
|
|
cs.display_origin_top = (vertical_display_start - vertical_visible_start_line) << height_shift;
|
|
|
|
cs.display_vram_top = m_crtc_state.regs.Y;
|
|
|
|
cs.display_vram_top = m_crtc_state.regs.Y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cs.display_origin_top = 0;
|
|
|
|
cs.display_origin_top = 0;
|
|
|
|
cs.display_vram_top =
|
|
|
|
cs.display_vram_top = std::min<u16>(
|
|
|
|
std::min<u16>(m_crtc_state.regs.Y + ((vertical_display_start_line - cs.vertical_display_start) << height_shift),
|
|
|
|
m_crtc_state.regs.Y + ((vertical_visible_start_line - vertical_display_start) << height_shift), VRAM_HEIGHT - 1);
|
|
|
|
VRAM_HEIGHT - 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cs.vertical_display_end <= vertical_display_end_line)
|
|
|
|
if (vertical_display_end <= vertical_visible_end_line)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cs.display_vram_height = std::min<u16>(
|
|
|
|
cs.display_vram_height = std::min<u16>(
|
|
|
|
(cs.vertical_display_end - std::max(cs.vertical_display_start, vertical_display_start_line)) << height_shift,
|
|
|
|
(vertical_display_end - std::max(vertical_display_start, vertical_visible_start_line)) << height_shift,
|
|
|
|
VRAM_HEIGHT - cs.display_vram_top);
|
|
|
|
VRAM_HEIGHT - cs.display_vram_top);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
|
{
|
|
|
|
{
|
|
|
|
cs.display_vram_height = std::min<u16>(
|
|
|
|
cs.display_vram_height = std::min<u16>(
|
|
|
|
(vertical_display_end_line - std::max(cs.vertical_display_start, vertical_display_start_line)) << height_shift,
|
|
|
|
(vertical_visible_end_line - std::max(vertical_display_start, vertical_visible_start_line)) << height_shift,
|
|
|
|
VRAM_HEIGHT - cs.display_vram_top);
|
|
|
|
VRAM_HEIGHT - cs.display_vram_top);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|