VGA console was extremely slow in QEMU because every character caused:
- 4 outb I/O port writes for cursor update
- Direct writes to VGA MMIO (0xB8000) which QEMU traps per-access
- Full-screen memmove on MMIO for each scroll
Three-layer optimization:
1. Shadow buffer: all VGA writes target a RAM shadow[] array. Only
dirty cells are flushed to VGA MMIO. Scrolling uses RAM-speed
memmove instead of MMIO memmove.
2. Batched TTY output: tty_write_kbuf/tty_write now OPOST-expand
into a local buffer and call console_write_buf() once per chunk
instead of console_put_char() per character. VGA cursor is
updated once per batch, not per character.
3. Deferred flush: vga_write_buf() (bulk TTY path) does NOT flush
to VGA MMIO at all. Screen is refreshed at 50Hz via vga_flush()
called from the timer tick. Single-char paths (echo, kprintf)
still flush immediately for responsiveness.
Result: 20/20 smoke tests in 8s WITHOUT console=serial (was timing
out at 90s before). The console=serial workaround is no longer
needed.