From: Chad Jablonski <chad@jablonski.xyz>
To: qemu-devel@nongnu.org
Cc: balaton@eik.bme.hu, Chad Jablonski <chad@jablonski.xyz>
Subject: [PATCH v3 11/11] ati-vga: Implement HOST_DATA flush to VRAM
Date: Tue, 18 Nov 2025 10:48:12 -0500 [thread overview]
Message-ID: <20251118154812.57861-12-chad@jablonski.xyz> (raw)
In-Reply-To: <20251118154812.57861-1-chad@jablonski.xyz>
Implement flushing the 128-bit HOST_DATA accumulator to VRAM to enable
text rendering in X. Currently supports only the monochrome
foreground/background datatype with the SRCCOPY ROP.
The flush is broken up into two steps for clarity. First, expansion of the
monochrome bits to the destination color depth. Then the expanded pixels
are clipped and copied into VRAM.
Signed-off-by: Chad Jablonski <chad@jablonski.xyz>
---
hw/display/ati.c | 4 +-
hw/display/ati_2d.c | 127 ++++++++++++++++++++++++++++++++++++++++++
hw/display/ati_int.h | 3 +
hw/display/ati_regs.h | 13 +++++
4 files changed, 145 insertions(+), 2 deletions(-)
diff --git a/hw/display/ati.c b/hw/display/ati.c
index dc302eb6f2..c5892a6305 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -1038,7 +1038,7 @@ static void ati_mm_write(void *opaque, hwaddr addr,
case HOST_DATA7:
s->host_data.acc[s->host_data.next] = data;
if (s->host_data.next >= 3) {
- qemu_log_mask(LOG_UNIMP, "HOST_DATA flush not yet implemented\n");
+ ati_flush_host_data(s);
s->host_data.next = 0;
break;
}
@@ -1046,7 +1046,7 @@ static void ati_mm_write(void *opaque, hwaddr addr,
break;
case HOST_DATA_LAST:
s->host_data.acc[s->host_data.next] = data;
- qemu_log_mask(LOG_UNIMP, "HOST_DATA flush not yet implemented\n");
+ ati_flush_host_data(s);
ati_host_data_reset(&s->host_data);
break;
default:
diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c
index e30a4cd941..2cd5348965 100644
--- a/hw/display/ati_2d.c
+++ b/hw/display/ati_2d.c
@@ -123,6 +123,12 @@ static ATIBlitDest setup_2d_blt_dst(ATIVGAState *s)
void ati_2d_blt(ATIVGAState *s)
{
+ uint32_t src = s->regs.dp_gui_master_cntl & GMC_SRC_SOURCE_MASK;
+ if (src == GMC_SRC_SOURCE_HOST_DATA) {
+ /* HOST_DATA blits are handled separately by ati_flush_host_data() */
+ return;
+ }
+
/* 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);
@@ -299,3 +305,124 @@ void ati_2d_blt(ATIVGAState *s)
(s->regs.dp_mix & GMC_ROP3_MASK) >> 16);
}
}
+
+void ati_flush_host_data(ATIVGAState *s)
+{
+ DisplaySurface *ds;
+ unsigned src = s->regs.dp_gui_master_cntl & GMC_SRC_SOURCE_MASK;
+ unsigned src_datatype = s->regs.dp_gui_master_cntl & GMC_SRC_DATATYPE_MASK;
+ unsigned rop = s->regs.dp_mix & GMC_ROP3_MASK;
+ ATIBlitDest dst;
+ bool lsb_to_msb;
+ uint32_t fg, bg;
+ unsigned bypp, row, col, idx;
+ uint8_t pix_buf[ATI_HOST_DATA_ACC_BITS * sizeof(uint32_t)];
+
+ if (src != GMC_SRC_SOURCE_HOST_DATA) {
+ qemu_log_mask(LOG_UNIMP,
+ "host_data_blt: only GMC_SRC_SOURCE_HOST_DATA "
+ "supported\n");
+ return;
+ }
+
+ if (src_datatype != GMC_SRC_DATATYPE_MONO_FRGD_BKGD) {
+ qemu_log_mask(LOG_UNIMP,
+ "host_data_blt: only GMC_SRC_DATATYPE_MONO_FRGD_BKGD "
+ "supported\n");
+ return;
+ }
+
+ if (rop != ROP3_SRCCOPY) {
+ qemu_log_mask(LOG_UNIMP,
+ "host_data_blt: only ROP3_SRCCOPY supported. rop: %x\n",
+ rop);
+ return;
+ }
+
+ dst = setup_2d_blt_dst(s);
+ if (!dst.valid) {
+ return;
+ }
+
+ if (!dst.left_to_right || !dst.top_to_bottom) {
+ qemu_log_mask(LOG_UNIMP, "host_data_blt: only L->R, T->B supported\n");
+ return;
+ }
+
+ lsb_to_msb = s->regs.dp_gui_master_cntl & GMC_BYTE_ORDER_LSB_TO_MSB;
+ fg = s->regs.dp_src_frgd_clr;
+ bg = s->regs.dp_src_bkgd_clr;
+ bypp = dst.bpp / 8;
+
+ /* Expand monochrome bits to color pixels */
+ idx = 0;
+ for (int word = 0; word < 4; word++) {
+ for (int i = 0; i < 32; i++) {
+ int bit = lsb_to_msb ? i : (31 - i);
+ bool is_fg = extract32(s->host_data.acc[word], bit, 1);
+ uint32_t color = is_fg ? fg : bg;
+ stn_he_p(&pix_buf[idx * bypp], bypp, color);
+ idx += 1;
+ }
+ }
+
+ /* Copy to VRAM one scanline at a time */
+ row = s->host_data.row;
+ col = s->host_data.col;
+ idx = 0;
+ while (idx < ATI_HOST_DATA_ACC_BITS && row < dst.rect.height) {
+ uint8_t *vram_dst;
+ unsigned start_col, end_col, vis_row, num_pix, pix_idx;
+ unsigned pix_in_scanline = MIN(ATI_HOST_DATA_ACC_BITS -
+ idx, dst.rect.width - col);
+
+ /* Row-based clipping */
+ if (row < dst.src_top_offset ||
+ row >= dst.src_top_offset + dst.visible.height) {
+ goto skip_pix;
+ }
+
+ /* Column-based clipping */
+ start_col = MAX(col, dst.src_left_offset);
+ end_col = MIN(col + pix_in_scanline,
+ dst.src_left_offset + dst.visible.width);
+ if (end_col <= start_col) {
+ goto skip_pix;
+ }
+
+ /* Copy expanded bits/pixels to VRAM */
+ vis_row = row - dst.src_top_offset;
+ num_pix = end_col - start_col;
+ vram_dst = dst.bits +
+ (dst.visible.y + vis_row) * dst.stride +
+ (dst.visible.x + (start_col - dst.src_left_offset)) * bypp;
+
+ pix_idx = (idx + (start_col - col)) * bypp;
+ memcpy(vram_dst, &pix_buf[pix_idx], num_pix * bypp);
+
+ skip_pix:
+ idx += pix_in_scanline;
+ col += pix_in_scanline;
+ if (col >= dst.rect.width) {
+ col = 0;
+ row += 1;
+ }
+ }
+ /* Track state of the overall blit for use by the next flush */
+ s->host_data.row = row;
+ s->host_data.col = col;
+
+ /*
+ * TODO: This is setting the entire blit region to dirty.
+ * We maybe just need this tiny section?
+ */
+ ds = qemu_console_surface(s->vga.con);
+ 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.visible.y * surface_stride(ds),
+ dst.visible.height * surface_stride(ds));
+ }
+}
diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h
index b9142ce6d8..f38f6a43d7 100644
--- a/hw/display/ati_int.h
+++ b/hw/display/ati_int.h
@@ -34,6 +34,8 @@
#define ATI_RAGE128_LINEAR_APER_SIZE (64 * MiB)
#define ATI_R100_LINEAR_APER_SIZE (128 * MiB)
+#define ATI_HOST_DATA_ACC_BITS 128
+
#define TYPE_ATI_VGA "ati-vga"
OBJECT_DECLARE_SIMPLE_TYPE(ATIVGAState, ATI_VGA)
@@ -126,5 +128,6 @@ struct ATIVGAState {
const char *ati_reg_name(int num);
void ati_2d_blt(ATIVGAState *s);
+void ati_flush_host_data(ATIVGAState *s);
#endif /* ATI_INT_H */
diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h
index 9eb68fbec6..8f3335817f 100644
--- a/hw/display/ati_regs.h
+++ b/hw/display/ati_regs.h
@@ -404,6 +404,7 @@
#define GMC_BRUSH_SOLIDCOLOR 0x000000d0
#define GMC_SRC_DSTCOLOR 0x00003000
#define GMC_BYTE_ORDER_MSB_TO_LSB 0x00000000
+#define GMC_BYTE_ORDER_LSB_TO_MSB 0x00004000
#define GMC_DP_SRC_RECT 0x02000000
#define GMC_3D_FCN_EN_CLR 0x00000000
#define GMC_AUX_CLIP_CLEAR 0x20000000
@@ -431,6 +432,18 @@
#define GMC_DST_CLIP_DEFAULT 0x00000000
#define GMC_DST_CLIP_LEAVE_ALONE 0x00000008
+/* DP_GUI_MASTER_CNTL DP_SRC_DATATYPE named constants */
+#define GMC_SRC_DATATYPE_MASK 0x00003000
+#define GMC_SRC_DATATYPE_MONO_FRGD_BKGD 0x00000000
+#define GMC_SRC_DATATYPE_MONO_FRGD 0x00001000
+#define GMC_SRC_DATATYPE_COLOR 0x00003000
+
+/* DP_GUI_MASTER_CNTL DP_SRC_SOURCE named constants */
+#define GMC_SRC_SOURCE_MASK 0x07000000
+#define GMC_SRC_SOURCE_MEMORY 0x02000000
+#define GMC_SRC_SOURCE_HOST_DATA 0x03000000
+#define GMC_SRC_SOURCE_HOST_DATA_ALIGNED 0x04000000
+
/* DP_GUI_MASTER_CNTL ROP3 named constants */
#define GMC_ROP3_MASK 0x00ff0000
#define ROP3_BLACKNESS 0x00000000
--
2.51.0
next prev parent reply other threads:[~2025-11-18 15:50 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-18 15:48 [PATCH v3 00/11] ati-vga: Implement HOST_DATA transfers to Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 01/11] ati-vga: Fix DST_PITCH and SRC_PITCH reads Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 02/11] ati-vga: Add scissor clipping register support Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 03/11] ati-vga: Implement foreground and background color register writes Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 04/11] ati-vga: Latch src and dst pitch and offset on master_cntl default Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 05/11] ati-vga: Fix DP_GUI_MASTER_CNTL register mask Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 06/11] ati-vga: Create dst and sc rectangle helpers Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 07/11] ati-vga: Implement scissor rectangle clipping for 2D operations Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 08/11] ati-vga: Create 2d_blt destination setup helper Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 09/11] ati-vga: Refactor ati_2d_blt to use dst " Chad Jablonski
2025-11-18 15:48 ` [PATCH v3 10/11] ati-vga: Implement HOST_DATA register writes Chad Jablonski
2025-11-18 15:48 ` Chad Jablonski [this message]
2025-12-11 2:33 ` [PATCH v3 00/11] ati-vga: Implement HOST_DATA transfers to Chad Jablonski
2025-12-11 11:54 ` BALATON Zoltan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251118154812.57861-12-chad@jablonski.xyz \
--to=chad@jablonski.xyz \
--cc=balaton@eik.bme.hu \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).