Add PowerPC power-management state check callback.
[qemu] / hw / cirrus_vga.c
index 655db9d..85bf4a2 100644 (file)
@@ -1,9 +1,9 @@
 /*
  * QEMU Cirrus CLGD 54xx VGA Emulator.
- * 
+ *
  * Copyright (c) 2004 Fabrice Bellard
  * Copyright (c) 2004 Makoto Suzuki (suzu)
- * 
+ *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
  * in the Software without restriction, including without limitation the rights
@@ -31,7 +31,7 @@
 
 /*
  * TODO:
- *    - add support for WRITEMASK (GR2F)
+ *    - destination write mask support not complete (bits 5..7)
  *    - optimize linear mappings
  *    - optimize bitblt functions
  */
@@ -259,9 +259,6 @@ typedef struct CirrusVGAState {
     uint8_t *cirrus_srcptr;
     uint8_t *cirrus_srcptr_end;
     uint32_t cirrus_srccounter;
-    uint8_t *cirrus_dstptr;
-    uint8_t *cirrus_dstptr_end;
-    uint32_t cirrus_dstcounter;
     /* hwcursor display state */
     int last_hw_cursor_size;
     int last_hw_cursor_x;
@@ -269,6 +266,7 @@ typedef struct CirrusVGAState {
     int last_hw_cursor_y_start;
     int last_hw_cursor_y_end;
     int real_vram_size; /* XXX: suppress that */
+    CPUWriteMemoryFunc **cirrus_linear_write;
 } CirrusVGAState;
 
 typedef struct PCICirrusVGAState {
@@ -277,7 +275,7 @@ typedef struct PCICirrusVGAState {
 } PCICirrusVGAState;
 
 static uint8_t rop_to_index[256];
-    
+
 /***************************************
  *
  *  prototypes.
@@ -285,7 +283,8 @@ static uint8_t rop_to_index[256];
  ***************************************/
 
 
-static void cirrus_bitblt_reset(CirrusVGAState * s);
+static void cirrus_bitblt_reset(CirrusVGAState *s);
+static void cirrus_update_memory_access(CirrusVGAState *s);
 
 /***************************************
  *
@@ -403,7 +402,54 @@ static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = {
     cirrus_bitblt_rop_bkwd_notsrc_or_dst,
     cirrus_bitblt_rop_bkwd_notsrc_and_notdst,
 };
-    
+
+#define TRANSP_ROP(name) {\
+    name ## _8,\
+    name ## _16,\
+        }
+#define TRANSP_NOP(func) {\
+    func,\
+    func,\
+        }
+
+static const cirrus_bitblt_rop_t cirrus_fwd_transp_rop[16][2] = {
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_0),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_dst),
+    TRANSP_NOP(cirrus_bitblt_rop_nop),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_1),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_xor_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_notxor_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_bkwd_transp_rop[16][2] = {
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_0),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_dst),
+    TRANSP_NOP(cirrus_bitblt_rop_nop),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_1),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_xor_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_notxor_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_notdst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_dst),
+    TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_notdst),
+};
+
 #define ROP2(name) {\
     name ## _8,\
     name ## _16,\
@@ -456,25 +502,6 @@ static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = {
     ROP2(cirrus_colorexpand_transp_notsrc_and_notdst),
 };
 
-static const cirrus_bitblt_rop_t cirrus_colorexpand_transp_inv[16][4] = {
-    ROP2(cirrus_colorexpand_transp_inv_0),
-    ROP2(cirrus_colorexpand_transp_inv_src_and_dst),
-    ROP_NOP2(cirrus_bitblt_rop_nop),
-    ROP2(cirrus_colorexpand_transp_inv_src_and_notdst),
-    ROP2(cirrus_colorexpand_transp_inv_notdst),
-    ROP2(cirrus_colorexpand_transp_inv_src),
-    ROP2(cirrus_colorexpand_transp_inv_1),
-    ROP2(cirrus_colorexpand_transp_inv_notsrc_and_dst),
-    ROP2(cirrus_colorexpand_transp_inv_src_xor_dst),
-    ROP2(cirrus_colorexpand_transp_inv_src_or_dst),
-    ROP2(cirrus_colorexpand_transp_inv_notsrc_or_notdst),
-    ROP2(cirrus_colorexpand_transp_inv_src_notxor_dst),
-    ROP2(cirrus_colorexpand_transp_inv_src_or_notdst),
-    ROP2(cirrus_colorexpand_transp_inv_notsrc),
-    ROP2(cirrus_colorexpand_transp_inv_notsrc_or_dst),
-    ROP2(cirrus_colorexpand_transp_inv_notsrc_and_notdst),
-};
-
 static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = {
     ROP2(cirrus_colorexpand_0),
     ROP2(cirrus_colorexpand_src_and_dst),
@@ -494,6 +521,44 @@ static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = {
     ROP2(cirrus_colorexpand_notsrc_and_notdst),
 };
 
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = {
+    ROP2(cirrus_colorexpand_pattern_transp_0),
+    ROP2(cirrus_colorexpand_pattern_transp_src_and_dst),
+    ROP_NOP2(cirrus_bitblt_rop_nop),
+    ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst),
+    ROP2(cirrus_colorexpand_pattern_transp_notdst),
+    ROP2(cirrus_colorexpand_pattern_transp_src),
+    ROP2(cirrus_colorexpand_pattern_transp_1),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_src_or_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst),
+    ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst),
+    ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst),
+};
+
+static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = {
+    ROP2(cirrus_colorexpand_pattern_0),
+    ROP2(cirrus_colorexpand_pattern_src_and_dst),
+    ROP_NOP2(cirrus_bitblt_rop_nop),
+    ROP2(cirrus_colorexpand_pattern_src_and_notdst),
+    ROP2(cirrus_colorexpand_pattern_notdst),
+    ROP2(cirrus_colorexpand_pattern_src),
+    ROP2(cirrus_colorexpand_pattern_1),
+    ROP2(cirrus_colorexpand_pattern_notsrc_and_dst),
+    ROP2(cirrus_colorexpand_pattern_src_xor_dst),
+    ROP2(cirrus_colorexpand_pattern_src_or_dst),
+    ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst),
+    ROP2(cirrus_colorexpand_pattern_src_notxor_dst),
+    ROP2(cirrus_colorexpand_pattern_src_or_notdst),
+    ROP2(cirrus_colorexpand_pattern_notsrc),
+    ROP2(cirrus_colorexpand_pattern_notsrc_or_dst),
+    ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst),
+};
+
 static const cirrus_fill_t cirrus_fill[16][4] = {
     ROP2(cirrus_fill_0),
     ROP2(cirrus_fill_src_and_dst),
@@ -525,7 +590,7 @@ static inline void cirrus_bitblt_fgcol(CirrusVGAState *s)
         s->cirrus_blt_fgcol = le16_to_cpu(color);
         break;
     case 3:
-        s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 | 
+        s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 |
             (s->gr[0x11] << 8) | (s->gr[0x13] << 16);
         break;
     default:
@@ -549,7 +614,7 @@ static inline void cirrus_bitblt_bgcol(CirrusVGAState *s)
         s->cirrus_blt_bgcol = le16_to_cpu(color);
         break;
     case 3:
-        s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 | 
+        s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 |
             (s->gr[0x10] << 8) | (s->gr[0x12] << 16);
         break;
     default:
@@ -584,30 +649,11 @@ static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin,
 static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s,
                                            const uint8_t * src)
 {
-    uint8_t work_colorexp[256];
     uint8_t *dst;
-    int patternbytes = s->cirrus_blt_pixelwidth * 8;
 
-    if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
-        cirrus_bitblt_rop_t rop_func;
-        cirrus_bitblt_fgcol(s);
-        cirrus_bitblt_bgcol(s);
-        rop_func = cirrus_colorexpand[CIRRUS_ROP_SRC_INDEX][s->cirrus_blt_pixelwidth - 1];
-        rop_func(s, work_colorexp, src, patternbytes, 1, patternbytes, 8);
-       src = work_colorexp;
-       s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_COLOREXPAND;
-    }
-    if (s->cirrus_blt_mode & ~CIRRUS_BLTMODE_PATTERNCOPY) {
-#ifdef DEBUG_BITBLT
-       printf("cirrus: blt mode %02x (pattercopy) - unimplemented\n",
-              s->cirrus_blt_mode);
-#endif
-       return 0;
-    }
-    
     dst = s->vram_ptr + s->cirrus_blt_dstaddr;
     (*s->cirrus_rop) (s, dst, src,
-                      s->cirrus_blt_dstpitch, 0, 
+                      s->cirrus_blt_dstpitch, 0,
                       s->cirrus_blt_width, s->cirrus_blt_height);
     cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
                              s->cirrus_blt_dstpitch, s->cirrus_blt_width,
@@ -622,7 +668,7 @@ static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
     cirrus_fill_t rop_func;
 
     rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-    rop_func(s, s->vram_ptr + s->cirrus_blt_dstaddr, 
+    rop_func(s, s->vram_ptr + s->cirrus_blt_dstaddr,
              s->cirrus_blt_dstpitch,
              s->cirrus_blt_width, s->cirrus_blt_height);
     cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
@@ -641,19 +687,94 @@ static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop)
 static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s)
 {
     return cirrus_bitblt_common_patterncopy(s,
-                                           s->vram_ptr + 
+                                           s->vram_ptr +
                                             (s->cirrus_blt_srcaddr & ~7));
 }
 
-static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
+static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h)
 {
+    int sx, sy;
+    int dx, dy;
+    int width, height;
+    int depth;
+    int notify = 0;
+
+    depth = s->get_bpp((VGAState *)s) / 8;
+    s->get_resolution((VGAState *)s, &width, &height);
+
+    /* extra x, y */
+    sx = (src % (width * depth)) / depth;
+    sy = (src / (width * depth));
+    dx = (dst % (width *depth)) / depth;
+    dy = (dst / (width * depth));
+
+    /* normalize width */
+    w /= depth;
+
+    /* if we're doing a backward copy, we have to adjust
+       our x/y to be the upper left corner (instead of the lower
+       right corner) */
+    if (s->cirrus_blt_dstpitch < 0) {
+       sx -= (s->cirrus_blt_width / depth) - 1;
+       dx -= (s->cirrus_blt_width / depth) - 1;
+       sy -= s->cirrus_blt_height - 1;
+       dy -= s->cirrus_blt_height - 1;
+    }
+
+    /* are we in the visible portion of memory? */
+    if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 &&
+       (sx + w) <= width && (sy + h) <= height &&
+       (dx + w) <= width && (dy + h) <= height) {
+       notify = 1;
+    }
+
+    /* make to sure only copy if it's a plain copy ROP */
+    if (*s->cirrus_rop != cirrus_bitblt_rop_fwd_src &&
+       *s->cirrus_rop != cirrus_bitblt_rop_bkwd_src)
+       notify = 0;
+
+    /* we have to flush all pending changes so that the copy
+       is generated at the appropriate moment in time */
+    if (notify)
+       vga_hw_update();
+
     (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr,
                      s->vram_ptr + s->cirrus_blt_srcaddr,
                      s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
                      s->cirrus_blt_width, s->cirrus_blt_height);
-    cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
-                            s->cirrus_blt_dstpitch, s->cirrus_blt_width,
-                            s->cirrus_blt_height);
+
+    if (notify)
+       s->ds->dpy_copy(s->ds,
+                       sx, sy, dx, dy,
+                       s->cirrus_blt_width / depth,
+                       s->cirrus_blt_height);
+
+    /* we don't have to notify the display that this portion has
+       changed since dpy_copy implies this */
+
+    if (!notify)
+       cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+                                s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+                                s->cirrus_blt_height);
+}
+
+static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
+{
+    if (s->ds->dpy_copy) {
+       cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->start_addr,
+                      s->cirrus_blt_srcaddr - s->start_addr,
+                      s->cirrus_blt_width, s->cirrus_blt_height);
+    } else {
+       (*s->cirrus_rop) (s, s->vram_ptr + s->cirrus_blt_dstaddr,
+                         s->vram_ptr + s->cirrus_blt_srcaddr,
+                         s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch,
+                         s->cirrus_blt_width, s->cirrus_blt_height);
+
+       cirrus_invalidate_region(s, s->cirrus_blt_dstaddr,
+                                s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+                                s->cirrus_blt_height);
+    }
+
     return 1;
 }
 
@@ -667,7 +788,7 @@ static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s)
 {
     int copy_count;
     uint8_t *end_ptr;
-    
+
     if (s->cirrus_srccounter > 0) {
         if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
             cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf);
@@ -711,9 +832,7 @@ static void cirrus_bitblt_reset(CirrusVGAState * s)
     s->cirrus_srcptr = &s->cirrus_bltbuf[0];
     s->cirrus_srcptr_end = &s->cirrus_bltbuf[0];
     s->cirrus_srccounter = 0;
-    s->cirrus_dstptr = &s->cirrus_bltbuf[0];
-    s->cirrus_dstptr_end = &s->cirrus_bltbuf[0];
-    s->cirrus_dstcounter = 0;
+    cirrus_update_memory_access(s);
 }
 
 static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
@@ -728,23 +847,26 @@ static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
        if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
            s->cirrus_blt_srcpitch = 8;
        } else {
+            /* XXX: check for 24 bpp */
            s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth;
        }
        s->cirrus_srccounter = s->cirrus_blt_srcpitch;
     } else {
        if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
             w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth;
-            if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY) 
+            if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY)
                 s->cirrus_blt_srcpitch = ((w + 31) >> 5);
             else
                 s->cirrus_blt_srcpitch = ((w + 7) >> 3);
        } else {
-           s->cirrus_blt_srcpitch = s->cirrus_blt_width;
+            /* always align input size to 32 bits */
+           s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3;
        }
         s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height;
     }
     s->cirrus_srcptr = s->cirrus_bltbuf;
     s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch;
+    cirrus_update_memory_access(s);
     return 1;
 }
 
@@ -790,8 +912,8 @@ static void cirrus_bitblt_start(CirrusVGAState * s)
     blt_rop = s->gr[0x32];
 
 #ifdef DEBUG_BITBLT
-    printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spicth=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n",
-           blt_rop, 
+    printf("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n",
+           blt_rop,
            s->cirrus_blt_mode,
            s->cirrus_blt_modeext,
            s->cirrus_blt_width,
@@ -800,7 +922,7 @@ static void cirrus_bitblt_start(CirrusVGAState * s)
            s->cirrus_blt_srcpitch,
            s->cirrus_blt_dstaddr,
            s->cirrus_blt_srcaddr,
-           s->sr[0x2f]);
+           s->gr[0x2f]);
 #endif
 
     switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) {
@@ -835,43 +957,68 @@ static void cirrus_bitblt_start(CirrusVGAState * s)
     }
 
     if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) &&
-        (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST | 
+        (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST |
                                CIRRUS_BLTMODE_TRANSPARENTCOMP |
-                               CIRRUS_BLTMODE_PATTERNCOPY | 
-                               CIRRUS_BLTMODE_COLOREXPAND)) == 
+                               CIRRUS_BLTMODE_PATTERNCOPY |
+                               CIRRUS_BLTMODE_COLOREXPAND)) ==
          (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) {
         cirrus_bitblt_fgcol(s);
         cirrus_bitblt_solidfill(s, blt_rop);
     } else {
-        if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND | 
-                                   CIRRUS_BLTMODE_PATTERNCOPY)) == 
+        if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND |
+                                   CIRRUS_BLTMODE_PATTERNCOPY)) ==
             CIRRUS_BLTMODE_COLOREXPAND) {
 
             if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
-                if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) {
+                if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
                     cirrus_bitblt_bgcol(s);
-                    s->cirrus_rop = cirrus_colorexpand_transp_inv[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-                } else {
+                else
                     cirrus_bitblt_fgcol(s);
-                    s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-                }
+                s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
             } else {
                 cirrus_bitblt_fgcol(s);
                 cirrus_bitblt_bgcol(s);
                 s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
             }
         } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) {
-            s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
-        } else {
-            if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
-                s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
-                s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
-                s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]];
+            if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) {
+                if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+                    if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV)
+                        cirrus_bitblt_bgcol(s);
+                    else
+                        cirrus_bitblt_fgcol(s);
+                    s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+                } else {
+                    cirrus_bitblt_fgcol(s);
+                    cirrus_bitblt_bgcol(s);
+                    s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+                }
             } else {
-                s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]];
+                s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
             }
-        }
-        
+        } else {
+           if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) {
+               if (s->cirrus_blt_pixelwidth > 2) {
+                   printf("src transparent without colorexpand must be 8bpp or 16bpp\n");
+                   goto bitblt_ignore;
+               }
+               if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
+                   s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
+                   s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
+                   s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+               } else {
+                   s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1];
+               }
+           } else {
+               if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) {
+                   s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch;
+                   s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch;
+                   s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]];
+               } else {
+                   s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]];
+               }
+           }
+       }
         // setup bitblt engine.
         if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) {
             if (!cirrus_bitblt_cputovideo(s))
@@ -912,13 +1059,13 @@ static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value)
  *
  ***************************************/
 
-static void cirrus_get_offsets(VGAState *s1, 
-                                   uint32_t *pline_offset,
-                                   uint32_t *pstart_addr)
+static void cirrus_get_offsets(VGAState *s1,
+                               uint32_t *pline_offset,
+                               uint32_t *pstart_addr,
+                               uint32_t *pline_compare)
 {
     CirrusVGAState * s = (CirrusVGAState *)s1;
-    uint32_t start_addr;
-    uint32_t line_offset;
+    uint32_t start_addr, line_offset, line_compare;
 
     line_offset = s->cr[0x13]
        | ((s->cr[0x1b] & 0x10) << 4);
@@ -931,6 +1078,11 @@ static void cirrus_get_offsets(VGAState *s1,
        | ((s->cr[0x1b] & 0x0c) << 15)
        | ((s->cr[0x1d] & 0x80) << 12);
     *pstart_addr = start_addr;
+
+    line_compare = s->cr[0x18] |
+        ((s->cr[0x07] & 0x10) << 4) |
+        ((s->cr[0x09] & 0x40) << 3);
+    *pline_compare = line_compare;
 }
 
 static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s)
@@ -996,10 +1148,10 @@ static int cirrus_get_bpp(VGAState *s1)
 static void cirrus_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) | 
+    height = s->cr[0x12] |
+        ((s->cr[0x07] & 0x02) << 7) |
         ((s->cr[0x07] & 0x40) << 3);
     height = (height + 1);
     /* interlace support */
@@ -1030,10 +1182,10 @@ static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index)
     else
        offset <<= 12;
 
-    if (s->vram_size <= offset)
+    if (s->real_vram_size <= offset)
        limit = 0;
     else
-       limit = s->vram_size - offset;
+       limit = s->real_vram_size - offset;
 
     if (((s->gr[0x0b] & 0x01) == 0) && (bank_index != 0)) {
        if (limit > 0x8000) {
@@ -1186,7 +1338,6 @@ cirrus_hook_write_sr(CirrusVGAState * s, unsigned reg_index, int reg_value)
     case 0x14:                 // Scratch Register 2
     case 0x15:                 // Scratch Register 3
     case 0x16:                 // Performance Tuning Register
-    case 0x17:                 // Configuration Readback and Extended Control
     case 0x18:                 // Signature Generator Control
     case 0x19:                 // Signature Generator Result
     case 0x1a:                 // Signature Generator Result
@@ -1201,6 +1352,10 @@ cirrus_hook_write_sr(CirrusVGAState * s, unsigned reg_index, int reg_value)
               reg_index, reg_value);
 #endif
        break;
+    case 0x17:                 // Configuration Readback and Extended Control
+       s->sr[reg_index] = (s->sr[reg_index] & 0x38) | (reg_value & 0xc7);
+        cirrus_update_memory_access(s);
+        break;
     default:
 #ifdef DEBUG_CIRRUS
        printf("cirrus: outport sr_index %02x, sr_value %02x\n", reg_index,
@@ -1335,13 +1490,19 @@ cirrus_hook_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value)
        return CIRRUS_HOOK_NOT_HANDLED;
     case 0x05:                 // Standard VGA, Cirrus extended mode
        s->gr[reg_index] = reg_value & 0x7f;
+        cirrus_update_memory_access(s);
        break;
     case 0x09:                 // bank offset #0
     case 0x0A:                 // bank offset #1
+       s->gr[reg_index] = reg_value;
+       cirrus_update_bank_ptr(s, 0);
+       cirrus_update_bank_ptr(s, 1);
+        break;
     case 0x0B:
        s->gr[reg_index] = reg_value;
        cirrus_update_bank_ptr(s, 0);
        cirrus_update_bank_ptr(s, 1);
+        cirrus_update_memory_access(s);
        break;
     case 0x10:                 // BGCOLOR 0x0000ff00
     case 0x11:                 // FGCOLOR 0x0000ff00
@@ -1492,6 +1653,7 @@ cirrus_hook_write_cr(CirrusVGAState * s, unsigned reg_index, int reg_value)
     case 0x1a:                 // Miscellaneous Control
     case 0x1b:                 // Extended Display Control
     case 0x1c:                 // Sync Adjust and Genlock
+    case 0x1d:                 // Overlay Extended Control
        s->cr[reg_index] = reg_value;
 #ifdef DEBUG_CIRRUS
        printf("cirrus: handled outport cr_index %02x, cr_value %02x\n",
@@ -1503,7 +1665,6 @@ cirrus_hook_write_cr(CirrusVGAState * s, unsigned reg_index, int reg_value)
     case 0x26:                 // Attribute Controller Index Readback (R)
     case 0x27:                 // Part ID (R)
        break;
-    case 0x1d:                 // Overlay Extended Control
     case 0x25:                 // Part Status
     default:
 #ifdef DEBUG_CIRRUS
@@ -1759,11 +1920,12 @@ static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s,
     dst = s->vram_ptr + offset;
     for (x = 0; x < 8; x++) {
        if (val & 0x80) {
-           *dst++ = s->cirrus_shadow_gr1;
+           *dst = s->cirrus_shadow_gr1;
        } else if (mode == 5) {
-           *dst++ = s->cirrus_shadow_gr0;
+           *dst = s->cirrus_shadow_gr0;
        }
        val <<= 1;
+       dst++;
     }
     cpu_physical_memory_set_dirty(s->vram_offset + offset);
     cpu_physical_memory_set_dirty(s->vram_offset + offset + 7);
@@ -1781,13 +1943,14 @@ static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s,
     dst = s->vram_ptr + offset;
     for (x = 0; x < 8; x++) {
        if (val & 0x80) {
-           *dst++ = s->cirrus_shadow_gr1;
-           *dst++ = s->gr[0x11];
+           *dst = s->cirrus_shadow_gr1;
+           *(dst + 1) = s->gr[0x11];
        } else if (mode == 5) {
-           *dst++ = s->cirrus_shadow_gr0;
-           *dst++ = s->gr[0x10];
+           *dst = s->cirrus_shadow_gr0;
+           *(dst + 1) = s->gr[0x10];
        }
        val <<= 1;
+       dst += 2;
     }
     cpu_physical_memory_set_dirty(s->vram_offset + offset);
     cpu_physical_memory_set_dirty(s->vram_offset + offset + 15);
@@ -1873,7 +2036,7 @@ static uint32_t cirrus_vga_mem_readl(void *opaque, target_phys_addr_t addr)
     return v;
 }
 
-static void cirrus_vga_mem_writeb(void *opaque, target_phys_addr_t addr, 
+static void cirrus_vga_mem_writeb(void *opaque, target_phys_addr_t addr,
                                   uint32_t mem_value)
 {
     CirrusVGAState *s = opaque;
@@ -1984,7 +2147,7 @@ static CPUWriteMemoryFunc *cirrus_vga_mem_write[3] = {
 static inline void invalidate_cursor1(CirrusVGAState *s)
 {
     if (s->last_hw_cursor_size) {
-        vga_invalidate_scanlines((VGAState *)s, 
+        vga_invalidate_scanlines((VGAState *)s,
                                  s->last_hw_cursor_y + s->last_hw_cursor_y_start,
                                  s->last_hw_cursor_y + s->last_hw_cursor_y_end);
     }
@@ -2060,7 +2223,7 @@ static void cirrus_cursor_invalidate(VGAState *s1)
         s->last_hw_cursor_y != s->hw_cursor_y) {
 
         invalidate_cursor1(s);
-        
+
         s->last_hw_cursor_size = size;
         s->last_hw_cursor_x = s->hw_cursor_x;
         s->last_hw_cursor_y = s->hw_cursor_y;
@@ -2077,8 +2240,8 @@ static void cirrus_cursor_draw_line(VGAState *s1, uint8_t *d1, int scr_y)
     unsigned int color0, color1;
     const uint8_t *palette, *src;
     uint32_t content;
-    
-    if (!(s->sr[0x12] & CIRRUS_CURSOR_SHOW)) 
+
+    if (!(s->sr[0x12] & CIRRUS_CURSOR_SHOW))
         return;
     /* fast test to see if the cursor intersects with the scan line */
     if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) {
@@ -2089,7 +2252,7 @@ static void cirrus_cursor_draw_line(VGAState *s1, uint8_t *d1, int scr_y)
     if (scr_y < s->hw_cursor_y ||
         scr_y >= (s->hw_cursor_y + h))
         return;
-    
+
     src = s->vram_ptr + s->real_vram_size - 16 * 1024;
     if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) {
         src += (s->sr[0x13] & 0x3c) * 256;
@@ -2119,11 +2282,11 @@ static void cirrus_cursor_draw_line(VGAState *s1, uint8_t *d1, int scr_y)
         x2 = s->last_scr_width;
     w = x2 - x1;
     palette = s->cirrus_hidden_palette;
-    color0 = s->rgb_to_pixel(c6_to_8(palette[0x0 * 3]), 
-                             c6_to_8(palette[0x0 * 3 + 1]), 
+    color0 = s->rgb_to_pixel(c6_to_8(palette[0x0 * 3]),
+                             c6_to_8(palette[0x0 * 3 + 1]),
                              c6_to_8(palette[0x0 * 3 + 2]));
-    color1 = s->rgb_to_pixel(c6_to_8(palette[0xf * 3]), 
-                             c6_to_8(palette[0xf * 3 + 1]), 
+    color1 = s->rgb_to_pixel(c6_to_8(palette[0xf * 3]),
+                             c6_to_8(palette[0xf * 3 + 1]),
                              c6_to_8(palette[0xf * 3 + 2]));
     bpp = ((s->ds->depth + 7) >> 3);
     d1 += x1 * bpp;
@@ -2158,7 +2321,7 @@ static uint32_t cirrus_linear_readb(void *opaque, target_phys_addr_t addr)
 
     addr &= s->cirrus_addr_mask;
 
-    if (((s->sr[0x17] & 0x44) == 0x44) && 
+    if (((s->sr[0x17] & 0x44) == 0x44) &&
         ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) {
        /* memory-mapped I/O */
        ret = cirrus_mmio_blt_read(s, addr & 0xff);
@@ -2216,8 +2379,8 @@ static void cirrus_linear_writeb(void *opaque, target_phys_addr_t addr,
     unsigned mode;
 
     addr &= s->cirrus_addr_mask;
-        
-    if (((s->sr[0x17] & 0x44) == 0x44) && 
+
+    if (((s->sr[0x17] & 0x44) == 0x44) &&
         ((addr & s->linear_mmio_mask) ==  s->linear_mmio_mask)) {
        /* memory-mapped I/O */
        cirrus_mmio_blt_write(s, addr & 0xff, val);
@@ -2291,6 +2454,36 @@ static CPUWriteMemoryFunc *cirrus_linear_write[3] = {
     cirrus_linear_writel,
 };
 
+static void cirrus_linear_mem_writeb(void *opaque, target_phys_addr_t addr,
+                                     uint32_t val)
+{
+    CirrusVGAState *s = (CirrusVGAState *) opaque;
+
+    addr &= s->cirrus_addr_mask;
+    *(s->vram_ptr + addr) = val;
+    cpu_physical_memory_set_dirty(s->vram_offset + addr);
+}
+
+static void cirrus_linear_mem_writew(void *opaque, target_phys_addr_t addr,
+                                     uint32_t val)
+{
+    CirrusVGAState *s = (CirrusVGAState *) opaque;
+
+    addr &= s->cirrus_addr_mask;
+    cpu_to_le16w((uint16_t *)(s->vram_ptr + addr), val);
+    cpu_physical_memory_set_dirty(s->vram_offset + addr);
+}
+
+static void cirrus_linear_mem_writel(void *opaque, target_phys_addr_t addr,
+                                     uint32_t val)
+{
+    CirrusVGAState *s = (CirrusVGAState *) opaque;
+
+    addr &= s->cirrus_addr_mask;
+    cpu_to_le32w((uint32_t *)(s->vram_ptr + addr), val);
+    cpu_physical_memory_set_dirty(s->vram_offset + addr);
+}
+
 /***************************************
  *
  *  system to screen memory access
@@ -2392,6 +2585,37 @@ static CPUWriteMemoryFunc *cirrus_linear_bitblt_write[3] = {
     cirrus_linear_bitblt_writel,
 };
 
+/* Compute the memory access functions */
+static void cirrus_update_memory_access(CirrusVGAState *s)
+{
+    unsigned mode;
+
+    if ((s->sr[0x17] & 0x44) == 0x44) {
+        goto generic_io;
+    } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
+        goto generic_io;
+    } else {
+       if ((s->gr[0x0B] & 0x14) == 0x14) {
+            goto generic_io;
+       } else if (s->gr[0x0B] & 0x02) {
+            goto generic_io;
+        }
+
+       mode = s->gr[0x05] & 0x7;
+       if (mode < 4 || mode > 5 || ((s->gr[0x0B] & 0x4) == 0)) {
+            s->cirrus_linear_write[0] = cirrus_linear_mem_writeb;
+            s->cirrus_linear_write[1] = cirrus_linear_mem_writew;
+            s->cirrus_linear_write[2] = cirrus_linear_mem_writel;
+        } else {
+        generic_io:
+            s->cirrus_linear_write[0] = cirrus_linear_writeb;
+            s->cirrus_linear_write[1] = cirrus_linear_writew;
+            s->cirrus_linear_write[2] = cirrus_linear_writel;
+        }
+    }
+}
+
+
 /* I/O ports */
 
 static uint32_t vga_ioport_read(void *opaque, uint32_t addr)
@@ -2440,7 +2664,11 @@ static uint32_t vga_ioport_read(void *opaque, uint32_t addr)
        case 0x3c7:
            val = s->dac_state;
            break;
-       case 0x3c9:
+       case 0x3c8:
+           val = s->dac_write_index;
+           s->cirrus_hidden_dac_lockindex = 0;
+           break;
+        case 0x3c9:
            if (cirrus_hook_read_palette(s, &val))
                break;
            val = s->palette[s->dac_read_index * 3 + s->dac_sub_index];
@@ -2604,7 +2832,7 @@ static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
        printf("vga: write CR%x = 0x%02x\n", s->cr_index, val);
 #endif
        /* handle CR0-7 protection */
-       if ((s->cr[11] & 0x80) && s->cr_index <= 7) {
+       if ((s->cr[0x11] & 0x80) && s->cr_index <= 7) {
            /* can always write bit 4 of CR7 */
            if (s->cr_index == 7)
                s->cr[7] = (s->cr[7] & ~0x10) | (val & 0x10);
@@ -2616,7 +2844,7 @@ static void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
        case 0x09:
        case 0x0c:
        case 0x0d:
-       case 0x12:              /* veritcal display end */
+       case 0x12:              /* vertical display end */
            s->cr[s->cr_index] = val;
            break;
 
@@ -2736,6 +2964,105 @@ static CPUWriteMemoryFunc *cirrus_mmio_write[3] = {
     cirrus_mmio_writel,
 };
 
+/* load/save state */
+
+static void cirrus_vga_save(QEMUFile *f, void *opaque)
+{
+    CirrusVGAState *s = opaque;
+
+    if (s->pci_dev)
+        pci_device_save(s->pci_dev, f);
+
+    qemu_put_be32s(f, &s->latch);
+    qemu_put_8s(f, &s->sr_index);
+    qemu_put_buffer(f, s->sr, 256);
+    qemu_put_8s(f, &s->gr_index);
+    qemu_put_8s(f, &s->cirrus_shadow_gr0);
+    qemu_put_8s(f, &s->cirrus_shadow_gr1);
+    qemu_put_buffer(f, s->gr + 2, 254);
+    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);
+
+    qemu_put_8s(f, &s->cirrus_hidden_dac_lockindex);
+    qemu_put_8s(f, &s->cirrus_hidden_dac_data);
+
+    qemu_put_be32s(f, &s->hw_cursor_x);
+    qemu_put_be32s(f, &s->hw_cursor_y);
+    /* XXX: we do not save the bitblt state - we assume we do not save
+       the state when the blitter is active */
+}
+
+static int cirrus_vga_load(QEMUFile *f, void *opaque, int version_id)
+{
+    CirrusVGAState *s = opaque;
+    int ret;
+
+    if (version_id > 2)
+        return -EINVAL;
+
+    if (s->pci_dev && version_id >= 2) {
+        ret = pci_device_load(s->pci_dev, f);
+        if (ret < 0)
+            return ret;
+    }
+
+    qemu_get_be32s(f, &s->latch);
+    qemu_get_8s(f, &s->sr_index);
+    qemu_get_buffer(f, s->sr, 256);
+    qemu_get_8s(f, &s->gr_index);
+    qemu_get_8s(f, &s->cirrus_shadow_gr0);
+    qemu_get_8s(f, &s->cirrus_shadow_gr1);
+    s->gr[0x00] = s->cirrus_shadow_gr0 & 0x0f;
+    s->gr[0x01] = s->cirrus_shadow_gr1 & 0x0f;
+    qemu_get_buffer(f, s->gr + 2, 254);
+    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);
+
+    qemu_get_8s(f, &s->cirrus_hidden_dac_lockindex);
+    qemu_get_8s(f, &s->cirrus_hidden_dac_data);
+
+    qemu_get_be32s(f, &s->hw_cursor_x);
+    qemu_get_be32s(f, &s->hw_cursor_y);
+
+    /* force refresh */
+    s->graphic_mode = -1;
+    cirrus_update_bank_ptr(s, 0);
+    cirrus_update_bank_ptr(s, 1);
+    return 0;
+}
+
 /***************************************
  *
  *  initialize
@@ -2783,15 +3110,16 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
     register_ioport_read(0x3ba, 1, 1, vga_ioport_read, s);
     register_ioport_read(0x3da, 1, 1, vga_ioport_read, s);
 
-    vga_io_memory = cpu_register_io_memory(0, cirrus_vga_mem_read, 
+    vga_io_memory = cpu_register_io_memory(0, cirrus_vga_mem_read,
                                            cirrus_vga_mem_write, s);
-    cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000, 
+    cpu_register_physical_memory(isa_mem_base + 0x000a0000, 0x20000,
                                  vga_io_memory);
 
     s->sr[0x06] = 0x0f;
-    s->sr[0x1F] = 0x22;                // MemClock
     if (device_id == CIRRUS_ID_CLGD5446) {
         /* 4MB 64 bit memory config, always PCI */
+        s->sr[0x1F] = 0x2d;            // MemClock
+        s->gr[0x18] = 0x0f;             // fastest memory configuration
 #if 1
         s->sr[0x0f] = 0x98;
         s->sr[0x17] = 0x20;
@@ -2804,8 +3132,9 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
         s->real_vram_size = 2048 * 1024;
 #endif
     } else {
+        s->sr[0x1F] = 0x22;            // MemClock
         s->sr[0x0F] = CIRRUS_MEMSIZE_2M;
-        if (is_pci) 
+        if (is_pci)
             s->sr[0x17] = CIRRUS_BUSTYPE_PCI;
         else
             s->sr[0x17] = CIRRUS_BUSTYPE_ISA;
@@ -2825,6 +3154,8 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
     s->cirrus_linear_io_addr =
        cpu_register_io_memory(0, cirrus_linear_read, cirrus_linear_write,
                               s);
+    s->cirrus_linear_write = cpu_get_io_memory_write(s->cirrus_linear_io_addr);
+
     /* I/O handler for LFB */
     s->cirrus_linear_bitblt_io_addr =
        cpu_register_io_memory(0, cirrus_linear_bitblt_read, cirrus_linear_bitblt_write,
@@ -2843,6 +3174,8 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
     s->get_resolution = cirrus_get_resolution;
     s->cursor_invalidate = cirrus_cursor_invalidate;
     s->cursor_draw_line = cirrus_cursor_draw_line;
+
+    register_savevm("cirrus_vga", 0, 2, cirrus_vga_save, cirrus_vga_load, s);
 }
 
 /***************************************
@@ -2851,14 +3184,14 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
  *
  ***************************************/
 
-void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base, 
+void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
                          unsigned long vga_ram_offset, int vga_ram_size)
 {
     CirrusVGAState *s;
 
     s = qemu_mallocz(sizeof(CirrusVGAState));
-    
-    vga_common_init((VGAState *)s, 
+
+    vga_common_init((VGAState *)s,
                     ds, vga_ram_base, vga_ram_offset, vga_ram_size);
     cirrus_init_common(s, CIRRUS_ID_CLGD5430, 0);
     /* XXX ISA-LFB support */
@@ -2891,20 +3224,20 @@ static void cirrus_pci_mmio_map(PCIDevice *d, int region_num,
                                 s->cirrus_mmio_io_addr);
 }
 
-void pci_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base, 
+void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
                          unsigned long vga_ram_offset, int vga_ram_size)
 {
     PCICirrusVGAState *d;
     uint8_t *pci_conf;
     CirrusVGAState *s;
     int device_id;
-    
+
     device_id = CIRRUS_ID_CLGD5446;
 
     /* setup PCI configuration registers */
-    d = (PCICirrusVGAState *)pci_register_device("Cirrus VGA", 
-                                                 sizeof(PCICirrusVGAState), 
-                                                 0, -1, NULL, NULL);
+    d = (PCICirrusVGAState *)pci_register_device(bus, "Cirrus VGA",
+                                                 sizeof(PCICirrusVGAState),
+                                                 -1, NULL, NULL);
     pci_conf = d->dev.config;
     pci_conf[0x00] = (uint8_t) (PCI_VENDOR_CIRRUS & 0xff);
     pci_conf[0x01] = (uint8_t) (PCI_VENDOR_CIRRUS >> 8);
@@ -2917,10 +3250,14 @@ void pci_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
 
     /* setup VGA */
     s = &d->cirrus_vga;
-    vga_common_init((VGAState *)s, 
+    vga_common_init((VGAState *)s,
                     ds, vga_ram_base, vga_ram_offset, vga_ram_size);
     cirrus_init_common(s, device_id, 1);
 
+    graphic_console_init(s->ds, s->update, s->invalidate, s->screen_dump, s);
+
+    s->pci_dev = (PCIDevice *)d;
+
     /* setup memory space */
     /* memory #0 LFB */
     /* memory #1 memory-mapped I/O */