spelling fixes
[qemu] / hw / vga.c
index c3aa93f..2a0a9c3 100644 (file)
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -1,5 +1,5 @@
 /*
- * QEMU VGA Emulator. An S3 86c968 is emulated
+ * QEMU VGA Emulator.
  * 
  * Copyright (c) 2003 Fabrice Bellard
  * 
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <unistd.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <time.h>
-#include <sys/time.h>
-#include <malloc.h>
-#include <termios.h>
-#include <sys/poll.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-
-#define NO_THUNK_TYPE_SIZE
-#include "thunk.h"
-
-#include "cpu.h"
-#include "exec-all.h"
-
 #include "vl.h"
+#include "vga_int.h"
 
 //#define DEBUG_VGA
 //#define DEBUG_VGA_MEM
 //#define DEBUG_VGA_REG
 
 //#define DEBUG_S3
-#define CONFIG_S3VGA
-
-#define MSR_COLOR_EMULATION 0x01
-#define MSR_PAGE_SELECT     0x20
-
-#define ST01_V_RETRACE      0x08
-#define ST01_DISP_ENABLE    0x01
-
-typedef struct VGAState {
-    uint8_t *vram_ptr;
-    unsigned long vram_offset;
-    unsigned int vram_size;
-    uint32_t latch;
-    uint8_t sr_index;
-    uint8_t sr[8];
-    uint8_t gr_index;
-    uint8_t gr[16];
-    uint8_t ar_index;
-    uint8_t ar[21];
-    int ar_flip_flop;
-    uint8_t cr_index;
-    uint8_t cr[256]; /* CRT registers */
-    uint8_t msr; /* Misc Output Register */
-    uint8_t fcr; /* Feature Control Register */
-    uint8_t st00; /* status 0 */
-    uint8_t st01; /* status 1 */
-    uint8_t dac_state;
-    uint8_t dac_sub_index;
-    uint8_t dac_read_index;
-    uint8_t dac_write_index;
-    uint8_t dac_cache[3]; /* used when writing */
-    uint8_t palette[768];
-
-    /* display refresh support */
-    DisplayState *ds;
-    uint32_t font_offsets[2];
-    int graphic_mode;
-    uint8_t shift_control;
-    uint8_t double_scan;
-    uint32_t line_offset;
-    uint32_t line_compare;
-    uint32_t start_addr;
-    uint8_t last_cw, last_ch;
-    uint32_t last_width, last_height;
-    uint8_t cursor_start, cursor_end;
-    uint32_t cursor_offset;
-    unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned b);
-    /* tell for each page if it has been updated since the last time */
-    uint8_t vram_updated[VGA_RAM_SIZE / 4096];
-    uint32_t last_palette[256];
-#define CH_ATTR_SIZE (160 * 100)
-    uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */
-} VGAState;
+//#define DEBUG_BOCHS_VBE
+
+/* S3 VGA is deprecated - another graphic card will be emulated */
+//#define CONFIG_S3VGA
 
 /* force some bits to zero */
-static const uint8_t sr_mask[8] = {
+const uint8_t sr_mask[8] = {
     (uint8_t)~0xfc,
     (uint8_t)~0xc2,
     (uint8_t)~0xf0,
@@ -119,7 +46,7 @@ static const uint8_t sr_mask[8] = {
     (uint8_t)~0x00,
 };
 
-static const uint8_t gr_mask[16] = {
+const uint8_t gr_mask[16] = {
     (uint8_t)~0xf0, /* 0x00 */
     (uint8_t)~0xf0, /* 0x01 */
     (uint8_t)~0xf0, /* 0x02 */
@@ -220,12 +147,12 @@ static uint32_t expand4[256];
 static uint16_t expand2[256];
 static uint8_t expand4to8[16];
 
-VGAState vga_state;
+VGAState *vga_state;
 int vga_io_memory;
 
-static uint32_t vga_ioport_read(CPUState *env, uint32_t addr)
+static uint32_t vga_ioport_read(void *opaque, uint32_t addr)
 {
-    VGAState *s = &vga_state;
+    VGAState *s = opaque;
     int val, index;
 
     /* check port range access depending on color/monochrome mode */
@@ -263,6 +190,9 @@ static uint32_t vga_ioport_read(CPUState *env, uint32_t addr)
         case 0x3c7:
             val = s->dac_state;
             break;
+       case 0x3c8:
+           val = s->dac_write_index;
+           break;
         case 0x3c9:
             val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
             if (++s->dac_sub_index == 3) {
@@ -313,16 +243,16 @@ static uint32_t vga_ioport_read(CPUState *env, uint32_t addr)
             break;
         }
     }
-#ifdef DEBUG_VGA
+#if defined(DEBUG_VGA)
     printf("VGA: read addr=0x%04x data=0x%02x\n", addr, val);
 #endif
     return val;
 }
 
-static void vga_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
+static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 {
-    VGAState *s = &vga_state;
-    int index, v;
+    VGAState *s = opaque;
+    int index;
 
     /* check port range access depending on color/monochrome mode */
     if ((addr >= 0x3b0 && addr <= 0x3bf && (s->msr & MSR_COLOR_EMULATION)) ||
@@ -440,15 +370,21 @@ static void vga_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
             break;
         case 0x31:
             /* update start address */
-            s->cr[s->cr_index] = val;
-            v = (val >> 4) & 3;
-            s->cr[0x69] = (s->cr[69] & ~0x03) | v;
+            {
+                int v;
+                s->cr[s->cr_index] = val;
+                v = (val >> 4) & 3;
+                s->cr[0x69] = (s->cr[69] & ~0x03) | v;
+            }
             break;
         case 0x51:
             /* update start address */
-            s->cr[s->cr_index] = val;
-            v = val & 3;
-            s->cr[0x69] = (s->cr[69] & ~0x0c) | (v << 2);
+            {
+                int v;
+                s->cr[s->cr_index] = val;
+                v = val & 3;
+                s->cr[0x69] = (s->cr[69] & ~0x0c) | (v << 2);
+            }
             break;
 #endif
         default:
@@ -468,32 +404,201 @@ static void vga_ioport_write(CPUState *env, uint32_t addr, uint32_t val)
     }
 }
 
+#ifdef CONFIG_BOCHS_VBE
+static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr)
+{
+    VGAState *s = opaque;
+    uint32_t val;
+    val = s->vbe_index;
+    return val;
+}
+
+static uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr)
+{
+    VGAState *s = opaque;
+    uint32_t val;
+
+    if (s->vbe_index <= VBE_DISPI_INDEX_NB)
+        val = s->vbe_regs[s->vbe_index];
+    else
+        val = 0;
+#ifdef DEBUG_BOCHS_VBE
+    printf("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val);
+#endif
+    return val;
+}
+
+static void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val)
+{
+    VGAState *s = opaque;
+    s->vbe_index = val;
+}
+
+static void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val)
+{
+    VGAState *s = opaque;
+
+    if (s->vbe_index <= VBE_DISPI_INDEX_NB) {
+#ifdef DEBUG_BOCHS_VBE
+        printf("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val);
+#endif
+        switch(s->vbe_index) {
+        case VBE_DISPI_INDEX_ID:
+            if (val == VBE_DISPI_ID0 ||
+                val == VBE_DISPI_ID1 ||
+                val == VBE_DISPI_ID2) {
+                s->vbe_regs[s->vbe_index] = val;
+            }
+            break;
+        case VBE_DISPI_INDEX_XRES:
+            if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) {
+                s->vbe_regs[s->vbe_index] = val;
+            }
+            break;
+        case VBE_DISPI_INDEX_YRES:
+            if (val <= VBE_DISPI_MAX_YRES) {
+                s->vbe_regs[s->vbe_index] = val;
+            }
+            break;
+        case VBE_DISPI_INDEX_BPP:
+            if (val == 0)
+                val = 8;
+            if (val == 4 || val == 8 || val == 15 || 
+                val == 16 || val == 24 || val == 32) {
+                s->vbe_regs[s->vbe_index] = val;
+            }
+            break;
+        case VBE_DISPI_INDEX_BANK:
+            val &= s->vbe_bank_mask;
+            s->vbe_regs[s->vbe_index] = val;
+            s->bank_offset = (val << 16);
+            break;
+        case VBE_DISPI_INDEX_ENABLE:
+            if (val & VBE_DISPI_ENABLED) {
+                int h, shift_control;
+
+                s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = 
+                    s->vbe_regs[VBE_DISPI_INDEX_XRES];
+                s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = 
+                    s->vbe_regs[VBE_DISPI_INDEX_YRES];
+                s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0;
+                s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0;
+                
+                if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
+                    s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1;
+                else
+                    s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] * 
+                        ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
+                s->vbe_start_addr = 0;
+                
+                /* clear the screen (should be done in BIOS) */
+                if (!(val & VBE_DISPI_NOCLEARMEM)) {
+                    memset(s->vram_ptr, 0, 
+                           s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset);
+                }
+                
+                /* we initialize the VGA graphic mode (should be done
+                   in BIOS) */
+                s->gr[0x06] = (s->gr[0x06] & ~0x0c) | 0x05; /* graphic mode + memory map 1 */
+                s->cr[0x17] |= 3; /* no CGA modes */
+                s->cr[0x13] = s->vbe_line_offset >> 3;
+                /* width */
+                s->cr[0x01] = (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1;
+                /* height */
+                h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1;
+                s->cr[0x12] = h;
+                s->cr[0x07] = (s->cr[0x07] & ~0x42) | 
+                    ((h >> 7) & 0x02) | ((h >> 3) & 0x40);
+                /* line compare to 1023 */
+                s->cr[0x18] = 0xff;
+                s->cr[0x07] |= 0x10;
+                s->cr[0x09] |= 0x40;
+                
+                if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
+                    shift_control = 0;
+                    s->sr[0x01] &= ~8; /* no double line */
+                } else {
+                    shift_control = 2;
+                    s->sr[4] |= 0x08; /* set chain 4 mode */
+                    s->sr[2] |= 0x0f; /* activate all planes */
+                }
+                s->gr[0x05] = (s->gr[0x05] & ~0x60) | (shift_control << 5);
+                s->cr[0x09] &= ~0x9f; /* no double scan */
+            } else {
+                /* XXX: the bios should do that */
+                s->bank_offset = 0;
+            }
+            s->vbe_regs[s->vbe_index] = val;
+            break;
+        case VBE_DISPI_INDEX_VIRT_WIDTH:
+            {
+                int w, h, line_offset;
+
+                if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES])
+                    return;
+                w = val;
+                if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
+                    line_offset = w >> 1;
+                else
+                    line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
+                h = s->vram_size / line_offset;
+                /* XXX: support weird bochs semantics ? */
+                if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES])
+                    return;
+                s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w;
+                s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h;
+                s->vbe_line_offset = line_offset;
+            }
+            break;
+        case VBE_DISPI_INDEX_X_OFFSET:
+        case VBE_DISPI_INDEX_Y_OFFSET:
+            {
+                int x;
+                s->vbe_regs[s->vbe_index] = val;
+                s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET];
+                x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET];
+                if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4)
+                    s->vbe_start_addr += x >> 1;
+                else
+                    s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3);
+                s->vbe_start_addr >>= 2;
+            }
+            break;
+        default:
+            break;
+        }
+    }
+}
+#endif
+
 /* called for accesses between 0xa0000 and 0xc0000 */
-static uint32_t vga_mem_readb(uint32_t addr)
+uint32_t vga_mem_readb(void *opaque, target_phys_addr_t addr)
 {
-    VGAState *s = &vga_state;
+    VGAState *s = opaque;
     int memory_map_mode, plane;
     uint32_t ret;
     
     /* convert to VGA memory offset */
     memory_map_mode = (s->gr[6] >> 2) & 3;
+    addr &= 0x1ffff;
     switch(memory_map_mode) {
     case 0:
-        addr -= 0xa0000;
         break;
     case 1:
-        addr -= 0xa0000;
         if (addr >= 0x10000)
             return 0xff;
+        addr += s->bank_offset;
         break;
     case 2:
-        addr -= 0xb0000;
+        addr -= 0x10000;
         if (addr >= 0x8000)
             return 0xff;
         break;
     default:
     case 3:
-        addr -= 0xb8000;
+        addr -= 0x18000;
+        if (addr >= 0x8000)
+            return 0xff;
         break;
     }
     
@@ -523,28 +628,40 @@ static uint32_t vga_mem_readb(uint32_t addr)
     return ret;
 }
 
-static uint32_t vga_mem_readw(uint32_t addr)
+static uint32_t vga_mem_readw(void *opaque, target_phys_addr_t addr)
 {
     uint32_t v;
-    v = vga_mem_readb(addr);
-    v |= vga_mem_readb(addr + 1) << 8;
+#ifdef TARGET_WORDS_BIGENDIAN
+    v = vga_mem_readb(opaque, addr) << 8;
+    v |= vga_mem_readb(opaque, addr + 1);
+#else
+    v = vga_mem_readb(opaque, addr);
+    v |= vga_mem_readb(opaque, addr + 1) << 8;
+#endif
     return v;
 }
 
-static uint32_t vga_mem_readl(uint32_t addr)
+static uint32_t vga_mem_readl(void *opaque, target_phys_addr_t addr)
 {
     uint32_t v;
-    v = vga_mem_readb(addr);
-    v |= vga_mem_readb(addr + 1) << 8;
-    v |= vga_mem_readb(addr + 2) << 16;
-    v |= vga_mem_readb(addr + 3) << 24;
+#ifdef TARGET_WORDS_BIGENDIAN
+    v = vga_mem_readb(opaque, addr) << 24;
+    v |= vga_mem_readb(opaque, addr + 1) << 16;
+    v |= vga_mem_readb(opaque, addr + 2) << 8;
+    v |= vga_mem_readb(opaque, addr + 3);
+#else
+    v = vga_mem_readb(opaque, addr);
+    v |= vga_mem_readb(opaque, addr + 1) << 8;
+    v |= vga_mem_readb(opaque, addr + 2) << 16;
+    v |= vga_mem_readb(opaque, addr + 3) << 24;
+#endif
     return v;
 }
 
 /* called for accesses between 0xa0000 and 0xc0000 */
-void vga_mem_writeb(uint32_t addr, uint32_t val)
+void vga_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
 {
-    VGAState *s = &vga_state;
+    VGAState *s = opaque;
     int memory_map_mode, plane, write_mode, b, func_select;
     uint32_t write_mask, bit_mask, set_mask;
 
@@ -553,23 +670,25 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
 #endif
     /* convert to VGA memory offset */
     memory_map_mode = (s->gr[6] >> 2) & 3;
+    addr &= 0x1ffff;
     switch(memory_map_mode) {
     case 0:
-        addr -= 0xa0000;
         break;
     case 1:
-        addr -= 0xa0000;
         if (addr >= 0x10000)
             return;
+        addr += s->bank_offset;
         break;
     case 2:
-        addr -= 0xb0000;
+        addr -= 0x10000;
         if (addr >= 0x8000)
             return;
         break;
     default:
     case 3:
-        addr -= 0xb8000;
+        addr -= 0x18000;
+        if (addr >= 0x8000)
+            return;
         break;
     }
     
@@ -581,7 +700,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
 #ifdef DEBUG_VGA_MEM
             printf("vga: chain4: [0x%x]\n", addr);
 #endif
-            s->vram_updated[addr >> 12] = 1;
+            cpu_physical_memory_set_dirty(s->vram_offset + addr);
         }
     } else if (s->gr[5] & 0x10) {
         /* odd/even mode (aka text mode mapping) */
@@ -592,7 +711,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
 #ifdef DEBUG_VGA_MEM
             printf("vga: odd/even: [0x%x]\n", addr);
 #endif
-            s->vram_updated[addr >> 12] = 1;
+            cpu_physical_memory_set_dirty(s->vram_offset + addr);
         }
     } else {
         /* standard VGA latched access */
@@ -664,22 +783,34 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
             printf("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n", 
                    addr * 4, write_mask, val);
 #endif
-        s->vram_updated[addr >> 10] = 1;
+            cpu_physical_memory_set_dirty(s->vram_offset + (addr << 2));
     }
 }
 
-void vga_mem_writew(uint32_t addr, uint32_t val)
+static void vga_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
 {
-    vga_mem_writeb(addr, val & 0xff);
-    vga_mem_writeb(addr + 1, (val >> 8) & 0xff);
+#ifdef TARGET_WORDS_BIGENDIAN
+    vga_mem_writeb(opaque, addr, (val >> 8) & 0xff);
+    vga_mem_writeb(opaque, addr + 1, val & 0xff);
+#else
+    vga_mem_writeb(opaque, addr, val & 0xff);
+    vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+#endif
 }
 
-void vga_mem_writel(uint32_t addr, uint32_t val)
+static void vga_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
 {
-    vga_mem_writeb(addr, val & 0xff);
-    vga_mem_writeb(addr + 1, (val >> 8) & 0xff);
-    vga_mem_writeb(addr + 2, (val >> 16) & 0xff);
-    vga_mem_writeb(addr + 3, (val >> 24) & 0xff);
+#ifdef TARGET_WORDS_BIGENDIAN
+    vga_mem_writeb(opaque, addr, (val >> 24) & 0xff);
+    vga_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff);
+    vga_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff);
+    vga_mem_writeb(opaque, addr + 3, val & 0xff);
+#else
+    vga_mem_writeb(opaque, addr, val & 0xff);
+    vga_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+    vga_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+    vga_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+#endif
 }
 
 typedef void vga_draw_glyph8_func(uint8_t *d, int linesize,
@@ -724,14 +855,6 @@ static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsign
 #define DEPTH 32
 #include "vga_template.h"
 
-static inline int c6_to_8(int v)
-{
-    int b;
-    v &= 0x3f;
-    b = v & 1;
-    return (v << 2) | (b << 1) | b;
-}
-
 static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
 {
     unsigned int col;
@@ -812,29 +935,50 @@ static int update_palette256(VGAState *s)
     return full_update;
 }
 
+static void vga_get_offsets(VGAState *s, 
+                            uint32_t *pline_offset, 
+                            uint32_t *pstart_addr)
+{
+    uint32_t start_addr, line_offset;
+#ifdef CONFIG_BOCHS_VBE
+    if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+        line_offset = s->vbe_line_offset;
+        start_addr = s->vbe_start_addr;
+    } else
+#endif
+    {  
+        /* compute line_offset in bytes */
+        line_offset = s->cr[0x13];
+#ifdef CONFIG_S3VGA
+        {
+            uinr32_t v;
+            v = (s->cr[0x51] >> 4) & 3; /* S3 extension */
+            if (v == 0)
+                v = (s->cr[0x43] >> 2) & 1; /* S3 extension */
+            line_offset |= (v << 8);
+        }
+#endif
+        line_offset <<= 3;
+        
+        /* starting address */
+        start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
+#ifdef CONFIG_S3VGA
+        start_addr |= (s->cr[0x69] & 0x1f) << 16; /* S3 extension */
+#endif
+    }
+    *pline_offset = line_offset;
+    *pstart_addr = start_addr;
+}
+
 /* update start_addr and line_offset. Return TRUE if modified */
 static int update_basic_params(VGAState *s)
 {
     int full_update;
-    uint32_t start_addr, line_offset, line_compare, v;
+    uint32_t start_addr, line_offset, line_compare;
     
     full_update = 0;
-    /* compute line_offset in bytes */
-    line_offset = s->cr[0x13];
-#ifdef CONFIG_S3VGA
-    v = (s->cr[0x51] >> 4) & 3; /* S3 extension */
-    if (v == 0)
-        v = (s->cr[0x43] >> 2) & 1; /* S3 extension */
-    line_offset |= (v << 8);
-#endif
-    line_offset <<= 3;
-
-    /* starting address */
-    start_addr = s->cr[0x0d] | (s->cr[0x0c] << 8);
-#ifdef CONFIG_S3VGA
-    start_addr |= (s->cr[0x69] & 0x1f) << 16; /* S3 extension */
-#endif
 
+    s->get_offsets(s, &line_offset, &start_addr);
     /* line compare */
     line_compare = s->cr[0x18] | 
         ((s->cr[0x07] & 0x10) << 4) |
@@ -932,14 +1076,14 @@ static void vga_draw_text(VGAState *s, int full_update)
     
     /* compute font data address (in plane 2) */
     v = s->sr[3];
-    offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
+    offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
     if (offset != s->font_offsets[0]) {
         s->font_offsets[0] = offset;
         full_update = 1;
     }
     font_base[0] = s->vram_ptr + offset;
 
-    offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
+    offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2;
     font_base[1] = s->vram_ptr + offset;
     if (offset != s->font_offsets[1]) {
         s->font_offsets[1] = offset;
@@ -954,7 +1098,7 @@ static void vga_draw_text(VGAState *s, int full_update)
     /* total width & height */
     cheight = (s->cr[9] & 0x1f) + 1;
     cw = 8;
-    if (s->sr[1] & 0x01)
+    if (!(s->sr[1] & 0x01))
         cw = 9;
     if (s->sr[1] & 0x08)
         cw = 16; /* NOTE: no 18 pixel wide */
@@ -969,9 +1113,16 @@ static void vga_draw_text(VGAState *s, int full_update)
             ((s->cr[0x07] & 0x40) << 3);
         height = (height + 1) / cheight;
     }
+    if ((height * width) > CH_ATTR_SIZE) {
+        /* better than nothing: exit if transient size is too big */
+        return;
+    }
+
     if (width != s->last_width || height != s->last_height ||
-        cw != s->last_cw || cw != s->last_cw) {
-        dpy_resize(s->ds, width * cw, height * cheight);
+        cw != s->last_cw || cheight != s->last_ch) {
+        s->last_scr_width = width * cw;
+        s->last_scr_height = height * cheight;
+        dpy_resize(s->ds, s->last_scr_width, s->last_scr_height);
         s->last_width = width;
         s->last_height = height;
         s->last_ch = cheight;
@@ -1082,6 +1233,7 @@ enum {
     VGA_DRAW_LINE8,
     VGA_DRAW_LINE15,
     VGA_DRAW_LINE16,
+    VGA_DRAW_LINE24,
     VGA_DRAW_LINE32,
     VGA_DRAW_LINE_NB,
 };
@@ -1127,17 +1279,58 @@ static vga_draw_line_func *vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
     vga_draw_line16_16,
     vga_draw_line16_32,
 
+    vga_draw_line24_8,
+    vga_draw_line24_15,
+    vga_draw_line24_16,
+    vga_draw_line24_32,
+
     vga_draw_line32_8,
     vga_draw_line32_15,
     vga_draw_line32_16,
     vga_draw_line32_32,
 };
 
+static int vga_get_bpp(VGAState *s)
+{
+    int ret;
+#ifdef CONFIG_BOCHS_VBE
+    if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) {
+        ret = s->vbe_regs[VBE_DISPI_INDEX_BPP];
+    } else 
+#endif
+    {
+        ret = 0;
+    }
+    return ret;
+}
+
+static void vga_get_resolution(VGAState *s, int *pwidth, int *pheight)
+{
+    int width, height;
+    
+    width = (s->cr[0x01] + 1) * 8;
+    height = s->cr[0x12] | 
+        ((s->cr[0x07] & 0x02) << 7) | 
+        ((s->cr[0x07] & 0x40) << 3);
+    height = (height + 1);
+    *pwidth = width;
+    *pheight = height;
+}
+
+void vga_invalidate_scanlines(VGAState *s, int y1, int y2)
+{
+    int y;
+    if (y1 >= VGA_MAX_HEIGHT)
+        return;
+    if (y2 >= VGA_MAX_HEIGHT)
+        y2 = VGA_MAX_HEIGHT;
+    for(y = y1; y < y2; y++) {
+        s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f);
+    }
+}
+
 /* 
  * graphic modes
- * Missing:
- * - double scan
- * - double width 
  */
 static void vga_draw_graphic(VGAState *s, int full_update)
 {
@@ -1150,13 +1343,9 @@ static void vga_draw_graphic(VGAState *s, int full_update)
     
     full_update |= update_basic_params(s);
 
-    width = (s->cr[0x01] + 1) * 8;
-    height = s->cr[0x12] | 
-        ((s->cr[0x07] & 0x02) << 7) | 
-        ((s->cr[0x07] & 0x40) << 3);
-    height = (height + 1);
+    s->get_resolution(s, &width, &height);
     disp_width = width;
-    
+
     shift_control = (s->gr[0x05] >> 5) & 3;
     double_scan = (s->cr[0x09] & 0x80);
     if (shift_control > 1) {
@@ -1189,19 +1378,44 @@ static void vga_draw_graphic(VGAState *s, int full_update)
             v = VGA_DRAW_LINE2;
         }
     } else {
-        full_update |= update_palette256(s);
-        v = VGA_DRAW_LINE8D2;
+        switch(s->get_bpp(s)) {
+        default:
+        case 0:
+            full_update |= update_palette256(s);
+            v = VGA_DRAW_LINE8D2;
+            break;
+        case 8:
+            full_update |= update_palette256(s);
+            v = VGA_DRAW_LINE8;
+            break;
+        case 15:
+            v = VGA_DRAW_LINE15;
+            break;
+        case 16:
+            v = VGA_DRAW_LINE16;
+            break;
+        case 24:
+            v = VGA_DRAW_LINE24;
+            break;
+        case 32:
+            v = VGA_DRAW_LINE32;
+            break;
+        }
     }
     vga_draw_line = vga_draw_line_table[v * 4 + get_depth_index(s->ds->depth)];
 
     if (disp_width != s->last_width ||
         height != s->last_height) {
         dpy_resize(s->ds, disp_width, height);
+        s->last_scr_width = disp_width;
+        s->last_scr_height = height;
         s->last_width = disp_width;
         s->last_height = height;
         full_update = 1;
     }
-
+    if (s->cursor_invalidate)
+        s->cursor_invalidate(s);
+    
     line_offset = s->line_offset;
 #if 0
     printf("w=%d h=%d v=%d line_offset=%d double_scan=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=%02x\n",
@@ -1226,13 +1440,16 @@ static void vga_draw_graphic(VGAState *s, int full_update)
         if (!(s->cr[0x17] & 2)) {
             addr = (addr & ~0x8000) | ((y1 & 2) << 14);
         }
-        page0 = addr >> 12;
-        page1 = (addr + bwidth - 1) >> 12;
-        update = full_update | s->vram_updated[page0] | s->vram_updated[page1];
-        if ((page1 - page0) > 1) {
+        page0 = s->vram_offset + (addr & TARGET_PAGE_MASK);
+        page1 = s->vram_offset + ((addr + bwidth - 1) & TARGET_PAGE_MASK);
+        update = full_update | cpu_physical_memory_is_dirty(page0) |
+            cpu_physical_memory_is_dirty(page1);
+        if ((page1 - page0) > TARGET_PAGE_SIZE) {
             /* if wide line, can use another page */
-            update |= s->vram_updated[page0 + 1];
+            update |= cpu_physical_memory_is_dirty(page0 + TARGET_PAGE_SIZE);
         }
+        /* explicit invalidation for the hardware cursor */
+        update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1;
         if (update) {
             if (y_start < 0)
                 y_start = y;
@@ -1241,6 +1458,8 @@ static void vga_draw_graphic(VGAState *s, int full_update)
             if (page1 > page_max)
                 page_max = page1;
             vga_draw_line(s, d, s->vram_ptr + addr, width);
+            if (s->cursor_draw_line)
+                s->cursor_draw_line(s, d, y);
         } else {
             if (y_start >= 0) {
                 /* flush to display */
@@ -1274,58 +1493,97 @@ static void vga_draw_graphic(VGAState *s, int full_update)
     }
     /* reset modified pages */
     if (page_max != -1) {
-        memset(s->vram_updated + page_min, 0, page_max - page_min + 1);
+        cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE);
     }
+    memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4);
 }
 
-/* draw text terminal (very limited, just for simple boot debug
-   messages) */
-static int last_cursor_pos;
-
-void vga_draw_dumb(VGAState *s)
+static void vga_draw_blank(VGAState *s, int full_update)
 {
-    int c, i, cursor_pos, eol;
-
-    cursor_pos = s->cr[0x0f] | (s->cr[0x0e] << 8);
-    eol = 0;
-    for(i = last_cursor_pos; i < cursor_pos; i++) {
-        /* XXX: should use vga RAM */
-        c = phys_ram_base[0xb8000 + (i) * 2];
-        if (c >= ' ') {
-            putchar(c);
-            eol = 0;
-        } else {
-            if (!eol)
-                putchar('\n');
-            eol = 1;
-        }
+    int i, w, val;
+    uint8_t *d;
+
+    if (!full_update)
+        return;
+    if (s->last_scr_width <= 0 || s->last_scr_height <= 0)
+        return;
+    if (s->ds->depth == 8) 
+        val = s->rgb_to_pixel(0, 0, 0);
+    else
+        val = 0;
+    w = s->last_scr_width * ((s->ds->depth + 7) >> 3);
+    d = s->ds->data;
+    for(i = 0; i < s->last_scr_height; i++) {
+        memset(d, val, w);
+        d += s->ds->linesize;
     }
-    fflush(stdout);
-    last_cursor_pos = cursor_pos;
+    dpy_update(s->ds, 0, 0, 
+               s->last_scr_width, s->last_scr_height);
 }
 
+#define GMODE_TEXT     0
+#define GMODE_GRAPH    1
+#define GMODE_BLANK 2 
+
 void vga_update_display(void)
 {
-    VGAState *s = &vga_state;
+    VGAState *s = vga_state;
     int full_update, graphic_mode;
 
     if (s->ds->depth == 0) {
-        vga_draw_dumb(s);
+        /* nothing to do */
     } else {
+        switch(s->ds->depth) {
+        case 8:
+            s->rgb_to_pixel = rgb_to_pixel8_dup;
+            break;
+        case 15:
+            s->rgb_to_pixel = rgb_to_pixel15_dup;
+            break;
+        default:
+        case 16:
+            s->rgb_to_pixel = rgb_to_pixel16_dup;
+            break;
+        case 32:
+            s->rgb_to_pixel = rgb_to_pixel32_dup;
+            break;
+        }
+        
         full_update = 0;
-        graphic_mode = s->gr[6] & 1;
+        if (!(s->ar_index & 0x20)) {
+            graphic_mode = GMODE_BLANK;
+        } else {
+            graphic_mode = s->gr[6] & 1;
+        }
         if (graphic_mode != s->graphic_mode) {
             s->graphic_mode = graphic_mode;
             full_update = 1;
         }
-        if (graphic_mode)
-            vga_draw_graphic(s, full_update);
-        else
+        switch(graphic_mode) {
+        case GMODE_TEXT:
             vga_draw_text(s, full_update);
+            break;
+        case GMODE_GRAPH:
+            vga_draw_graphic(s, full_update);
+            break;
+        case GMODE_BLANK:
+        default:
+            vga_draw_blank(s, full_update);
+            break;
+        }
     }
 }
 
-void vga_reset(VGAState *s)
+/* force a full display refresh */
+void vga_invalidate_display(void)
+{
+    VGAState *s = vga_state;
+    
+    s->last_width = -1;
+    s->last_height = -1;
+}
+
+static void vga_reset(VGAState *s)
 {
     memset(s, 0, sizeof(VGAState));
 #ifdef CONFIG_S3VGA
@@ -1338,22 +1596,121 @@ void vga_reset(VGAState *s)
     s->graphic_mode = -1; /* force full update */
 }
 
-CPUReadMemoryFunc *vga_mem_read[3] = {
+static CPUReadMemoryFunc *vga_mem_read[3] = {
     vga_mem_readb,
     vga_mem_readw,
     vga_mem_readl,
 };
 
-CPUWriteMemoryFunc *vga_mem_write[3] = {
+static CPUWriteMemoryFunc *vga_mem_write[3] = {
     vga_mem_writeb,
     vga_mem_writew,
     vga_mem_writel,
 };
 
-int vga_initialize(DisplayState *ds, uint8_t *vga_ram_base, 
-                   unsigned long vga_ram_offset, int vga_ram_size)
+static void vga_save(QEMUFile *f, void *opaque)
+{
+    VGAState *s = opaque;
+    int i;
+
+    qemu_put_be32s(f, &s->latch);
+    qemu_put_8s(f, &s->sr_index);
+    qemu_put_buffer(f, s->sr, 8);
+    qemu_put_8s(f, &s->gr_index);
+    qemu_put_buffer(f, s->gr, 16);
+    qemu_put_8s(f, &s->ar_index);
+    qemu_put_buffer(f, s->ar, 21);
+    qemu_put_be32s(f, &s->ar_flip_flop);
+    qemu_put_8s(f, &s->cr_index);
+    qemu_put_buffer(f, s->cr, 256);
+    qemu_put_8s(f, &s->msr);
+    qemu_put_8s(f, &s->fcr);
+    qemu_put_8s(f, &s->st00);
+    qemu_put_8s(f, &s->st01);
+
+    qemu_put_8s(f, &s->dac_state);
+    qemu_put_8s(f, &s->dac_sub_index);
+    qemu_put_8s(f, &s->dac_read_index);
+    qemu_put_8s(f, &s->dac_write_index);
+    qemu_put_buffer(f, s->dac_cache, 3);
+    qemu_put_buffer(f, s->palette, 768);
+
+    qemu_put_be32s(f, &s->bank_offset);
+#ifdef CONFIG_BOCHS_VBE
+    qemu_put_byte(f, 1);
+    qemu_put_be16s(f, &s->vbe_index);
+    for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
+        qemu_put_be16s(f, &s->vbe_regs[i]);
+    qemu_put_be32s(f, &s->vbe_start_addr);
+    qemu_put_be32s(f, &s->vbe_line_offset);
+    qemu_put_be32s(f, &s->vbe_bank_mask);
+#else
+    qemu_put_byte(f, 0);
+#endif
+}
+
+static int vga_load(QEMUFile *f, void *opaque, int version_id)
+{
+    VGAState *s = opaque;
+    int is_vbe, i;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_be32s(f, &s->latch);
+    qemu_get_8s(f, &s->sr_index);
+    qemu_get_buffer(f, s->sr, 8);
+    qemu_get_8s(f, &s->gr_index);
+    qemu_get_buffer(f, s->gr, 16);
+    qemu_get_8s(f, &s->ar_index);
+    qemu_get_buffer(f, s->ar, 21);
+    qemu_get_be32s(f, &s->ar_flip_flop);
+    qemu_get_8s(f, &s->cr_index);
+    qemu_get_buffer(f, s->cr, 256);
+    qemu_get_8s(f, &s->msr);
+    qemu_get_8s(f, &s->fcr);
+    qemu_get_8s(f, &s->st00);
+    qemu_get_8s(f, &s->st01);
+
+    qemu_get_8s(f, &s->dac_state);
+    qemu_get_8s(f, &s->dac_sub_index);
+    qemu_get_8s(f, &s->dac_read_index);
+    qemu_get_8s(f, &s->dac_write_index);
+    qemu_get_buffer(f, s->dac_cache, 3);
+    qemu_get_buffer(f, s->palette, 768);
+
+    qemu_get_be32s(f, &s->bank_offset);
+    is_vbe = qemu_get_byte(f);
+#ifdef CONFIG_BOCHS_VBE
+    if (!is_vbe)
+        return -EINVAL;
+    qemu_get_be16s(f, &s->vbe_index);
+    for(i = 0; i < VBE_DISPI_INDEX_NB; i++)
+        qemu_get_be16s(f, &s->vbe_regs[i]);
+    qemu_get_be32s(f, &s->vbe_start_addr);
+    qemu_get_be32s(f, &s->vbe_line_offset);
+    qemu_get_be32s(f, &s->vbe_bank_mask);
+#else
+    if (is_vbe)
+        return -EINVAL;
+#endif
+
+    /* force refresh */
+    s->graphic_mode = -1;
+    return 0;
+}
+
+static void vga_map(PCIDevice *pci_dev, int region_num, 
+                    uint32_t addr, uint32_t size, int type)
+{
+    VGAState *s = vga_state;
+
+    cpu_register_physical_memory(addr, s->vram_size, s->vram_offset);
+}
+
+void vga_common_init(VGAState *s, DisplayState *ds, uint8_t *vga_ram_base, 
+                     unsigned long vga_ram_offset, int vga_ram_size)
 {
-    VGAState *s = &vga_state;
     int i, j, v, b;
 
     for(i = 0;i < 256; i++) {
@@ -1381,46 +1738,180 @@ int vga_initialize(DisplayState *ds, uint8_t *vga_ram_base,
 
     vga_reset(s);
 
-    switch(ds->depth) {
-    case 8:
-        s->rgb_to_pixel = rgb_to_pixel8_dup;
-        break;
-    case 15:
-        s->rgb_to_pixel = rgb_to_pixel15_dup;
-        break;
-    default:
-    case 16:
-        s->rgb_to_pixel = rgb_to_pixel16_dup;
-        break;
-    case 32:
-        s->rgb_to_pixel = rgb_to_pixel32_dup;
-        break;
-    }
-
     s->vram_ptr = vga_ram_base;
     s->vram_offset = vga_ram_offset;
     s->vram_size = vga_ram_size;
     s->ds = ds;
+    s->get_bpp = vga_get_bpp;
+    s->get_offsets = vga_get_offsets;
+    s->get_resolution = vga_get_resolution;
+    /* XXX: currently needed for display */
+    vga_state = s;
+}
+
+
+int vga_initialize(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base, 
+                   unsigned long vga_ram_offset, int vga_ram_size)
+{
+    VGAState *s;
+
+    s = qemu_mallocz(sizeof(VGAState));
+    if (!s)
+        return -1;
+
+    vga_common_init(s, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
 
-    register_ioport_write(0x3c0, 16, vga_ioport_write, 1);
+    register_savevm("vga", 0, 1, vga_save, vga_load, s);
 
-    register_ioport_write(0x3b4, 2, vga_ioport_write, 1);
-    register_ioport_write(0x3d4, 2, vga_ioport_write, 1);
-    register_ioport_write(0x3ba, 1, vga_ioport_write, 1);
-    register_ioport_write(0x3da, 1, vga_ioport_write, 1);
+    register_ioport_write(0x3c0, 16, 1, vga_ioport_write, s);
 
-    register_ioport_read(0x3c0, 16, vga_ioport_read, 1);
+    register_ioport_write(0x3b4, 2, 1, vga_ioport_write, s);
+    register_ioport_write(0x3d4, 2, 1, vga_ioport_write, s);
+    register_ioport_write(0x3ba, 1, 1, vga_ioport_write, s);
+    register_ioport_write(0x3da, 1, 1, vga_ioport_write, s);
 
-    register_ioport_read(0x3b4, 2, vga_ioport_read, 1);
-    register_ioport_read(0x3d4, 2, vga_ioport_read, 1);
-    register_ioport_read(0x3ba, 1, vga_ioport_read, 1);
-    register_ioport_read(0x3da, 1, vga_ioport_read, 1);
+    register_ioport_read(0x3c0, 16, 1, vga_ioport_read, s);
 
-    vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write);
+    register_ioport_read(0x3b4, 2, 1, vga_ioport_read, s);
+    register_ioport_read(0x3d4, 2, 1, vga_ioport_read, s);
+    register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
+    register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
+    s->bank_offset = 0;
+
+#ifdef CONFIG_BOCHS_VBE
+    s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID0;
+    s->vbe_bank_mask = ((s->vram_size >> 16) - 1);
 #if defined (TARGET_I386)
-    cpu_register_physical_memory(0x000a0000, 0x20000, vga_io_memory);
-#elif defined (TARGET_PPC)
-    cpu_register_physical_memory(0xf00a0000, 0x20000, vga_io_memory);
+    register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
+    register_ioport_read(0x1cf, 1, 2, vbe_ioport_read_data, s);
+
+    register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
+    register_ioport_write(0x1cf, 1, 2, vbe_ioport_write_data, s);
+
+    /* old Bochs IO ports */
+    register_ioport_read(0xff80, 1, 2, vbe_ioport_read_index, s);
+    register_ioport_read(0xff81, 1, 2, vbe_ioport_read_data, s);
+
+    register_ioport_write(0xff80, 1, 2, vbe_ioport_write_index, s);
+    register_ioport_write(0xff81, 1, 2, vbe_ioport_write_data, s); 
+#else
+    register_ioport_read(0x1ce, 1, 2, vbe_ioport_read_index, s);
+    register_ioport_read(0x1d0, 1, 2, vbe_ioport_read_data, s);
+
+    register_ioport_write(0x1ce, 1, 2, vbe_ioport_write_index, s);
+    register_ioport_write(0x1d0, 1, 2, vbe_ioport_write_data, s);
 #endif
+#endif /* CONFIG_BOCHS_VBE */
+
+    vga_io_memory = cpu_register_io_memory(0, vga_mem_read, vga_mem_write, s);
+    cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, 
+                                 vga_io_memory);
+
+    if (bus) {
+        PCIDevice *d;
+        uint8_t *pci_conf;
+
+        d = pci_register_device(bus, "VGA", 
+                                sizeof(PCIDevice),
+                                -1, NULL, NULL);
+        pci_conf = d->config;
+        pci_conf[0x00] = 0x34; // dummy VGA (same as Bochs ID)
+        pci_conf[0x01] = 0x12;
+        pci_conf[0x02] = 0x11;
+        pci_conf[0x03] = 0x11;
+        pci_conf[0x0a] = 0x00; // VGA controller 
+        pci_conf[0x0b] = 0x03;
+        pci_conf[0x0e] = 0x00; // header_type
+
+        /* XXX: vga_ram_size must be a power of two */
+        pci_register_io_region(d, 0, vga_ram_size, 
+                               PCI_ADDRESS_SPACE_MEM_PREFETCH, vga_map);
+    } else {
+#ifdef CONFIG_BOCHS_VBE
+        /* XXX: use optimized standard vga accesses */
+        cpu_register_physical_memory(VBE_DISPI_LFB_PHYSICAL_ADDRESS, 
+                                     vga_ram_size, vga_ram_offset);
+#endif
+    }
+    return 0;
+}
+
+/********************************************************/
+/* vga screen dump */
+
+static int vga_save_w, vga_save_h;
+
+static void vga_save_dpy_update(DisplayState *s, 
+                                int x, int y, int w, int h)
+{
+}
+
+static void vga_save_dpy_resize(DisplayState *s, int w, int h)
+{
+    s->linesize = w * 4;
+    s->data = qemu_malloc(h * s->linesize);
+    vga_save_w = w;
+    vga_save_h = h;
+}
+
+static void vga_save_dpy_refresh(DisplayState *s)
+{
+}
+
+static int ppm_save(const char *filename, uint8_t *data, 
+                    int w, int h, int linesize)
+{
+    FILE *f;
+    uint8_t *d, *d1;
+    unsigned int v;
+    int y, x;
+
+    f = fopen(filename, "wb");
+    if (!f)
+        return -1;
+    fprintf(f, "P6\n%d %d\n%d\n",
+            w, h, 255);
+    d1 = data;
+    for(y = 0; y < h; y++) {
+        d = d1;
+        for(x = 0; x < w; x++) {
+            v = *(uint32_t *)d;
+            fputc((v >> 16) & 0xff, f);
+            fputc((v >> 8) & 0xff, f);
+            fputc((v) & 0xff, f);
+            d += 4;
+        }
+        d1 += linesize;
+    }
+    fclose(f);
     return 0;
 }
+
+/* save the vga display in a PPM image even if no display is
+   available */
+void vga_screen_dump(const char *filename)
+{
+    VGAState *s = vga_state;
+    DisplayState *saved_ds, ds1, *ds = &ds1;
+    
+    /* XXX: this is a little hackish */
+    vga_invalidate_display();
+    saved_ds = s->ds;
+
+    memset(ds, 0, sizeof(DisplayState));
+    ds->dpy_update = vga_save_dpy_update;
+    ds->dpy_resize = vga_save_dpy_resize;
+    ds->dpy_refresh = vga_save_dpy_refresh;
+    ds->depth = 32;
+
+    s->ds = ds;
+    s->graphic_mode = -1;
+    vga_update_display();
+    
+    if (ds->data) {
+        ppm_save(filename, ds->data, vga_save_w, vga_save_h, 
+                 s->ds->linesize);
+        qemu_free(ds->data);
+    }
+    s->ds = saved_ds;
+}