On Mon, 2 Feb 2026, Chad Jablonski wrote:
> Both supported ROPs follow the same memory set dirty logic.
> This consolidates that logic to remove the duplication.
>
> Signed-off-by: Chad Jablonski <chad@jablonski.xyz>
> ---
> hw/display/ati_2d.c | 44 +++++++++++++++++++++++---------------------
> 1 file changed, 23 insertions(+), 21 deletions(-)
>
> diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c
> index cfc7bf9789..5d24d11d7a 100644
> --- a/hw/display/ati_2d.c
> +++ b/hw/display/ati_2d.c
> @@ -43,15 +43,29 @@ static int ati_bpp_from_datatype(ATIVGAState *s)
> }
> }
>
> +static void ati_set_dirty(ATIVGAState *s, const uint8_t *dst_bits,
> + unsigned dst_y, unsigned dst_height, unsigned rop3)
> +{
> + VGACommonState *vga = &s->vga;
> + DisplaySurface *ds = qemu_console_surface(vga->con);
> + unsigned dst_offset = dst_bits - vga->vram_ptr;
Code style suggests an empty line here.
Are you sure dst_offset calculation this way is correct? Also if
s->regs.crtc_offset is not 0 as that seems to be added to dst_bits for
Rage128Pro. This may be replaced with correct value from ctx later but
then you could just keep the original here to make sure it's not broken.
> + DPRINTF("%p %u ds: %p %d %d rop: %x\n",
> + vga->vram_ptr, vga->vbe_start_addr,
> + surface_data(ds), surface_stride(ds), surface_bits_per_pixel(ds),
> + rop3 >> 16);
> + if (dst_bits >= vga->vram_ptr + vga->vbe_start_addr &&
> + dst_bits < vga->vram_ptr + vga->vbe_start_addr +
> + vga->vbe_regs[VBE_DISPI_INDEX_YRES] * vga->vbe_line_offset) {
> + memory_region_set_dirty(&vga->vram, vga->vbe_start_addr +
> + dst_offset + dst_y * surface_stride(ds),
> + dst_height * surface_stride(ds));
> + }
> +}
> +
> void ati_2d_blt(ATIVGAState *s)
> {
> /* FIXME it is probably more complex than this and may need to be */
> /* rewritten but for now as a start just to get some output: */
> - DisplaySurface *ds = qemu_console_surface(s->vga.con);
> - DPRINTF("%p %u ds: %p %d %d rop: %x\n", s->vga.vram_ptr,
> - s->vga.vbe_start_addr, surface_data(ds), surface_stride(ds),
> - surface_bits_per_pixel(ds),
> - (s->regs.dp_mix & GMC_ROP3_MASK) >> 16);
> unsigned dst_x = (s->regs.dp_cntl & DST_X_LEFT_TO_RIGHT ?
> s->regs.dst_x : s->regs.dst_x + 1 - s->regs.dst_width);
> unsigned dst_y = (s->regs.dp_cntl & DST_Y_TOP_TO_BOTTOM ?
> @@ -166,14 +180,6 @@ void ati_2d_blt(ATIVGAState *s)
> memmove(&dst_bits[i], &src_bits[j], s->regs.dst_width * bypp);
> }
> }
> - if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr &&
> - dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr +
> - s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) {
> - memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr +
> - s->regs.dst_offset +
> - dst_y * surface_stride(ds),
> - s->regs.dst_height * surface_stride(ds));
> - }
> break;
> }
> case ROP3_PATCOPY:
> @@ -216,18 +222,14 @@ void ati_2d_blt(ATIVGAState *s)
> }
> }
> }
> - if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr &&
> - dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr +
> - s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) {
> - memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr +
> - s->regs.dst_offset +
> - dst_y * surface_stride(ds),
> - s->regs.dst_height * surface_stride(ds));
> - }
> break;
> }
> default:
> qemu_log_mask(LOG_UNIMP, "Unimplemented ati_2d blt op %x\n",
> (s->regs.dp_mix & GMC_ROP3_MASK) >> 16);
> + return;
> }
> +
> + ati_set_dirty(s, dst_bits, dst_y, s->regs.dst_height,
> + (s->regs.dp_mix & GMC_ROP3_MASK));
> }
Last two parameters (dst_height and rop3) are not needed because they can
be get from s. This is changed later but also removed there so maybe don't
add it here in the first place just use values from state as copied from
the original lines.
Regards,
BALATON Zoltan