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 <peter.maydell@linaro.org>
---
hw/display/cirrus_vga_rop2.h | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/hw/display/cirrus_vga_rop2.h b/hw/display/cirrus_vga_rop2.h
index b208b7348a..8be35ec6e2 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
--
2.43.0
© 2016 - 2026 Red Hat, Inc.