From d634c883ca07c28da2cb84c019659694f05d8b3a Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 21 Jun 2020 13:12:38 +0200 Subject: [PATCH] ati-vga: Support unaligned access to hardware cursor registers This fixes horizontal mouse movement and pointer color with MacOS that writes these registers with access size less than 4 so previously only the last portion of access was effective overwriting previous partial writes. Signed-off-by: BALATON Zoltan Message-id: ba1d5ba97f246e8807f86f1243c2bdc6497dc8f2.1592737958.git.balaton@eik.bme.hu Signed-off-by: Gerd Hoffmann --- hw/display/ati.c | 85 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/hw/display/ati.c b/hw/display/ati.c index 7216f7e08f..245130d52f 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -389,22 +389,28 @@ static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) case 0xf00 ... 0xfff: val = pci_default_read_config(&s->dev, addr - 0xf00, size); break; - case CUR_OFFSET: - val = s->regs.cur_offset; + case CUR_OFFSET ... CUR_OFFSET + 3: + val = ati_reg_read_offs(s->regs.cur_offset, addr - CUR_OFFSET, size); break; - case CUR_HORZ_VERT_POSN: - val = s->regs.cur_hv_pos; - val |= s->regs.cur_offset & BIT(31); + case CUR_HORZ_VERT_POSN ... CUR_HORZ_VERT_POSN + 3: + val = ati_reg_read_offs(s->regs.cur_hv_pos, + addr - CUR_HORZ_VERT_POSN, size); + if (addr + size > CUR_HORZ_VERT_POSN + 3) { + val |= (s->regs.cur_offset & BIT(31)) >> (4 - size); + } break; - case CUR_HORZ_VERT_OFF: - val = s->regs.cur_hv_offs; - val |= s->regs.cur_offset & BIT(31); + case CUR_HORZ_VERT_OFF ... CUR_HORZ_VERT_OFF + 3: + val = ati_reg_read_offs(s->regs.cur_hv_offs, + addr - CUR_HORZ_VERT_OFF, size); + if (addr + size > CUR_HORZ_VERT_OFF + 3) { + val |= (s->regs.cur_offset & BIT(31)) >> (4 - size); + } break; - case CUR_CLR0: - val = s->regs.cur_color0; + case CUR_CLR0 ... CUR_CLR0 + 3: + val = ati_reg_read_offs(s->regs.cur_color0, addr - CUR_CLR0, size); break; - case CUR_CLR1: - val = s->regs.cur_color1; + case CUR_CLR1 ... CUR_CLR1 + 3: + val = ati_reg_read_offs(s->regs.cur_color1, addr - CUR_CLR1, size); break; case DST_OFFSET: val = s->regs.dst_offset; @@ -679,48 +685,71 @@ static void ati_mm_write(void *opaque, hwaddr addr, case 0xf00 ... 0xfff: /* read-only copy of PCI config space so ignore writes */ break; - case CUR_OFFSET: - if (s->regs.cur_offset != (data & 0x87fffff0)) { - s->regs.cur_offset = data & 0x87fffff0; + case CUR_OFFSET ... CUR_OFFSET + 3: + { + uint32_t t = s->regs.cur_offset; + + ati_reg_write_offs(&t, addr - CUR_OFFSET, data, size); + t &= 0x87fffff0; + if (s->regs.cur_offset != t) { + s->regs.cur_offset = t; ati_cursor_define(s); } break; - case CUR_HORZ_VERT_POSN: - s->regs.cur_hv_pos = data & 0x3fff0fff; - if (data & BIT(31)) { - s->regs.cur_offset |= data & BIT(31); + } + case CUR_HORZ_VERT_POSN ... CUR_HORZ_VERT_POSN + 3: + { + uint32_t t = s->regs.cur_hv_pos | (s->regs.cur_offset & BIT(31)); + + ati_reg_write_offs(&t, addr - CUR_HORZ_VERT_POSN, data, size); + s->regs.cur_hv_pos = t & 0x3fff0fff; + if (t & BIT(31)) { + s->regs.cur_offset |= t & BIT(31); } else if (s->regs.cur_offset & BIT(31)) { s->regs.cur_offset &= ~BIT(31); ati_cursor_define(s); } if (!s->cursor_guest_mode && - (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(data & BIT(31))) { + (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(t & BIT(31))) { dpy_mouse_set(s->vga.con, s->regs.cur_hv_pos >> 16, s->regs.cur_hv_pos & 0xffff, 1); } break; + } case CUR_HORZ_VERT_OFF: - s->regs.cur_hv_offs = data & 0x3f003f; - if (data & BIT(31)) { - s->regs.cur_offset |= data & BIT(31); + { + uint32_t t = s->regs.cur_hv_offs | (s->regs.cur_offset & BIT(31)); + + ati_reg_write_offs(&t, addr - CUR_HORZ_VERT_OFF, data, size); + s->regs.cur_hv_offs = t & 0x3f003f; + if (t & BIT(31)) { + s->regs.cur_offset |= t & BIT(31); } else if (s->regs.cur_offset & BIT(31)) { s->regs.cur_offset &= ~BIT(31); ati_cursor_define(s); } break; - case CUR_CLR0: - if (s->regs.cur_color0 != (data & 0xffffff)) { - s->regs.cur_color0 = data & 0xffffff; + } + case CUR_CLR0 ... CUR_CLR0 + 3: + { + uint32_t t = s->regs.cur_color0; + + ati_reg_write_offs(&t, addr - CUR_CLR0, data, size); + t &= 0xffffff; + if (s->regs.cur_color0 != t) { + s->regs.cur_color0 = t; ati_cursor_define(s); } break; - case CUR_CLR1: + } + case CUR_CLR1 ... CUR_CLR1 + 3: /* * Update cursor unconditionally here because some clients set up * other registers before actually writing cursor data to memory at * offset so we would miss cursor change unless always updating here */ - s->regs.cur_color1 = data & 0xffffff; + ati_reg_write_offs(&s->regs.cur_color1, addr - CUR_CLR1, data, size); + s->regs.cur_color1 &= 0xffffff; ati_cursor_define(s); break; case DST_OFFSET: