sm501: Fix hardware cursor

Rework HWC handling to simplify it and fix cursor not updating on
screen as needed. Previously cursor was not updated because checking
for changes in a line overrode the update flag set for the cursor but
fixing this is not enough because the cursor should also be updated if
its shape or location changes. Introduce hwc_invalidate() function to
handle that similar to other display controller models.

Signed-off-by: BALATON Zoltan <balaton@eik.bme.hu>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Tested-by: Aurelien Jarno <aurelien@aurel32.net>
Message-id: 6970a5e9868b7246656c1d02038dc5d5fa369507.1492787889.git.balaton@eik.bme.hu
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
BALATON Zoltan 2017-04-21 17:18:09 +02:00 committed by Peter Maydell
parent afef2e1d53
commit 6a2a5aae02
2 changed files with 106 additions and 86 deletions

View File

@ -554,6 +554,24 @@ static uint32_t get_local_mem_size_index(uint32_t size)
return index;
}
static inline int get_width(SM501State *s, int crt)
{
int width = crt ? s->dc_crt_h_total : s->dc_panel_h_total;
return (width & 0x00000FFF) + 1;
}
static inline int get_height(SM501State *s, int crt)
{
int height = crt ? s->dc_crt_v_total : s->dc_panel_v_total;
return (height & 0x00000FFF) + 1;
}
static inline int get_bpp(SM501State *s, int crt)
{
int bpp = crt ? s->dc_crt_control : s->dc_panel_control;
return 1 << (bpp & 3);
}
/**
* Check the availability of hardware cursor.
* @param crt 0 for PANEL, 1 for CRT.
@ -568,10 +586,10 @@ static inline int is_hwc_enabled(SM501State *state, int crt)
* Get the address which holds cursor pattern data.
* @param crt 0 for PANEL, 1 for CRT.
*/
static inline uint32_t get_hwc_address(SM501State *state, int crt)
static inline uint8_t *get_hwc_address(SM501State *state, int crt)
{
uint32_t addr = crt ? state->dc_crt_hwc_addr : state->dc_panel_hwc_addr;
return (addr & 0x03FFFFF0)/* >> 4*/;
return state->local_mem + (addr & 0x03FFFFF0);
}
/**
@ -597,50 +615,48 @@ static inline uint32_t get_hwc_x(SM501State *state, int crt)
}
/**
* Get the cursor position in x coordinate.
* Get the hardware cursor palette.
* @param crt 0 for PANEL, 1 for CRT.
* @param index 0, 1, 2 or 3 which specifies color of corsor dot.
* @param palette pointer to a [3 * 3] array to store color values in
*/
static inline uint16_t get_hwc_color(SM501State *state, int crt, int index)
static inline void get_hwc_palette(SM501State *state, int crt, uint8_t *palette)
{
uint32_t color_reg = 0;
uint16_t color_565 = 0;
int i;
uint32_t color_reg;
uint16_t rgb565;
if (index == 0) {
return 0;
}
for (i = 0; i < 3; i++) {
if (i + 1 == 3) {
color_reg = crt ? state->dc_crt_hwc_color_3
: state->dc_panel_hwc_color_3;
} else {
color_reg = crt ? state->dc_crt_hwc_color_1_2
: state->dc_panel_hwc_color_1_2;
}
switch (index) {
case 1:
case 2:
color_reg = crt ? state->dc_crt_hwc_color_1_2
: state->dc_panel_hwc_color_1_2;
break;
case 3:
color_reg = crt ? state->dc_crt_hwc_color_3
: state->dc_panel_hwc_color_3;
break;
default:
printf("invalid hw cursor color.\n");
abort();
if (i + 1 == 2) {
rgb565 = (color_reg >> 16) & 0xFFFF;
} else {
rgb565 = color_reg & 0xFFFF;
}
palette[i * 3 + 0] = (rgb565 << 3) & 0xf8; /* red */
palette[i * 3 + 1] = (rgb565 >> 3) & 0xfc; /* green */
palette[i * 3 + 2] = (rgb565 >> 8) & 0xf8; /* blue */
}
switch (index) {
case 1:
case 3:
color_565 = (uint16_t)(color_reg & 0xFFFF);
break;
case 2:
color_565 = (uint16_t)((color_reg >> 16) & 0xFFFF);
break;
}
return color_565;
}
static int within_hwc_y_range(SM501State *state, int y, int crt)
static inline void hwc_invalidate(SM501State *s, int crt)
{
int hwc_y = get_hwc_y(state, crt);
return (hwc_y <= y && y < hwc_y + SM501_HWC_HEIGHT);
int w = get_width(s, crt);
int h = get_height(s, crt);
int bpp = get_bpp(s, crt);
int start = get_hwc_y(s, crt);
int end = MIN(h, start + SM501_HWC_HEIGHT) + 1;
start *= w * bpp;
end *= w * bpp;
memory_region_set_dirty(&s->local_mem_region, start, end - start);
}
static void sm501_2d_operation(SM501State *s)
@ -1021,10 +1037,18 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr,
break;
case SM501_DC_PANEL_HWC_ADDR:
s->dc_panel_hwc_addr = value & 0x8FFFFFF0;
value &= 0x8FFFFFF0;
if (value != s->dc_panel_hwc_addr) {
hwc_invalidate(s, 0);
s->dc_panel_hwc_addr = value;
}
break;
case SM501_DC_PANEL_HWC_LOC:
s->dc_panel_hwc_location = value & 0x0FFF0FFF;
value &= 0x0FFF0FFF;
if (value != s->dc_panel_hwc_location) {
hwc_invalidate(s, 0);
s->dc_panel_hwc_location = value;
}
break;
case SM501_DC_PANEL_HWC_COLOR_1_2:
s->dc_panel_hwc_color_1_2 = value;
@ -1056,10 +1080,18 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr,
break;
case SM501_DC_CRT_HWC_ADDR:
s->dc_crt_hwc_addr = value & 0x8FFFFFF0;
value &= 0x8FFFFFF0;
if (value != s->dc_crt_hwc_addr) {
hwc_invalidate(s, 1);
s->dc_crt_hwc_addr = value;
}
break;
case SM501_DC_CRT_HWC_LOC:
s->dc_crt_hwc_location = value & 0x0FFF0FFF;
value &= 0x0FFF0FFF;
if (value != s->dc_crt_hwc_location) {
hwc_invalidate(s, 1);
s->dc_crt_hwc_location = value;
}
break;
case SM501_DC_CRT_HWC_COLOR_1_2:
s->dc_crt_hwc_color_1_2 = value;
@ -1182,8 +1214,9 @@ static const MemoryRegionOps sm501_2d_engine_ops = {
typedef void draw_line_func(uint8_t *d, const uint8_t *s,
int width, const uint32_t *pal);
typedef void draw_hwc_line_func(SM501State *s, int crt, uint8_t *palette,
int c_y, uint8_t *d, int width);
typedef void draw_hwc_line_func(uint8_t *d, const uint8_t *s,
int width, const uint8_t *palette,
int c_x, int c_y);
#define DEPTH 8
#include "sm501_template.h"
@ -1271,12 +1304,11 @@ static inline int get_depth_index(DisplaySurface *surface)
static void sm501_draw_crt(SM501State *s)
{
DisplaySurface *surface = qemu_console_surface(s->con);
int y;
int width = (s->dc_crt_h_total & 0x00000FFF) + 1;
int height = (s->dc_crt_v_total & 0x00000FFF) + 1;
uint8_t *src = s->local_mem;
int src_bpp = 0;
int y, c_x = 0, c_y = 0;
uint8_t *hwc_src = NULL, *src = s->local_mem;
int width = get_width(s, 1);
int height = get_height(s, 1);
int src_bpp = get_bpp(s, 1);
int dst_bpp = surface_bytes_per_pixel(surface);
uint32_t *palette = (uint32_t *)&s->dc_palette[SM501_DC_CRT_PALETTE -
SM501_DC_PANEL_PALETTE];
@ -1291,17 +1323,14 @@ static void sm501_draw_crt(SM501State *s)
ram_addr_t offset = 0;
/* choose draw_line function */
switch (s->dc_crt_control & 3) {
case SM501_DC_CRT_CONTROL_8BPP:
src_bpp = 1;
switch (src_bpp) {
case 1:
draw_line = draw_line8_funcs[ds_depth_index];
break;
case SM501_DC_CRT_CONTROL_16BPP:
src_bpp = 2;
case 2:
draw_line = draw_line16_funcs[ds_depth_index];
break;
case SM501_DC_CRT_CONTROL_32BPP:
src_bpp = 4;
case 4:
draw_line = draw_line32_funcs[ds_depth_index];
break;
default:
@ -1313,18 +1342,12 @@ static void sm501_draw_crt(SM501State *s)
/* set up to draw hardware cursor */
if (is_hwc_enabled(s, 1)) {
int i;
/* get cursor palette */
for (i = 0; i < 3; i++) {
uint16_t rgb565 = get_hwc_color(s, 1, i + 1);
hwc_palette[i * 3 + 0] = (rgb565 & 0xf800) >> 8; /* red */
hwc_palette[i * 3 + 1] = (rgb565 & 0x07e0) >> 3; /* green */
hwc_palette[i * 3 + 2] = (rgb565 & 0x001f) << 3; /* blue */
}
/* choose cursor draw line function */
draw_hwc_line = draw_hwc_line_funcs[ds_depth_index];
hwc_src = get_hwc_address(s, 1);
c_x = get_hwc_x(s, 1);
c_y = get_hwc_y(s, 1);
get_hwc_palette(s, 1, hwc_palette);
}
/* adjust console size */
@ -1339,14 +1362,16 @@ static void sm501_draw_crt(SM501State *s)
/* draw each line according to conditions */
memory_region_sync_dirty_bitmap(&s->local_mem_region);
for (y = 0; y < height; y++) {
int update_hwc = draw_hwc_line ? within_hwc_y_range(s, y, 1) : 0;
int update = full_update || update_hwc;
int update, update_hwc;
ram_addr_t page0 = offset;
ram_addr_t page1 = offset + width * src_bpp - 1;
/* check if hardware cursor is enabled and we're within its range */
update_hwc = draw_hwc_line && c_y <= y && y < c_y + SM501_HWC_HEIGHT;
update = full_update || update_hwc;
/* check dirty flags for each line */
update = memory_region_get_dirty(&s->local_mem_region, page0,
page1 - page0, DIRTY_MEMORY_VGA);
update |= memory_region_get_dirty(&s->local_mem_region, page0,
page1 - page0, DIRTY_MEMORY_VGA);
/* draw line and change status */
if (update) {
@ -1358,7 +1383,7 @@ static void sm501_draw_crt(SM501State *s)
/* draw hardware cursor */
if (update_hwc) {
draw_hwc_line(s, 1, hwc_palette, y - get_hwc_y(s, 1), d, width);
draw_hwc_line(d, hwc_src, width, hwc_palette, c_x, y - c_y);
}
if (y_start < 0) {

View File

@ -92,29 +92,24 @@ static void glue(draw_line32_, PIXEL_NAME)(
/**
* Draw hardware cursor image on the given line.
*/
static void glue(draw_hwc_line_, PIXEL_NAME)(SM501State *s, int crt,
uint8_t *palette, int c_y, uint8_t *d, int width)
static void glue(draw_hwc_line_, PIXEL_NAME)(uint8_t *d, const uint8_t *s,
int width, const uint8_t *palette, int c_x, int c_y)
{
int x, i;
uint8_t *pixval, bitset = 0;
/* get hardware cursor pattern */
uint32_t cursor_addr = get_hwc_address(s, crt);
assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
cursor_addr += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */
pixval = s->local_mem + cursor_addr;
int i;
uint8_t bitset = 0;
/* get cursor position */
x = get_hwc_x(s, crt);
d += x * BPP;
assert(0 <= c_y && c_y < SM501_HWC_HEIGHT);
s += SM501_HWC_WIDTH * c_y / 4; /* 4 pixels per byte */
d += c_x * BPP;
for (i = 0; i < SM501_HWC_WIDTH && x + i < width; i++) {
for (i = 0; i < SM501_HWC_WIDTH && c_x + i < width; i++) {
uint8_t v;
/* get pixel value */
if (i % 4 == 0) {
bitset = ldub_p(pixval);
pixval++;
bitset = ldub_p(s);
s++;
}
v = bitset & 3;
bitset >>= 2;