PS2 mouse and keyboard separation (Paul Brook)
[qemu] / hw / cirrus_vga.c
index 55a866a..934cde9 100644 (file)
@@ -31,8 +31,7 @@
 
 /*
  * TODO:
- *    - add support for WRITEMASK (GR2F)
- *    - add support for scanline modulo in pattern fill
+ *    - destination write mask support not complete (bits 5..7)
  *    - optimize linear mappings
  *    - optimize bitblt functions
  */
@@ -233,6 +232,7 @@ typedef struct CirrusVGAState {
     int cirrus_linear_bitblt_io_addr;
     int cirrus_mmio_io_addr;
     uint32_t cirrus_addr_mask;
+    uint32_t linear_mmio_mask;
     uint8_t cirrus_shadow_gr0;
     uint8_t cirrus_shadow_gr1;
     uint8_t cirrus_hidden_dac_lockindex;
@@ -259,15 +259,14 @@ 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;
     int last_hw_cursor_y;
     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 {
@@ -284,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);
 
 /***************************************
  *
@@ -417,6 +417,25 @@ static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = {
     func,\
         }
 
+static const cirrus_bitblt_rop_t cirrus_patternfill[16][4] = {
+    ROP2(cirrus_patternfill_0),
+    ROP2(cirrus_patternfill_src_and_dst),
+    ROP_NOP2(cirrus_bitblt_rop_nop),
+    ROP2(cirrus_patternfill_src_and_notdst),
+    ROP2(cirrus_patternfill_notdst),
+    ROP2(cirrus_patternfill_src),
+    ROP2(cirrus_patternfill_1),
+    ROP2(cirrus_patternfill_notsrc_and_dst),
+    ROP2(cirrus_patternfill_src_xor_dst),
+    ROP2(cirrus_patternfill_src_or_dst),
+    ROP2(cirrus_patternfill_notsrc_or_notdst),
+    ROP2(cirrus_patternfill_src_notxor_dst),
+    ROP2(cirrus_patternfill_src_or_notdst),
+    ROP2(cirrus_patternfill_notsrc),
+    ROP2(cirrus_patternfill_notsrc_or_dst),
+    ROP2(cirrus_patternfill_notsrc_and_notdst),
+};
+
 static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = {
     ROP2(cirrus_colorexpand_transp_0),
     ROP2(cirrus_colorexpand_transp_src_and_dst),
@@ -436,25 +455,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),
@@ -474,6 +474,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),
@@ -564,46 +602,15 @@ 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;
-    uint8_t *dstc;
-    int x, y;
-    int tilewidth, tileheight;
-    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;
-    for (y = 0; y < s->cirrus_blt_height; y += 8) {
-       dstc = dst;
-       tileheight = qemu_MIN(8, s->cirrus_blt_height - y);
-       for (x = 0; x < s->cirrus_blt_width; x += patternbytes) {
-           tilewidth = qemu_MIN(patternbytes, s->cirrus_blt_width - x);
-           (*s->cirrus_rop) (s, dstc, src,
-                             s->cirrus_blt_dstpitch, patternbytes,
-                             tilewidth, tileheight);
-           dstc += patternbytes;
-       }
-       dst += s->cirrus_blt_dstpitch * 8;
-    }
+    (*s->cirrus_rop) (s, dst, src,
+                      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,
-                            s->cirrus_blt_height);
+                             s->cirrus_blt_dstpitch, s->cirrus_blt_width,
+                             s->cirrus_blt_height);
     return 1;
 }
 
@@ -633,8 +640,8 @@ 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->cirrus_blt_srcaddr);
+                                           s->vram_ptr + 
+                                            (s->cirrus_blt_srcaddr & ~7));
 }
 
 static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s)
@@ -703,9 +710,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)
@@ -720,6 +725,7 @@ 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;
@@ -731,12 +737,14 @@ static int cirrus_bitblt_cputovideo(CirrusVGAState * s)
             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;
 }
 
@@ -782,7 +790,7 @@ 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",
+    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,
@@ -792,7 +800,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) {
@@ -840,18 +848,32 @@ static void cirrus_bitblt_start(CirrusVGAState * s)
             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) {
+            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_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;
@@ -983,6 +1005,22 @@ static int cirrus_get_bpp(VGAState *s1)
     return ret;
 }
 
+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) | 
+        ((s->cr[0x07] & 0x40) << 3);
+    height = (height + 1);
+    /* interlace support */
+    if (s->cr[0x1a] & 0x01)
+        height = height * 2;
+    *pwidth = width;
+    *pheight = height;
+}
+
 /***************************************
  *
  * bank memory
@@ -1004,10 +1042,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) {
@@ -1160,7 +1198,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
@@ -1175,6 +1212,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,
@@ -1309,13 +1350,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
@@ -1466,6 +1513,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",
@@ -1477,7 +1525,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
@@ -1733,11 +1780,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);
@@ -1755,13 +1803,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);
@@ -1970,7 +2019,7 @@ static inline void cirrus_cursor_compute_yrange(CirrusVGAState *s)
     uint32_t content;
     int y, y_min, y_max;
 
-    src = s->vram_ptr + 0x200000 - 16 * 1024;
+    src = s->vram_ptr + s->real_vram_size - 16 * 1024;
     if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) {
         src += (s->sr[0x13] & 0x3c) * 256;
         y_min = 64;
@@ -2064,7 +2113,7 @@ static void cirrus_cursor_draw_line(VGAState *s1, uint8_t *d1, int scr_y)
         scr_y >= (s->hw_cursor_y + h))
         return;
     
-    src = s->vram_ptr + 0x200000 - 16 * 1024;
+    src = s->vram_ptr + s->real_vram_size - 16 * 1024;
     if (s->sr[0x12] & CIRRUS_CURSOR_LARGE) {
         src += (s->sr[0x13] & 0x3c) * 256;
         src += (scr_y - s->hw_cursor_y) * 16;
@@ -2130,10 +2179,10 @@ static uint32_t cirrus_linear_readb(void *opaque, target_phys_addr_t addr)
     CirrusVGAState *s = (CirrusVGAState *) opaque;
     uint32_t ret;
 
-    /* XXX: s->vram_size must be a power of two */
     addr &= s->cirrus_addr_mask;
 
-    if (((s->sr[0x17] & 0x44) == 0x44) && ((addr & 0x1fff00) == 0x1fff00)) {
+    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);
     } else if (0) {
@@ -2190,8 +2239,9 @@ 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) && ((addr & 0x1fff00) == 0x1fff00)) {
+        
+    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);
     } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
@@ -2264,6 +2314,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
@@ -2365,6 +2445,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)
@@ -2413,7 +2524,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];
@@ -2577,7 +2692,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);
@@ -2709,13 +2824,102 @@ static CPUWriteMemoryFunc *cirrus_mmio_write[3] = {
     cirrus_mmio_writel,
 };
 
+/* load/save state */
+
+static void cirrus_vga_save(QEMUFile *f, void *opaque)
+{
+    CirrusVGAState *s = opaque;
+
+    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;
+
+    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, 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
  *
  ***************************************/
 
-static void cirrus_init_common(CirrusVGAState * s, int device_id)
+static void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci)
 {
     int vga_io_memory, i;
     static int inited;
@@ -2762,11 +2966,37 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id)
                                  vga_io_memory);
 
     s->sr[0x06] = 0x0f;
-    s->sr[0x0F] = CIRRUS_MEMSIZE_2M;
-    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;
+        s->sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */
+        s->real_vram_size = 4096 * 1024;
+#else
+        s->sr[0x0f] = 0x18;
+        s->sr[0x17] = 0x20;
+        s->sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */
+        s->real_vram_size = 2048 * 1024;
+#endif
+    } else {
+        s->sr[0x1F] = 0x22;            // MemClock
+        s->sr[0x0F] = CIRRUS_MEMSIZE_2M;
+        if (is_pci) 
+            s->sr[0x17] = CIRRUS_BUSTYPE_PCI;
+        else
+            s->sr[0x17] = CIRRUS_BUSTYPE_ISA;
+        s->real_vram_size = 2048 * 1024;
+        s->sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */
+    }
     s->cr[0x27] = device_id;
 
+    /* Win2K seems to assume that the pattern buffer is at 0xff
+       initially ! */
+    memset(s->vram_ptr, 0xff, s->real_vram_size);
+
     s->cirrus_hidden_dac_lockindex = 5;
     s->cirrus_hidden_dac_data = 0;
 
@@ -2774,6 +3004,8 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id)
     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,
@@ -2784,12 +3016,16 @@ static void cirrus_init_common(CirrusVGAState * s, int device_id)
        cpu_register_io_memory(0, cirrus_mmio_read, cirrus_mmio_write, s);
 
     /* XXX: s->vram_size must be a power of two */
-    s->cirrus_addr_mask = s->vram_size - 1;
+    s->cirrus_addr_mask = s->real_vram_size - 1;
+    s->linear_mmio_mask = s->real_vram_size - 256;
 
     s->get_bpp = cirrus_get_bpp;
     s->get_offsets = cirrus_get_offsets;
+    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, 1, cirrus_vga_save, cirrus_vga_load, s);
 }
 
 /***************************************
@@ -2807,8 +3043,7 @@ void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
     
     vga_common_init((VGAState *)s, 
                     ds, vga_ram_base, vga_ram_offset, vga_ram_size);
-    cirrus_init_common(s, CIRRUS_ID_CLGD5430);
-    s->sr[0x17] = CIRRUS_BUSTYPE_ISA;
+    cirrus_init_common(s, CIRRUS_ID_CLGD5430, 0);
     /* XXX ISA-LFB support */
 }
 
@@ -2839,7 +3074,7 @@ 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;
@@ -2850,9 +3085,9 @@ void pci_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
     device_id = CIRRUS_ID_CLGD5446;
 
     /* setup PCI configuration registers */
-    d = (PCICirrusVGAState *)pci_register_device("Cirrus VGA", 
+    d = (PCICirrusVGAState *)pci_register_device(bus, "Cirrus VGA", 
                                                  sizeof(PCICirrusVGAState), 
-                                                 0, -1, NULL, NULL);
+                                                 -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);
@@ -2867,8 +3102,7 @@ void pci_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
     s = &d->cirrus_vga;
     vga_common_init((VGAState *)s, 
                     ds, vga_ram_base, vga_ram_offset, vga_ram_size);
-    cirrus_init_common(s, device_id);
-    s->sr[0x17] = CIRRUS_BUSTYPE_PCI;
+    cirrus_init_common(s, device_id, 1);
 
     /* setup memory space */
     /* memory #0 LFB */