From: Paolo Bonzini <pbonzini@redhat.com>
To: qemu-devel@nongnu.org
Cc: kraxel@redhat.com
Subject: [PATCH 4/8] vga: implement horizontal pel panning in graphics modes
Date: Sun, 31 Dec 2023 10:39:14 +0100 [thread overview]
Message-ID: <20231231093918.239549-5-pbonzini@redhat.com> (raw)
In-Reply-To: <20231231093918.239549-1-pbonzini@redhat.com>
This implements smooth scrolling, as used for example by Commander Keen
and Second Reality.
Unfortunately, this is not enough to avoid tearing in Commander Keen,
because sometimes the wrong start address is used for a frame.
On real EGA, the panning register is sampled on every line, while
the display start is latched for the next frame at the start of the
vertical retrace. On real VGA, the panning register is also latched,
but at the end of the vertical retrace. It looks like Keen exploits
this by only waiting for horizontal retrace when setting the display
start, but implementing it breaks the 256-color Keen games...
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
hw/display/cirrus_vga.c | 4 ++
hw/display/vga-helpers.h | 100 ++++++++++++++++++++++++++++-----------
hw/display/vga.c | 36 ++++++++++++--
hw/display/vga_int.h | 3 ++
4 files changed, 111 insertions(+), 32 deletions(-)
diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c
index e6d2581d4eb..849eff43898 100644
--- a/hw/display/cirrus_vga.c
+++ b/hw/display/cirrus_vga.c
@@ -43,6 +43,7 @@
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "ui/pixel_ops.h"
+#include "vga_regs.h"
#include "cirrus_vga_internal.h"
#include "qom/object.h"
#include "ui/console.h"
@@ -1121,6 +1122,9 @@ static void cirrus_get_params(VGACommonState *s1,
params->line_compare = s->vga.cr[0x18] |
((s->vga.cr[0x07] & 0x10) << 4) |
((s->vga.cr[0x09] & 0x40) << 3);
+
+ params->hpel = s->vga.ar[VGA_ATC_PEL];
+ params->hpel_split = s->vga.ar[VGA_ATC_MODE] & 0x20;
}
static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s)
diff --git a/hw/display/vga-helpers.h b/hw/display/vga-helpers.h
index 83b9a15604a..29933562c45 100644
--- a/hw/display/vga-helpers.h
+++ b/hw/display/vga-helpers.h
@@ -98,14 +98,19 @@ static void vga_draw_glyph9(uint8_t *d, int linesize,
/*
* 4 color mode
*/
-static void vga_draw_line2(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line2(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
uint32_t plane_mask, *palette, data, v;
int x;
palette = vga->last_palette;
plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
+ hpel &= 7;
+ if (hpel) {
+ width += 8;
+ d = vga->panning_buf;
+ }
width >>= 3;
for(x = 0; x < width; x++) {
data = vga_read_dword_le(vga, addr & (VGA_VRAM_SIZE - 1));
@@ -126,6 +131,7 @@ static void vga_draw_line2(VGACommonState *vga, uint8_t *d,
d += 32;
addr += 4;
}
+ return hpel ? vga->panning_buf + 4 * hpel : NULL;
}
#define PUT_PIXEL2(d, n, v) \
@@ -134,14 +140,19 @@ static void vga_draw_line2(VGACommonState *vga, uint8_t *d,
/*
* 4 color mode, dup2 horizontal
*/
-static void vga_draw_line2d2(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line2d2(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
uint32_t plane_mask, *palette, data, v;
int x;
palette = vga->last_palette;
plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
+ hpel &= 7;
+ if (hpel) {
+ width += 8;
+ d = vga->panning_buf;
+ }
width >>= 3;
for(x = 0; x < width; x++) {
data = vga_read_dword_le(vga, addr & (VGA_VRAM_SIZE - 1));
@@ -162,19 +173,25 @@ static void vga_draw_line2d2(VGACommonState *vga, uint8_t *d,
d += 64;
addr += 4;
}
+ return hpel ? vga->panning_buf + 8 * hpel : NULL;
}
/*
* 16 color mode
*/
-static void vga_draw_line4(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line4(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
uint32_t plane_mask, data, v, *palette;
int x;
palette = vga->last_palette;
plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
+ hpel &= 7;
+ if (hpel) {
+ width += 8;
+ d = vga->panning_buf;
+ }
width >>= 3;
for(x = 0; x < width; x++) {
data = vga_read_dword_le(vga, addr & (VGA_VRAM_SIZE - 1));
@@ -194,19 +211,25 @@ static void vga_draw_line4(VGACommonState *vga, uint8_t *d,
d += 32;
addr += 4;
}
+ return hpel ? vga->panning_buf + 4 * hpel : NULL;
}
/*
* 16 color mode, dup2 horizontal
*/
-static void vga_draw_line4d2(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line4d2(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
uint32_t plane_mask, data, v, *palette;
int x;
palette = vga->last_palette;
plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf];
+ hpel &= 7;
+ if (hpel) {
+ width += 8;
+ d = vga->panning_buf;
+ }
width >>= 3;
for(x = 0; x < width; x++) {
data = vga_read_dword_le(vga, addr & (VGA_VRAM_SIZE - 1));
@@ -226,6 +249,7 @@ static void vga_draw_line4d2(VGACommonState *vga, uint8_t *d,
d += 64;
addr += 4;
}
+ return hpel ? vga->panning_buf + 8 * hpel : NULL;
}
/*
@@ -233,13 +257,18 @@ static void vga_draw_line4d2(VGACommonState *vga, uint8_t *d,
*
* XXX: add plane_mask support (never used in standard VGA modes)
*/
-static void vga_draw_line8d2(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line8d2(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
uint32_t *palette;
int x;
palette = vga->last_palette;
+ hpel = (hpel >> 1) & 3;
+ if (hpel) {
+ width += 8;
+ d = vga->panning_buf;
+ }
width >>= 3;
for(x = 0; x < width; x++) {
addr &= VGA_VRAM_SIZE - 1;
@@ -250,6 +279,7 @@ static void vga_draw_line8d2(VGACommonState *vga, uint8_t *d,
d += 32;
addr += 4;
}
+ return hpel ? vga->panning_buf + 8 * hpel : NULL;
}
/*
@@ -257,13 +287,18 @@ static void vga_draw_line8d2(VGACommonState *vga, uint8_t *d,
*
* XXX: add plane_mask support (never used in standard VGA modes)
*/
-static void vga_draw_line8(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line8(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
uint32_t *palette;
int x;
palette = vga->last_palette;
+ hpel = (hpel >> 1) & 3;
+ if (hpel) {
+ width += 8;
+ d = vga->panning_buf;
+ }
width >>= 3;
for(x = 0; x < width; x++) {
((uint32_t *)d)[0] = palette[vga_read_byte(vga, addr + 0)];
@@ -277,13 +312,14 @@ static void vga_draw_line8(VGACommonState *vga, uint8_t *d,
d += 32;
addr += 8;
}
+ return hpel ? vga->panning_buf + 4 * hpel : NULL;
}
/*
* 15 bit color
*/
-static void vga_draw_line15_le(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line15_le(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
int w;
uint32_t v, r, g, b;
@@ -298,10 +334,11 @@ static void vga_draw_line15_le(VGACommonState *vga, uint8_t *d,
addr += 2;
d += 4;
} while (--w != 0);
+ return NULL;
}
-static void vga_draw_line15_be(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line15_be(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
int w;
uint32_t v, r, g, b;
@@ -316,13 +353,14 @@ static void vga_draw_line15_be(VGACommonState *vga, uint8_t *d,
addr += 2;
d += 4;
} while (--w != 0);
+ return NULL;
}
/*
* 16 bit color
*/
-static void vga_draw_line16_le(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line16_le(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
int w;
uint32_t v, r, g, b;
@@ -337,10 +375,11 @@ static void vga_draw_line16_le(VGACommonState *vga, uint8_t *d,
addr += 2;
d += 4;
} while (--w != 0);
+ return NULL;
}
-static void vga_draw_line16_be(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line16_be(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
int w;
uint32_t v, r, g, b;
@@ -355,13 +394,14 @@ static void vga_draw_line16_be(VGACommonState *vga, uint8_t *d,
addr += 2;
d += 4;
} while (--w != 0);
+ return NULL;
}
/*
* 24 bit color
*/
-static void vga_draw_line24_le(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line24_le(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
int w;
uint32_t r, g, b;
@@ -375,10 +415,11 @@ static void vga_draw_line24_le(VGACommonState *vga, uint8_t *d,
addr += 3;
d += 4;
} while (--w != 0);
+ return NULL;
}
-static void vga_draw_line24_be(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line24_be(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
int w;
uint32_t r, g, b;
@@ -392,13 +433,14 @@ static void vga_draw_line24_be(VGACommonState *vga, uint8_t *d,
addr += 3;
d += 4;
} while (--w != 0);
+ return NULL;
}
/*
* 32 bit color
*/
-static void vga_draw_line32_le(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line32_le(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
int w;
uint32_t r, g, b;
@@ -412,10 +454,11 @@ static void vga_draw_line32_le(VGACommonState *vga, uint8_t *d,
addr += 4;
d += 4;
} while (--w != 0);
+ return NULL;
}
-static void vga_draw_line32_be(VGACommonState *vga, uint8_t *d,
- uint32_t addr, int width)
+static void *vga_draw_line32_be(VGACommonState *vga, uint8_t *d,
+ uint32_t addr, int width, int hpel)
{
int w;
uint32_t r, g, b;
@@ -429,4 +472,5 @@ static void vga_draw_line32_be(VGACommonState *vga, uint8_t *d,
addr += 4;
d += 4;
} while (--w != 0);
+ return NULL;
}
diff --git a/hw/display/vga.c b/hw/display/vga.c
index b1660bdde67..2467f3f6c65 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -50,6 +50,13 @@ bool have_vga = true;
/* Address mask for non-VESA modes. */
#define VGA_VRAM_SIZE 262144
+/* This value corresponds to a shift of zero pixels
+ * in 9-dot text mode. In other modes, bit 3 is undefined;
+ * we just ignore it, so that 8 corresponds to zero pixels
+ * in all modes.
+ */
+#define VGA_HPEL_NEUTRAL 8
+
/*
* Video Graphics Array (VGA)
*
@@ -1001,8 +1008,8 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
}
}
-typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d,
- uint32_t srcaddr, int width);
+typedef void *vga_draw_line_func(VGACommonState *s1, uint8_t *d,
+ uint32_t srcaddr, int width, int hpel);
#include "vga-access.h"
#include "vga-helpers.h"
@@ -1069,6 +1076,8 @@ static void vga_get_params(VGACommonState *s,
params->line_offset = s->vbe_line_offset;
params->start_addr = s->vbe_start_addr;
params->line_compare = 65535;
+ params->hpel = VGA_HPEL_NEUTRAL;
+ params->hpel_split = false;
} else {
/* compute line_offset in bytes */
params->line_offset = s->cr[VGA_CRTC_OFFSET] << 3;
@@ -1081,6 +1090,9 @@ static void vga_get_params(VGACommonState *s,
params->line_compare = s->cr[VGA_CRTC_LINE_COMPARE] |
((s->cr[VGA_CRTC_OVERFLOW] & 0x10) << 4) |
((s->cr[VGA_CRTC_MAX_SCAN] & 0x40) << 3);
+
+ params->hpel = s->ar[VGA_ATC_PEL];
+ params->hpel_split = s->ar[VGA_ATC_MODE] & 0x20;
}
}
@@ -1452,6 +1464,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
ram_addr_t page0, page1, region_start, region_end;
DirtyBitmapSnapshot *snap = NULL;
int disp_width, multi_scan, multi_run;
+ int hpel;
uint8_t *d;
uint32_t v, addr1, addr;
vga_draw_line_func *vga_draw_line = NULL;
@@ -1551,6 +1564,9 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
s->last_line_offset = s->params.line_offset;
s->last_depth = depth;
s->last_byteswap = byteswap;
+ /* 16 extra pixels are needed for double-width planar modes. */
+ s->panning_buf = g_realloc(s->panning_buf,
+ (disp_width + 16) * sizeof(uint32_t));
full_update = 1;
}
if (surface_data(surface) != s->vram_ptr + (s->params.start_addr * 4)
@@ -1630,8 +1646,12 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE],
s->params.line_compare, sr(s, VGA_SEQ_CLOCK_MODE));
#endif
+ hpel = bits <= 8 ? s->params.hpel : 0;
addr1 = (s->params.start_addr * 4);
bwidth = DIV_ROUND_UP(width * bits, 8);
+ if (hpel) {
+ bwidth += 4;
+ }
y_start = -1;
d = surface_data(surface);
linesize = surface_stride(surface);
@@ -1679,7 +1699,11 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
if (y_start < 0)
y_start = y;
if (!(is_buffer_shared(surface))) {
- vga_draw_line(s, d, addr, width);
+ uint8_t *p;
+ p = vga_draw_line(s, d, addr, width, hpel);
+ if (p) {
+ memcpy(d, p, disp_width * sizeof(uint32_t));
+ }
if (s->cursor_draw_line)
s->cursor_draw_line(s, d, y);
}
@@ -1701,8 +1725,12 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
multi_run--;
}
/* line compare acts on the displayed lines */
- if (y == s->params.line_compare)
+ if (y == s->params.line_compare) {
+ if (s->params.hpel_split) {
+ hpel = VGA_HPEL_NEUTRAL;
+ }
addr1 = 0;
+ }
d += linesize;
}
if (y_start >= 0) {
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index 6be61e04284..876a1d3697b 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -60,6 +60,8 @@ typedef struct VGADisplayParams {
uint32_t line_offset;
uint32_t start_addr;
uint32_t line_compare;
+ uint8_t hpel;
+ bool hpel_split;
} VGADisplayParams;
typedef struct VGACommonState {
@@ -111,6 +113,7 @@ typedef struct VGACommonState {
/* display refresh support */
QemuConsole *con;
uint32_t font_offsets[2];
+ uint8_t *panning_buf;
int graphic_mode;
uint8_t shift_control;
uint8_t double_scan;
--
2.43.0
next prev parent reply other threads:[~2023-12-31 9:41 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-12-31 9:39 [PATCH 0/8] vga: improve emulation fidelity Paolo Bonzini
2023-12-31 9:39 ` [PATCH 1/8] vga: remove unused macros Paolo Bonzini
2023-12-31 16:01 ` BALATON Zoltan
2024-01-02 15:13 ` Paolo Bonzini
2023-12-31 9:39 ` [PATCH 2/8] vga: introduce VGADisplayParams Paolo Bonzini
2024-01-02 9:59 ` Philippe Mathieu-Daudé
2023-12-31 9:39 ` [PATCH 3/8] vga: mask addresses in non-VESA modes to 256k Paolo Bonzini
2023-12-31 16:13 ` BALATON Zoltan
2023-12-31 9:39 ` Paolo Bonzini [this message]
2023-12-31 9:39 ` [PATCH 5/8] vga: optimize horizontal pel panning in 256-color modes Paolo Bonzini
2023-12-31 16:27 ` BALATON Zoltan
2024-01-01 19:02 ` Paolo Bonzini
2024-01-02 11:45 ` BALATON Zoltan
2023-12-31 9:39 ` [PATCH 6/8] vga: reindent memory access code Paolo Bonzini
2024-01-02 10:00 ` Philippe Mathieu-Daudé
2023-12-31 9:39 ` [PATCH 7/8] vga: use latches in odd/even mode too Paolo Bonzini
2023-12-31 9:39 ` [PATCH 8/8] vga: sort-of implement word and double-word access modes Paolo Bonzini
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=20231231093918.239549-5-pbonzini@redhat.com \
--to=pbonzini@redhat.com \
--cc=kraxel@redhat.com \
--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).