diff --git a/include/display.h b/include/display.h index 1553c4c4a..dda70d88a 100644 --- a/include/display.h +++ b/include/display.h @@ -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 * @@ -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 */ diff --git a/src/display.c b/src/display.c index 6ccc8c26a..7b5fe18ce 100644 --- a/src/display.c +++ b/src/display.c @@ -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 */ @@ -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); @@ -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 */ diff --git a/src/vi.h b/src/vi.h index 2a0ca22cc..bb6d830b1 100644 --- a/src/vi.h +++ b/src/vi.h @@ -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) @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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) }}; /** @} */