From: Marc-André Lureau <marcandre.lureau@redhat.com>
When bit 3 of the VGA Attribute Mode Control register is set, attribute
bit 7 switches from selecting bright background colors to enabling
character blink.
Implement this by tracking a separate blink phase timer that toggles
every 32 frames (matching real VGA hardware frame counter bit 5 @60hz),
and rendering blinking characters by replacing their foreground with
background during the off phase.
As with cursor, no VMState migration of the fields, as they are
transient display-side states.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/1585
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/display/vga_int.h | 2 ++
hw/display/vga.c | 24 +++++++++++++++++++++---
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index 747b5cc6cf8..5664317ecd6 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -130,6 +130,8 @@ typedef struct VGACommonState {
uint8_t cursor_start, cursor_end;
bool cursor_visible_phase;
int64_t cursor_blink_time;
+ bool blink_visible_phase;
+ int64_t blink_time;
uint32_t cursor_offset;
const GraphicHwOps *hw_ops;
bool full_update_text;
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 0ac4bf37310..ea01a889f20 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -45,8 +45,10 @@
bool have_vga = true;
-/* 16 state changes per vertical frame @60 Hz */
+/* frame counter bit 4: cursor blink toggles every 16 frames @60 Hz */
#define VGA_TEXT_CURSOR_PERIOD_MS (1000 * 2 * 16 / 60)
+/* frame counter bit 5: character blink toggles every 32 frames @60 Hz */
+#define VGA_TEXT_BLINK_PERIOD_MS (1000 * 2 * 32 / 60)
/* Address mask for non-VESA modes. */
#define VGA_VRAM_SIZE (256 * KiB)
@@ -1286,6 +1288,13 @@ static void vga_draw_text(VGACommonState *s, int full_update)
s->cursor_blink_time = now + VGA_TEXT_CURSOR_PERIOD_MS / 2;
s->cursor_visible_phase = !s->cursor_visible_phase;
}
+ if (now >= s->blink_time) {
+ s->blink_time = now + VGA_TEXT_BLINK_PERIOD_MS / 2;
+ s->blink_visible_phase = !s->blink_visible_phase;
+ if (s->ar[VGA_ATC_MODE] & 0x08) {
+ full_update = 1;
+ }
+ }
dest = surface_data(surface);
linesize = surface_stride(surface);
@@ -1317,8 +1326,17 @@ static void vga_draw_text(VGACommonState *s, int full_update)
#endif
font_ptr = font_base[(cattr >> 3) & 1];
font_ptr += 32 * 4 * ch;
- bgcol = palette[cattr >> 4];
- fgcol = palette[cattr & 0x0f];
+ if (s->ar[VGA_ATC_MODE] & 0x08) {
+ bgcol = palette[(cattr >> 4) & 0x07];
+ if ((cattr & 0x80) && !s->blink_visible_phase) {
+ fgcol = bgcol;
+ } else {
+ fgcol = palette[cattr & 0x0f];
+ }
+ } else {
+ bgcol = palette[cattr >> 4];
+ fgcol = palette[cattr & 0x0f];
+ }
if (cw == 16) {
vga_draw_glyph16(d1, linesize,
font_ptr, cheight, fgcol, bgcol);
--
2.54.0