Skip to content

Commit

Permalink
display: Add basic VI borders to resolution_t
Browse files Browse the repository at this point in the history
Border sizes are fully configurable. Sensible pre-configured option for handling overscan on CRT TVs is provided.
  • Loading branch information
thekovic committed Nov 21, 2024
1 parent 23bba79 commit 2247c56
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 33 deletions.
52 changes: 46 additions & 6 deletions include/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ typedef enum {
INTERLACE_FULL,
} interlace_mode_t;

/**
* @brief Video Interface borders structure
*
* This structure defines how thick (in dots) should the borders around a framebuffer be.
* The dots always assume VI scale (that is 640x480 NTSC or 640x576 PAL)
* and the framebuffer will be scaled to fit under them.
* For example, when displaying on CRT TVs, one can add borders around a framebuffer so
* that the whole image can be seen on the screen.
*
* If no borders are applied, the output will use the entire NSTC/PAL region space
* for showing a framebuffer, useful for emulators, upscalers, and LCD TVs.
*/
typedef struct vi_borders_s{
uint16_t left, right, up, down;
} vi_borders_t;

/**
* @brief Video resolution structure
*
Expand All @@ -78,23 +94,47 @@ typedef struct {
* Setting this variable to true on NTSC/MPAL will have no effect.
*/
bool pal60;
/**
* @brief
*
* This setting will enable additional borders around your display to cover
* the overscan margin of some CRT TVs to help ensure your entire frame is
* visible on the screen.
*
* Screen resolution is not affected by it. Due to the way VI scaling works,
* it is advisable to not use the #FILTERS_DISABLED for your display if borders
* are enabled.
*/
vi_borders_t vi_borders;
} resolution_t;

///@cond
#define const static const /* fool doxygen to document these static members */
///@endcond
/**
* @brief
* No VI borders defined. Useful when outputing for emulators, upscalers, or LCD TVs.
*/
const vi_borders_t VI_BORDERS_NONE = {0, 0, 0, 0};
/**
* @brief
* VI border preset that leaves a 5% margin on each side. Useful when outputing
* for CRT TVs in order to account for possible overscan to ensure the frame is
* visible on the screen.
*/
const vi_borders_t VI_BORDERS_CRT = {32, 32, 24, 24};
/** @brief 256x240 mode */
const resolution_t RESOLUTION_256x240 = {256, 240, INTERLACE_OFF};
const resolution_t RESOLUTION_256x240 = {.width = 256, .height = 240, .interlaced = INTERLACE_OFF, .vi_borders = VI_BORDERS_NONE};
/** @brief 320x240 mode */
const resolution_t RESOLUTION_320x240 = {320, 240, INTERLACE_OFF};
const resolution_t RESOLUTION_320x240 = {.width = 320, .height = 240, .interlaced = INTERLACE_OFF, .vi_borders = VI_BORDERS_NONE};
/** @brief 512x240 mode, high-res progressive */
const resolution_t RESOLUTION_512x240 = {512, 240, INTERLACE_OFF};
const resolution_t RESOLUTION_512x240 = {.width = 512, .height = 240, .interlaced = INTERLACE_OFF, .vi_borders = VI_BORDERS_NONE};
/** @brief 640x240 mode, high-res progressive */
const resolution_t RESOLUTION_640x240 = {640, 240, INTERLACE_OFF};
const resolution_t RESOLUTION_640x240 = {.width = 640, .height = 240, .interlaced = INTERLACE_OFF, .vi_borders = VI_BORDERS_NONE};
/** @brief 512x480 mode, interlaced */
const resolution_t RESOLUTION_512x480 = {512, 480, INTERLACE_HALF};
const resolution_t RESOLUTION_512x480 = {.width = 512, .height = 480, .interlaced = INTERLACE_HALF, .vi_borders = VI_BORDERS_NONE};
/** @brief 640x480 mode, interlaced */
const resolution_t RESOLUTION_640x480 = {640, 480, INTERLACE_HALF};
const resolution_t RESOLUTION_640x480 = {.width = 640, .height = 480, .interlaced = INTERLACE_HALF, .vi_borders = VI_BORDERS_NONE};
#undef const

/** @brief Valid bit depths */
Expand Down
19 changes: 10 additions & 9 deletions src/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ static uint32_t __width;
static uint32_t __height;
/** @brief Currently active video interlace mode */
static interlace_mode_t __interlace_mode = INTERLACE_OFF;
/** @brief Current VI display borders */
static vi_borders_t __borders;
/** @brief Number of active buffers */
static uint32_t __buffers = NUM_BUFFERS;
/** @brief Pointer to uncached 16-bit aligned version of buffers */
Expand Down Expand Up @@ -366,6 +368,7 @@ void display_init( resolution_t res, bitdepth_t bit, uint32_t num_buffers, gamma
__height = res.height;
__bitdepth = ( bit == DEPTH_16_BPP ) ? 2 : 4;
__interlace_mode = res.interlaced;
__borders = res.vi_borders;

surfaces = malloc(sizeof(surface_t) * __buffers);

Expand Down Expand Up @@ -409,18 +412,16 @@ void display_init( resolution_t res, bitdepth_t bit, uint32_t num_buffers, gamma
vi_write_safe(VI_V_VIDEO, (serrate) ? vi_ntsc_i.regs[VI_TO_INDEX(VI_V_VIDEO)] : vi_ntsc_p.regs[VI_TO_INDEX(VI_V_VIDEO)]);
}

// Configure scaling and positioning, taking into account VI border settings.
vi_write_safe(VI_H_VIDEO, *VI_H_VIDEO + VI_H_VIDEO_SET(__borders.left, 0) - VI_H_VIDEO_SET(0, __borders.right));
vi_write_safe(VI_X_SCALE, VI_X_SCALE_SET(__width, 640 - __borders.left - __borders.right));
vi_write_safe(VI_V_VIDEO, *VI_V_VIDEO + VI_V_VIDEO_SET(__borders.up, 0) - VI_V_VIDEO_SET(0, __borders.down));
const uint32_t base_height = (__tv_type == TV_PAL) ? 288 : 240;
vi_write_safe(VI_Y_SCALE, VI_Y_SCALE_SET(__height, base_height - __borders.up));

/* Configure other VI registers */
vi_write_safe(VI_ORIGIN, PhysicalAddr(__safe_buffer[0]));
vi_write_safe(VI_WIDTH, res.width);
vi_write_safe(VI_X_SCALE, VI_X_SCALE_SET(res.width));
if (__tv_type == TV_PAL)
{
vi_write_safe(VI_Y_SCALE, VI_Y_SCALE_SET_288_LINES(res.height));
}
else
{
vi_write_safe(VI_Y_SCALE, VI_Y_SCALE_SET_240_LINES(res.height));
}
vi_write_safe(VI_CTRL, control);

/* Calculate actual refresh rate for this configuration */
Expand Down
32 changes: 14 additions & 18 deletions src/vi.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,11 @@ typedef struct vi_config_s{

/** Under VI_X_SCALE */
/** @brief VI_X_SCALE Register: set 1/horizontal scale up factor (value is converted to 2.10 format) */
#define VI_X_SCALE_SET(value) (( 1024*(value) + 320 ) / 640)
#define VI_X_SCALE_SET(from, to) ((1024 * (from) + (to) / 2 ) / (to))

/** Under VI_Y_SCALE */
/** @brief VI_Y_SCALE Register: set 1/vertical scale up factor (value is converted to 2.10 format) */
#define VI_Y_SCALE_SET_240_LINES(value) (( 1024*(value) + 120 ) / 240)

/** Under VI_Y_SCALE */
/** @brief VI_Y_SCALE Register: set 1/vertical scale up factor (value is converted to 2.10 format) */
#define VI_Y_SCALE_SET_288_LINES(value) (( 1024*(value) + 144 ) / 288)
#define VI_Y_SCALE_SET(from, to) ((1024 * (from) + (to) / 2 ) / (to))

/** @brief VI period for showing one NTSC and MPAL picture in ms. */
#define VI_PERIOD_NTSC_MPAL ((float)1000/60)
Expand All @@ -227,8 +223,8 @@ static const vi_config_t vi_ntsc_p = {.regs = {
VI_H_VIDEO_SET(108, 748),
VI_V_VIDEO_SET(35, 515),
VI_V_BURST_SET(14, 516),
VI_X_SCALE_SET(0),
VI_Y_SCALE_SET_240_LINES(0),
VI_X_SCALE_SET(0, 640),
VI_Y_SCALE_SET(0, 240),
}};
static const vi_config_t vi_pal_p = {.regs = {
0,
Expand All @@ -243,8 +239,8 @@ static const vi_config_t vi_pal_p = {.regs = {
VI_H_VIDEO_SET(128, 768),
VI_V_VIDEO_SET(45, 621),
VI_V_BURST_SET(9, 619),
VI_X_SCALE_SET(0),
VI_Y_SCALE_SET_288_LINES(0),
VI_X_SCALE_SET(0, 640),
VI_Y_SCALE_SET(0, 288),
}};
static const vi_config_t vi_mpal_p = {.regs = {
0,
Expand All @@ -259,8 +255,8 @@ static const vi_config_t vi_mpal_p = {.regs = {
VI_H_VIDEO_SET(108, 748),
VI_V_VIDEO_SET(37, 511),
VI_V_BURST_SET(14, 516),
VI_X_SCALE_SET(0),
VI_Y_SCALE_SET_240_LINES(0)
VI_X_SCALE_SET(0, 640),
VI_Y_SCALE_SET(0, 240)
}};
static const vi_config_t vi_ntsc_i = {.regs = {
0,
Expand All @@ -275,8 +271,8 @@ static const vi_config_t vi_ntsc_i = {.regs = {
VI_H_VIDEO_SET(108, 748),
VI_V_VIDEO_SET(35, 515),
VI_V_BURST_SET(14, 516),
VI_X_SCALE_SET(0),
VI_Y_SCALE_SET_240_LINES(0)
VI_X_SCALE_SET(0, 640),
VI_Y_SCALE_SET(0, 240)
}};
static const vi_config_t vi_pal_i = {.regs = {
0,
Expand All @@ -291,8 +287,8 @@ static const vi_config_t vi_pal_i = {.regs = {
VI_H_VIDEO_SET(128, 768),
VI_V_VIDEO_SET(45, 621),
VI_V_BURST_SET(9, 619),
VI_X_SCALE_SET(0),
VI_Y_SCALE_SET_288_LINES(0)
VI_X_SCALE_SET(0, 640),
VI_Y_SCALE_SET(0, 288)
}};
static const vi_config_t vi_mpal_i = {.regs = {
0,
Expand All @@ -307,8 +303,8 @@ static const vi_config_t vi_mpal_i = {.regs = {
VI_H_VIDEO_SET(108, 748),
VI_V_VIDEO_SET(35, 509),
VI_V_BURST_SET(11, 514),
VI_X_SCALE_SET(0),
VI_Y_SCALE_SET_240_LINES(0)
VI_X_SCALE_SET(0, 640),
VI_Y_SCALE_SET(0, 240)
}};
/** @} */

Expand Down

0 comments on commit 2247c56

Please sign in to comment.