mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 10:19:47 -04:00
107 lines
3.7 KiB
C
107 lines
3.7 KiB
C
/* display_scanout.h — small, renderer-neutral CRTC presentation helpers. */
|
|
|
|
#ifndef PSXRECOMP_DISPLAY_SCANOUT_H
|
|
#define PSXRECOMP_DISPLAY_SCANOUT_H
|
|
|
|
#include <stdint.h>
|
|
|
|
/* PAL active video is 288 lines per field and 576 rows when the interlaced
|
|
* display bit is set. CPU presentation buffers must preserve that full
|
|
* canvas even though PS1 VRAM itself remains 512 rows and wraps on access. */
|
|
#define PSX_DISPLAY_PRESENT_MAX_HEIGHT 576u
|
|
|
|
typedef struct {
|
|
uint32_t canvas_height;
|
|
uint32_t canvas_origin_y;
|
|
uint32_t source_skip_y;
|
|
uint32_t source_height;
|
|
int offset_y;
|
|
int range_set;
|
|
int valid;
|
|
} PsxDisplayVerticalLayout;
|
|
|
|
/* Depth24 staging expands the visible source into the full active-video
|
|
* canvas. Every presentation backend must therefore consume the canvas
|
|
* height, while ordinary 15-bit display keeps its source height. */
|
|
static inline uint32_t psx_display_present_height(
|
|
int depth24, uint32_t source_height, uint32_t canvas_height)
|
|
{
|
|
return depth24 && canvas_height != 0u ? canvas_height : source_height;
|
|
}
|
|
|
|
static inline uint32_t psx_display_interlaced_rows(
|
|
uint32_t rows, int interlaced)
|
|
{
|
|
return interlaced ? rows * 2u : rows;
|
|
}
|
|
|
|
/* Canvas-origin clipping belongs only to staged depth24 presentation.
|
|
* Ordinary 15-bit renderers read their source rectangle directly. */
|
|
static inline uint32_t psx_display_clip_source_height(
|
|
int depth24, uint32_t source_height, uint32_t canvas_height,
|
|
uint32_t canvas_origin_y)
|
|
{
|
|
if (!depth24)
|
|
return source_height;
|
|
if (canvas_origin_y >= canvas_height)
|
|
return 0u;
|
|
const uint32_t available = canvas_height - canvas_origin_y;
|
|
return source_height < available ? source_height : available;
|
|
}
|
|
|
|
/* Preserve the legacy default for an unset/degenerate GP1(07h) range. A
|
|
* programmed increasing range that has no active-video intersection owns a
|
|
* zero height instead, so presentation fails closed to black. */
|
|
static inline uint32_t psx_display_source_height(
|
|
PsxDisplayVerticalLayout layout, uint32_t fallback_height)
|
|
{
|
|
return layout.range_set ? layout.source_height : fallback_height;
|
|
}
|
|
|
|
/* Intersect a GP1(07h) range with the PAL or NTSC active region. The canvas
|
|
* preserves the television scanout position without discarding visible VRAM
|
|
* rows. source_skip_y owns rows before the active region; canvas_origin_y owns
|
|
* black rows before the visible source. */
|
|
static inline PsxDisplayVerticalLayout psx_display_vertical_layout(
|
|
int pal, uint32_t raw_y1, uint32_t raw_y2)
|
|
{
|
|
const int ymin = pal ? 20 : 16;
|
|
const int ymax = pal ? 308 : 256;
|
|
const int centre = pal ? 0xA3 : 0x88;
|
|
PsxDisplayVerticalLayout out = {0};
|
|
int y1 = (int)raw_y1;
|
|
int y2 = (int)raw_y2;
|
|
|
|
if (raw_y2 <= raw_y1)
|
|
return out;
|
|
out.range_set = 1;
|
|
out.canvas_height = (uint32_t)(ymax - ymin);
|
|
if (y1 < ymin) y1 = ymin;
|
|
if (y1 > ymax) y1 = ymax;
|
|
if (y2 < ymin) y2 = ymin;
|
|
if (y2 > ymax) y2 = ymax;
|
|
if (y2 <= y1)
|
|
return out;
|
|
|
|
out.canvas_origin_y = (uint32_t)(y1 - ymin);
|
|
out.source_skip_y = raw_y1 < (uint32_t)ymin
|
|
? (uint32_t)ymin - raw_y1 : 0u;
|
|
out.source_height = (uint32_t)(y2 - y1);
|
|
out.offset_y = ((y1 + y2) - (centre * 2)) / 2;
|
|
out.valid = 1;
|
|
return out;
|
|
}
|
|
|
|
/* Return the vertical position of a GP1(07h) display range relative to the
|
|
* broadcast centre. Positive values move the scanout down. The limits and
|
|
* centres are the PS1 PAL/NTSC active regions documented by psx-spx. */
|
|
static inline int psx_display_vertical_offset(int pal, uint32_t raw_y1,
|
|
uint32_t raw_y2)
|
|
{
|
|
PsxDisplayVerticalLayout layout = psx_display_vertical_layout(
|
|
pal, raw_y1, raw_y2);
|
|
return layout.valid ? layout.offset_y : 0;
|
|
}
|
|
|
|
#endif
|