For the "color expansion" subtype of raster operations, the source
pixel format is a monochrome bitmap, and the destination can be any
of 8, 16, 24 or 32bpp.
For these pattern operations, 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.
In commit ad81218e40e27 ("depth=24 write mask fix (Volker Ruppert)")
in 2005, we updated the code to (attempt to) handle the 5-bit mask
case. However, we don't do the right thing when the 5-bit mask
indicates that we need to skip more than 8 bits of the input bitmap:
we will right-shift the 0x80 constant completely off the right hand
side, and will be off-by-one for all the source bitmap loads.
Fix this by calculating the whole number of input bytes we need to
skip and the residual number of bits. In the 8/16/32bpp case the
bytes to skip is always zero.
Cc: qemu-stable@nongnu.org
Fixes: ad81218e40e27 ("depth=24 write mask fix (Volker Ruppert)")
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
hw/display/cirrus_vga_rop2.h | 31 +++++++++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/hw/display/cirrus_vga_rop2.h b/hw/display/cirrus_vga_rop2.h
index 8be35ec6e2..33f9b3b613 100644
--- a/hw/display/cirrus_vga_rop2.h
+++ b/hw/display/cirrus_vga_rop2.h
@@ -108,12 +108,34 @@ glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH)
unsigned int col;
unsigned bitmask;
unsigned index;
+
+ /*
+ * Raster ops where the source is a monochrome bitmap with
+ * color expansion to 8/16/24/32bpp destination.
+ */
+
#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. We want to track the number of whole bytes
+ * to skip in the source (always either 0 or 1) and the number
+ * of bits within the byte to skip.
+ */
int dstskipleft = s->vga.gr[0x2f] & 0x1f;
- int srcskipleft = dstskipleft / 3;
+ int srcskipleftbits = (dstskipleft / 3) & 0x7;
+ int srcskipleftbytes = (dstskipleft / 3) >> 3;
#else
- int srcskipleft = s->vga.gr[0x2f] & 0x07;
- int dstskipleft = srcskipleft * (DEPTH / 8);
+ /*
+ * In all other modes, GR2F bits [2:0] are a count of how many
+ * destination pixels to suppress for each scanline, which is our
+ * srcskipleftbits. We get dstskipleft, the number of bytes to
+ * skip, by multiplying this by the bytes-per-pixel. In these
+ * modes we never need to skip an entire source byte.
+ */
+ int srcskipleftbits = s->vga.gr[0x2f] & 0x07;
+ int srcskipleftbytes = 0;
+ int dstskipleft = srcskipleftbits * (DEPTH / 8);
#endif
if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
@@ -125,7 +147,8 @@ glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH)
}
for(y = 0; y < bltheight; y++) {
- bitmask = 0x80 >> srcskipleft;
+ bitmask = 0x80 >> srcskipleftbits;
+ srcaddr += srcskipleftbytes;
bits = cirrus_src(s, srcaddr++) ^ bits_xor;
addr = dstaddr + dstskipleft;
for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) {
--
2.43.0
© 2016 - 2026 Red Hat, Inc.