@samitouri / QOSamiQemu / commits / a0e5242e1c

vga: implement text mode character blink

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. Related to: https://gitlab.com/qemu-project/qemu/-/work_items/1585 Acked-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-ID: <20260525081944.1494798-1-marcandre.lureau@redhat.com>

Marc-André Lureau committed May 25, 2026 at 12:19 UTC a0e5242e1c670b368e9639bf265e22e11c0c65fe
2 files changed +23 -4
hw/display/vga.c
+21 -4
@@ -45,8 +45,10 @@
45
46 bool have_vga = true;
47
48 -/* 16 state changes per vertical frame @60 Hz */
48 +/* frame counter bit 4: cursor blink toggles every 16 frames @60 Hz */
49 #define VGA_TEXT_CURSOR_PERIOD_MS (1000 * 2 * 16 / 60)
50 +/* frame counter bit 5: character blink toggles every 32 frames @60 Hz */
51 +#define VGA_TEXT_BLINK_PERIOD_MS (1000 * 2 * 32 / 60)
52
53 /* Address mask for non-VESA modes. */
54 #define VGA_VRAM_SIZE (256 * KiB)
@@ -1190,7 +1192,6 @@ static void vga_get_text_resolution(VGACommonState *s, int *pwidth, int *pheight
1192 * - double scan
1193 * - double width
1194 * - underline
1193 - * - flashing
1195 */
1196 static void vga_draw_text(VGACommonState *s, int full_update)
1197 {
@@ -1286,6 +1287,13 @@ static void vga_draw_text(VGACommonState *s, int full_update)
1287 s->cursor_blink_time = now + VGA_TEXT_CURSOR_PERIOD_MS / 2;
1288 s->cursor_visible_phase = !s->cursor_visible_phase;
1289 }
1290 + if (now >= s->blink_time) {
1291 + s->blink_time = now + VGA_TEXT_BLINK_PERIOD_MS / 2;
1292 + s->blink_visible_phase = !s->blink_visible_phase;
1293 + if (s->ar[VGA_ATC_MODE] & 0x08) {
1294 + full_update = 1;
1295 + }
1296 + }
1297
1298 dest = surface_data(surface);
1299 linesize = surface_stride(surface);
@@ -1317,8 +1325,17 @@ static void vga_draw_text(VGACommonState *s, int full_update)
1325 #endif
1326 font_ptr = font_base[(cattr >> 3) & 1];
1327 font_ptr += 32 * 4 * ch;
1320 - bgcol = palette[cattr >> 4];
1321 - fgcol = palette[cattr & 0x0f];
1328 + if (s->ar[VGA_ATC_MODE] & 0x08) {
1329 + bgcol = palette[(cattr >> 4) & 0x07];
1330 + if ((cattr & 0x80) && !s->blink_visible_phase) {
1331 + fgcol = bgcol;
1332 + } else {
1333 + fgcol = palette[cattr & 0x0f];
1334 + }
1335 + } else {
1336 + bgcol = palette[cattr >> 4];
1337 + fgcol = palette[cattr & 0x0f];
1338 + }
1339 if (cw == 16) {
1340 vga_draw_glyph16(d1, linesize,
1341 font_ptr, cheight, fgcol, bgcol);
hw/display/vga_int.h
+2
@@ -130,6 +130,8 @@ typedef struct VGACommonState {
130 uint8_t cursor_start, cursor_end;
131 bool cursor_visible_phase;
132 int64_t cursor_blink_time;
133 + bool blink_visible_phase;
134 + int64_t blink_time;
135 uint32_t cursor_offset;
136 const GraphicHwOps *hw_ops;
137 bool full_update_text;