Implement flushing the 128-bit HOST_DATA accumulator to VRAM to enable
text rendering in X. Currently supports only the monochrome
foreground/background datatype.

The flush is broken up into two steps. First, expansion of the
monochrome bits to the destination color depth. Then the expanded pixels
are sent to the ati_2d_do_blt one scanline at a time. ati_2d_do_blt then
clips and performs the blit.

Signed-off-by: Chad Jablonski <[email protected]>
---
 hw/display/ati.c      |  4 +-
 hw/display/ati_2d.c   | 85 ++++++++++++++++++++++++++++++++++++++++++-
 hw/display/ati_int.h  |  3 ++
 hw/display/ati_regs.h |  4 ++
 4 files changed, 93 insertions(+), 3 deletions(-)

diff --git a/hw/display/ati.c b/hw/display/ati.c
index e6868ddb8b..aa13c25f59 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -1052,13 +1052,13 @@ 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 >= 4) {
-            qemu_log_mask(LOG_UNIMP, "HOST_DATA flush not yet implemented\n");
+            ati_flush_host_data(s);
             s->host_data.next = 0;
         }
         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 a5dc5ba98e..5a0e918810 100644
--- a/hw/display/ati_2d.c
+++ b/hw/display/ati_2d.c
@@ -274,6 +274,12 @@ void ati_2d_blt(ATIVGAState *s)
     uint8_t *end = s->vga.vram_ptr + s->vga.vram_size;
     ATIBltDst dst;
     ATIBltSrc src;
+    uint32_t src_source = s->regs.dp_mix & DP_SRC_SOURCE;
+
+    if (src_source == DP_SRC_HOST || src_source == DP_SRC_HOST_BYTEALIGN) {
+        /* HOST_DATA sources are handled by ati_flush_host_data() */
+        return;
+    }
 
     setup_2d_blt_dst(s, &dst);
 
@@ -290,7 +296,6 @@ void ati_2d_blt(ATIVGAState *s)
         src.bits += s->regs.crtc_offset & 0x07ffffff;
         src.stride *= dst.bpp;
     }
-
     if (src.x > 0x3fff || src.y > 0x3fff || src.bits >= end
         || src.bits + src.x
          + (src.y + dst.rect.height) * src.stride >= end) {
@@ -300,3 +305,81 @@ void ati_2d_blt(ATIVGAState *s)
 
     ati_2d_do_blt(s, &src, &dst);
 }
+
+void ati_flush_host_data(ATIVGAState *s)
+{
+    ATIBltDst dst, chunk;
+    ATIBltSrc src;
+    uint32_t fg, bg;
+    unsigned bypp, row, col, idx;
+    uint8_t pix_buf[ATI_HOST_DATA_ACC_BITS * sizeof(uint32_t)];
+    uint32_t byte_pix_order = s->regs.dp_datatype & DP_BYTE_PIX_ORDER;
+
+    if ((s->regs.dp_mix & DP_SRC_SOURCE) != DP_SRC_HOST) {
+        qemu_log_mask(LOG_UNIMP, "host_data_blt: only DP_SRC_HOST 
supported\n");
+        return;
+    }
+    if ((s->regs.dp_datatype & DP_SRC_DATATYPE) != SRC_MONO_FRGD_BKGD) {
+        qemu_log_mask(LOG_UNIMP,
+                      "host_data_blt: only SRC_MONO_FRGD_BKGD supported\n");
+        return;
+    }
+
+    setup_2d_blt_dst(s, &dst);
+
+    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;
+    }
+
+    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 byte = 0; byte < 4; byte++) {
+            uint8_t byte_val = s->host_data.acc[word] >> (byte * 8);
+            for (int i = 0; i < 8; i++) {
+                int bit = byte_pix_order ? i : (7 - i);
+                bool is_fg = extract8(byte_val, bit, 1);
+                uint32_t color = is_fg ? fg : bg;
+                stn_he_p(&pix_buf[idx * bypp], bypp, color);
+                idx += 1;
+            }
+        }
+    }
+
+    /* Set up source to point at pix_buf (treated as a single row) */
+    src.bits = pix_buf;
+    src.y = 0;
+    src.stride = ATI_HOST_DATA_ACC_BITS * bypp;
+
+    /* Copy to VRAM one scanline chunk at a time */
+    row = s->host_data.row;
+    col = s->host_data.col;
+    idx = 0;
+    chunk = dst;
+    while (idx < ATI_HOST_DATA_ACC_BITS && row < dst.rect.height) {
+        unsigned pix_in_scanline = MIN(ATI_HOST_DATA_ACC_BITS - idx,
+                                       dst.rect.width - col);
+        src.x = idx;
+        /* Build a rect for this scanline chunk */
+        qemu_rect_init(&chunk.rect,
+                       dst.rect.x + col,
+                       dst.rect.y + row,
+                       pix_in_scanline, 1);
+        ati_2d_do_blt(s, &src, &chunk);
+        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;
+}
diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h
index 38725c57fa..ff7148a71a 100644
--- a/hw/display/ati_int.h
+++ b/hw/display/ati_int.h
@@ -29,6 +29,8 @@
 /* Radeon RV100 (VE) */
 #define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159
 
+#define ATI_HOST_DATA_ACC_BITS 128
+
 #define TYPE_ATI_VGA "ati-vga"
 OBJECT_DECLARE_SIMPLE_TYPE(ATIVGAState, ATI_VGA)
 
@@ -120,5 +122,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 48f15e9b1d..9d08d3e956 100644
--- a/hw/display/ati_regs.h
+++ b/hw/display/ati_regs.h
@@ -397,7 +397,11 @@
 #define DST_32BPP                               0x00000006
 #define DP_DST_DATATYPE                         0x0000000f
 #define DP_BRUSH_DATATYPE                       0x00000f00
+#define SRC_MONO_FRGD_BKGD                      0x00000000
+#define SRC_MONO_FRGD                           0x00010000
+#define SRC_COLOR                               0x00020000
 #define DP_SRC_DATATYPE                         0x00030000
+#define DP_BYTE_PIX_ORDER                       0x40000000
 
 #define BRUSH_SOLIDCOLOR                        0x00000d00
 
-- 
2.51.2


Reply via email to