From: Peter Maydell Date: Fri, 10 Apr 2026 18:32:48 +0000 (+0100) Subject: hw/display/cirrus_vga: Fix packed-24 color-expansion transparent pattern fills X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aefeecb413a8e404ecb6d210cc32d60da176a336;p=thirdparty%2Fqemu.git hw/display/cirrus_vga: Fix packed-24 color-expansion transparent pattern fills The Cirrus Logic VGA card has "pattern fill" blit modes where it repeatedly copies an 8x8 source pattern to the display. For the "color expansion" subtype of these, the source pixel format is an 8x8 monochrome bitmap, and the destination can be any of 8, 16, 24 or 32bpp. We implemented these wrong for the 24bpp case, in a way that results in a complaint from the undefined-behavior sanitizer about a shift by a negative value. For these pattern fills, the GR2F register includes a field which specifies how much to skip at the start of each scanline. In the 8, 16 and 32 bit cases, this field is 3 bits and is a count of pixels to skip. We get this case right. However, for the 24 bit case, the field is 5 bits and is a count of destination bytes to skip. We tried to add support for 24-bits in commit ad81218e40e27 ("depth=24 write mask fix (Volker Ruppert)") in 2005. However we got this wrong, because when we need to skip, for example, 30 bytes in the destination, this is 10 input pixels but the whole pattern is only 8 pixels wide, and we ended up with a negative bitpos for the first bit to use in the pattern. Fix the bug by masking srcskipleft in the 24-bit case so that it correctly gives the first pixel to use in the pattern even if we skip so many pixels that we have wrapped around to what would have been the second copy of the pattern to the destination. This patch was produced based on the information in the CL-GD5446 Technical Reference Manual, specifically sections 5.8 "GR2F: BLT Destination Left-Side Clipping" and 9.4.8 "Pattern Fills". Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3377 Fixes: ad81218e40e27 ("depth=24 write mask fix (Volker Ruppert)") Signed-off-by: Peter Maydell Reviewed-by: Junjie Cao Tested-by: Junjie Cao Message-ID: <20260410183249.4046456-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- diff --git a/hw/display/cirrus_vga_rop2.h b/hw/display/cirrus_vga_rop2.h index b208b7348ad..8be35ec6e20 100644 --- a/hw/display/cirrus_vga_rop2.h +++ b/hw/display/cirrus_vga_rop2.h @@ -191,10 +191,29 @@ glue(glue(glue(cirrus_colorexpand_pattern_transp_, ROP_NAME), _),DEPTH) int x, y, bitpos, pattern_y; unsigned int bits, bits_xor; unsigned int col; + + /* + * Copy from an 8x8 monochrome pattern with color expansion. + */ + #if DEPTH == 24 + /* + * For packed-24 modes, GR2F bits [4:0] are a count of destination + * bytes to be suppressed for each scanline, which we keep in + * dstskipleft. Our srcskipleft is the number of pixels to skip + * within the 8x8 source pattern to match up with that number + * of suppressed bytes. As the pattern repeats every 8 bits we + * take the number of pixels mod 8. + */ int dstskipleft = s->vga.gr[0x2f] & 0x1f; - int srcskipleft = dstskipleft / 3; + int srcskipleft = (dstskipleft / 3) & 0x7; #else + /* + * In all other modes, GR2F bits [2:0] are a count of how many + * destination pixels to suppress for each scanline, which is our + * srcskipleft. We get dstskipleft, the number of bytes to skip, + * by multiplying this by the bytes-per-pixel. + */ int srcskipleft = s->vga.gr[0x2f] & 0x07; int dstskipleft = srcskipleft * (DEPTH / 8); #endif