Full VGA support, including old CGA modes, VGA planar and mode X
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Fri, 8 Aug 2003 23:50:57 +0000 (23:50 +0000)
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Fri, 8 Aug 2003 23:50:57 +0000 (23:50 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@346 c046a42c-6fe2-441c-8c8c-71466251a162

hw/vga.c
hw/vga_template.h

index 97270bf..0ed8abf 100644 (file)
--- a/hw/vga.c
+++ b/hw/vga.c
@@ -49,6 +49,7 @@
 #include "thunk.h"
 
 //#define DEBUG_VGA
+//#define DEBUG_VGA_MEM
 
 #define MSR_COLOR_EMULATION 0x01
 #define MSR_PAGE_SELECT     0x20
@@ -85,7 +86,8 @@ typedef struct VGAState {
     DisplayState *ds;
     uint32_t font_offsets[2];
     int graphic_mode;
-    int shift_control;
+    uint8_t shift_control;
+    uint8_t double_scan;
     uint32_t line_offset;
     uint32_t line_compare;
     uint32_t start_addr;
@@ -93,10 +95,11 @@ typedef struct VGAState {
     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 (132 * 60)
+#define CH_ATTR_SIZE (160 * 100)
     uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */
 } VGAState;
 
@@ -199,6 +202,7 @@ static const uint32_t dmask4[4] = {
 
 static uint32_t expand4[256];
 static uint16_t expand2[256];
+static uint8_t expand4to8[16];
 
 VGAState vga_state;
 int vga_io_memory;
@@ -503,7 +507,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
     int memory_map_mode, plane, write_mode, b, func_select;
     uint32_t write_mask, bit_mask, set_mask;
 
-#ifdef DEBUG_VGA
+#ifdef DEBUG_VGA_MEM
     printf("vga: [0x%x] = 0x%02x\n", addr, val);
 #endif
     /* convert to VGA memory offset */
@@ -533,7 +537,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
         plane = addr & 3;
         if (s->sr[2] & (1 << plane)) {
             s->vram_ptr[addr] = val;
-#ifdef DEBUG_VGA
+#ifdef DEBUG_VGA_MEM
             printf("vga: chain4: [0x%x]\n", addr);
 #endif
             s->vram_updated[addr >> 12] = 1;
@@ -544,7 +548,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
         if (s->sr[2] & (1 << plane)) {
             addr = ((addr & ~1) << 1) | plane;
             s->vram_ptr[addr] = val;
-#ifdef DEBUG_VGA
+#ifdef DEBUG_VGA_MEM
             printf("vga: odd/even: [0x%x]\n", addr);
 #endif
             s->vram_updated[addr >> 12] = 1;
@@ -615,7 +619,7 @@ void vga_mem_writeb(uint32_t addr, uint32_t val)
         ((uint32_t *)s->vram_ptr)[addr] = 
             (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) | 
             (val & write_mask);
-#ifdef DEBUG_VGA
+#ifdef DEBUG_VGA_MEM
             printf("vga: latch: [0x%x] mask=0x%08x val=0x%08x\n", 
                    addr * 4, write_mask, val);
 #endif
@@ -699,28 +703,43 @@ static inline int c6_to_8(int v)
     return (v << 2) | (b << 1) | b;
 }
 
+static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b)
+{
+    unsigned int col;
+    col = rgb_to_pixel8(r, g, b);
+    col |= col << 8;
+    col |= col << 16;
+    return col;
+}
+
+static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b)
+{
+    unsigned int col;
+    col = rgb_to_pixel15(r, g, b);
+    col |= col << 16;
+    return col;
+}
+
+static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b)
+{
+    unsigned int col;
+    col = rgb_to_pixel16(r, g, b);
+    col |= col << 16;
+    return col;
+}
+
+static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b)
+{
+    unsigned int col;
+    col = rgb_to_pixel32(r, g, b);
+    return col;
+}
+
 /* return true if the palette was modified */
 static int update_palette16(VGAState *s)
 {
-    int full_update, i, depth;
+    int full_update, i;
     uint32_t v, col, *palette;
-    unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned b);
-    depth = s->ds->depth;
-    switch(depth) {
-    case 8:
-        rgb_to_pixel = rgb_to_pixel8;
-        break;
-    case 15:
-        rgb_to_pixel = rgb_to_pixel15;
-        break;
-    default:
-    case 16:
-        rgb_to_pixel = rgb_to_pixel16;
-        break;
-    case 32:
-        rgb_to_pixel = rgb_to_pixel32;
-        break;
-    }
 
     full_update = 0;
     palette = s->last_palette;
@@ -731,21 +750,35 @@ static int update_palette16(VGAState *s)
         else
             v = ((s->ar[0x14] & 0xc) << 4) | (v & 0x3f);
         v = v * 3;
-        col = rgb_to_pixel(c6_to_8(s->palette[v]), 
-                           c6_to_8(s->palette[v + 1]), 
-                           c6_to_8(s->palette[v + 2]));
-        
-        if (depth == 8) {
-            col |= col << 8;
-            col |= col << 16;
-        } else if (depth <= 16) {
-            col |= col << 16;
+        col = s->rgb_to_pixel(c6_to_8(s->palette[v]), 
+                              c6_to_8(s->palette[v + 1]), 
+                              c6_to_8(s->palette[v + 2]));
+        if (col != palette[i]) {
+            full_update = 1;
+            palette[i] = col;
         }
-        //        printf("%2d: %08x\n", i, col);
+    }
+    return full_update;
+}
+
+/* return true if the palette was modified */
+static int update_palette256(VGAState *s)
+{
+    int full_update, i;
+    uint32_t v, col, *palette;
+
+    full_update = 0;
+    palette = s->last_palette;
+    v = 0;
+    for(i = 0; i < 256; i++) {
+        col = s->rgb_to_pixel(c6_to_8(s->palette[v]), 
+                              c6_to_8(s->palette[v + 1]), 
+                              c6_to_8(s->palette[v + 2]));
         if (col != palette[i]) {
             full_update = 1;
             palette[i] = col;
         }
+        v += 3;
     }
     return full_update;
 }
@@ -806,6 +839,13 @@ static vga_draw_glyph8_func *vga_draw_glyph8_table[4] = {
     vga_draw_glyph8_32,
 };
 
+static vga_draw_glyph8_func *vga_draw_glyph16_table[4] = {
+    vga_draw_glyph16_8,
+    vga_draw_glyph16_16,
+    vga_draw_glyph16_16,
+    vga_draw_glyph16_32,
+};
+
 static vga_draw_glyph9_func *vga_draw_glyph9_table[4] = {
     vga_draw_glyph9_8,
     vga_draw_glyph9_16,
@@ -882,12 +922,19 @@ static void vga_draw_text(VGAState *s, int full_update)
     cw = 8;
     if (s->sr[1] & 0x01)
         cw = 9;
+    if (s->sr[1] & 0x08)
+        cw = 16; /* NOTE: no 18 pixel wide */
     x_incr = cw * ((s->ds->depth + 7) >> 3);
     width = (s->cr[0x01] + 1);
-    height = s->cr[0x12] | 
-        ((s->cr[0x07] & 0x02) << 7) | 
-        ((s->cr[0x07] & 0x40) << 3);
-    height = (height + 1) / cheight;
+    if (s->cr[0x06] == 100) {
+        /* ugly hack for CGA 160x100x16 - explain me the logic */
+        height = 100;
+    } else {
+        height = s->cr[0x12] | 
+            ((s->cr[0x07] & 0x02) << 7) | 
+            ((s->cr[0x07] & 0x40) << 3);
+        height = (height + 1) / cheight;
+    }
     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);
@@ -914,7 +961,10 @@ static void vga_draw_text(VGAState *s, int full_update)
     cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4;
     
     depth_index = get_depth_index(s->ds->depth);
-    vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
+    if (cw == 16)
+        vga_draw_glyph8 = vga_draw_glyph16_table[depth_index];
+    else
+        vga_draw_glyph8 = vga_draw_glyph8_table[depth_index];
     vga_draw_glyph9 = vga_draw_glyph9_table[depth_index];
     
     dest = s->ds->data;
@@ -944,7 +994,7 @@ static void vga_draw_text(VGAState *s, int full_update)
                 font_ptr += 32 * 4 * ch;
                 bgcol = palette[cattr >> 4];
                 fgcol = palette[cattr & 0x0f];
-                if (cw == 8) {
+                if (cw != 9) {
                     vga_draw_glyph8(d1, linesize, 
                                     font_ptr, cheight, fgcol, bgcol);
                 } else {
@@ -966,7 +1016,7 @@ static void vga_draw_text(VGAState *s, int full_update)
                     if (line_last >= line_start && line_start < cheight) {
                         h = line_last - line_start + 1;
                         d = d1 + linesize * line_start;
-                        if (cw == 8) {
+                        if (cw != 9) {
                             vga_draw_glyph8(d, linesize, 
                                             cursor_glyph, h, fgcol, bgcol);
                         } else {
@@ -989,17 +1039,45 @@ static void vga_draw_text(VGAState *s, int full_update)
     }
 }
 
-static vga_draw_line_func *vga_draw_line_table[4 * 6] = {
+enum {
+    VGA_DRAW_LINE2,
+    VGA_DRAW_LINE2D2,
+    VGA_DRAW_LINE4,
+    VGA_DRAW_LINE4D2,
+    VGA_DRAW_LINE8D2,
+    VGA_DRAW_LINE8,
+    VGA_DRAW_LINE15,
+    VGA_DRAW_LINE16,
+    VGA_DRAW_LINE32,
+    VGA_DRAW_LINE_NB,
+};
+
+static vga_draw_line_func *vga_draw_line_table[4 * VGA_DRAW_LINE_NB] = {
     vga_draw_line2_8,
     vga_draw_line2_16,
     vga_draw_line2_16,
     vga_draw_line2_32,
 
+    vga_draw_line2d2_8,
+    vga_draw_line2d2_16,
+    vga_draw_line2d2_16,
+    vga_draw_line2d2_32,
+
     vga_draw_line4_8,
     vga_draw_line4_16,
     vga_draw_line4_16,
     vga_draw_line4_32,
 
+    vga_draw_line4d2_8,
+    vga_draw_line4d2_16,
+    vga_draw_line4d2_16,
+    vga_draw_line4d2_32,
+
+    vga_draw_line8d2_8,
+    vga_draw_line8d2_16,
+    vga_draw_line8d2_16,
+    vga_draw_line8d2_32,
+
     vga_draw_line8_8,
     vga_draw_line8_16,
     vga_draw_line8_16,
@@ -1029,14 +1107,13 @@ static vga_draw_line_func *vga_draw_line_table[4 * 6] = {
  */
 static void vga_draw_graphic(VGAState *s, int full_update)
 {
-    int y, update, page_min, page_max, linesize, y_start;
+    int y1, y, update, page_min, page_max, linesize, y_start, double_scan, mask;
     int width, height, shift_control, line_offset, page0, page1, bwidth;
+    int disp_width;
     uint8_t *d;
     uint32_t v, addr1, addr;
     vga_draw_line_func *vga_draw_line;
-
-    full_update |= update_palette16(s);
-
+    
     full_update |= update_basic_params(s);
 
     width = (s->cr[0x01] + 1) * 8;
@@ -1044,30 +1121,53 @@ static void vga_draw_graphic(VGAState *s, int full_update)
         ((s->cr[0x07] & 0x02) << 7) | 
         ((s->cr[0x07] & 0x40) << 3);
     height = (height + 1);
-
-    if (width != s->last_width ||
-        height != s->last_height) {
-        dpy_resize(s->ds, width, height);
-        s->last_width = width;
-        s->last_height = height;
-        full_update = 1;
-    }
-
+    disp_width = width;
+    
     shift_control = (s->gr[0x05] >> 5) & 3;
-    if (shift_control != s->shift_control) {
+    double_scan = (s->cr[0x09] & 0x80);
+    if (shift_control != s->shift_control ||
+        double_scan != s->double_scan) {
         full_update = 1;
         s->shift_control = shift_control;
+        s->double_scan = double_scan;
     }
     
-    if (shift_control == 0)
-        v = 1; /* 4 bit/pixel */
-    else if (shift_control == 1)
-        v = 0; /* 2 bit/pixel */
-    else
-        v = 2; /* 8 bit/pixel */
+    if (shift_control == 0) {
+        full_update |= update_palette16(s);
+        if (s->sr[0x01] & 8) {
+            v = VGA_DRAW_LINE4D2;
+            disp_width <<= 1;
+        } else {
+            v = VGA_DRAW_LINE4;
+        }
+    } else if (shift_control == 1) {
+        full_update |= update_palette16(s);
+        if (s->sr[0x01] & 8) {
+            v = VGA_DRAW_LINE2D2;
+            disp_width <<= 1;
+        } else {
+            v = VGA_DRAW_LINE2;
+        }
+    } else {
+        full_update |= update_palette256(s);
+        v = VGA_DRAW_LINE8D2;
+        double_scan = 1; /* XXX: explain me why it is always activated */
+    }
     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_width = disp_width;
+        s->last_height = height;
+        full_update = 1;
+    }
+
     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",
+           width, height, v, line_offset, s->cr[9], s->cr[0x17], s->line_compare, s->sr[0x01]);
+#endif
     addr1 = (s->start_addr * 4);
     bwidth = width * 4;
     y_start = -1;
@@ -1075,14 +1175,17 @@ static void vga_draw_graphic(VGAState *s, int full_update)
     page_max = -1;
     d = s->ds->data;
     linesize = s->ds->linesize;
+    y1 = 0;
     for(y = 0; y < height; y++) {
         addr = addr1;
         if (!(s->cr[0x17] & 1)) {
+            int shift;
             /* CGA compatibility handling */
-            addr = (addr & ~0x2000) | ((y & 1) << 13);
+            shift = 14 + ((s->cr[0x17] >> 6) & 1);
+            addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift);
         }
         if (!(s->cr[0x17] & 2)) {
-            addr = (addr & ~0x4000) | ((y & 2) << 13);
+            addr = (addr & ~0x8000) | ((y1 & 2) << 14);
         }
         page0 = addr >> 12;
         page1 = (addr + bwidth - 1) >> 12;
@@ -1103,21 +1206,26 @@ static void vga_draw_graphic(VGAState *s, int full_update)
             if (y_start >= 0) {
                 /* flush to display */
                 dpy_update(s->ds, 0, y_start, 
-                           width, y - y_start);
+                           disp_width, y - y_start);
                 y_start = -1;
             }
         }
-        if (y == s->line_compare) {
-            addr1 = 0;
-        } else {
-            addr1 += line_offset;
+        if (!double_scan || (y & 1) != 0) {
+            if (y1 == s->line_compare) {
+                addr1 = 0;
+            } else {
+                mask = (s->cr[0x17] & 3) ^ 3;
+                if ((y1 & mask) == mask)
+                    addr1 += line_offset; 
+            }
+            y1++;
         }
         d += linesize;
     }
     if (y_start >= 0) {
         /* flush to display */
         dpy_update(s->ds, 0, y_start, 
-                   width, y - y_start);
+                   disp_width, y - y_start);
     }
     /* reset modified pages */
     if (page_max != -1) {
@@ -1199,7 +1307,7 @@ int vga_init(DisplayState *ds, uint8_t *vga_ram_base,
              unsigned long vga_ram_offset, int vga_ram_size)
 {
     VGAState *s = &vga_state;
-    int i, j, v;
+    int i, j, v, b;
 
     for(i = 0;i < 256; i++) {
         v = 0;
@@ -1214,9 +1322,34 @@ int vga_init(DisplayState *ds, uint8_t *vga_ram_base,
         }
         expand2[i] = v;
     }
+    for(i = 0; i < 16; i++) {
+        v = 0;
+        for(j = 0; j < 4; j++) {
+            b = ((i >> j) & 1);
+            v |= b << (2 * j);
+            v |= b << (2 * j + 1);
+        }
+        expand4to8[i] = v;
+    }
 
     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;
index 2f658b4..ac2e625 100644 (file)
 
 #if DEPTH != 15
 
-static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
-                                          const uint8_t *font_ptr, int h,
-                                          uint32_t fgcol, uint32_t bgcol)
+static inline void glue(vga_draw_glyph_line_, DEPTH)(uint8_t *d, 
+                                                     uint32_t font_data,
+                                                     uint32_t xorcol, 
+                                                     uint32_t bgcol)
 {
-    uint32_t font_data, xorcol;
-    
-    xorcol = bgcol ^ fgcol;
-    do {
-        font_data = font_ptr[0];
 #if BPP == 1
         ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
         ((uint32_t *)d)[3] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
@@ -64,6 +60,38 @@ static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
         ((uint32_t *)d)[6] = ((-(font_data >> 1) & 1) & xorcol) ^ bgcol;
         ((uint32_t *)d)[7] = ((-(font_data >> 0) & 1) & xorcol) ^ bgcol;
 #endif
+}
+
+static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize,
+                                          const uint8_t *font_ptr, int h,
+                                          uint32_t fgcol, uint32_t bgcol)
+{
+    uint32_t font_data, xorcol;
+    
+    xorcol = bgcol ^ fgcol;
+    do {
+        font_data = font_ptr[0];
+        glue(vga_draw_glyph_line_, DEPTH)(d, font_data, xorcol, bgcol);
+        font_ptr += 4;
+        d += linesize;
+    } while (--h);
+}
+
+static void glue(vga_draw_glyph16_, DEPTH)(uint8_t *d, int linesize,
+                                          const uint8_t *font_ptr, int h,
+                                          uint32_t fgcol, uint32_t bgcol)
+{
+    uint32_t font_data, xorcol;
+    
+    xorcol = bgcol ^ fgcol;
+    do {
+        font_data = font_ptr[0];
+        glue(vga_draw_glyph_line_, DEPTH)(d, 
+                                          expand4to8[font_data >> 4], 
+                                          xorcol, bgcol);
+        glue(vga_draw_glyph_line_, DEPTH)(d + 8 * BPP, 
+                                          expand4to8[font_data & 0x0f], 
+                                          xorcol, bgcol);
         font_ptr += 4;
         d += linesize;
     } while (--h);
@@ -151,6 +179,48 @@ static void glue(vga_draw_line2_, DEPTH)(VGAState *s1, uint8_t *d,
     }
 }
 
+#if BPP == 1
+#define PUT_PIXEL2(d, n, v) ((uint16_t *)d)[(n)] = (v)
+#elif BPP == 2
+#define PUT_PIXEL2(d, n, v) ((uint32_t *)d)[(n)] = (v)
+#else
+#define PUT_PIXEL2(d, n, v) \
+((uint32_t *)d)[2*(n)] = ((uint32_t *)d)[2*(n)+1] = (v)
+#endif
+
+/* 
+ * 4 color mode, dup2 horizontal
+ */
+static void glue(vga_draw_line2d2_, DEPTH)(VGAState *s1, uint8_t *d, 
+                                           const uint8_t *s, int width)
+{
+    uint32_t plane_mask, *palette, data, v;
+    int x;
+
+    palette = s1->last_palette;
+    plane_mask = mask16[s1->ar[0x12] & 0xf];
+    width >>= 3;
+    for(x = 0; x < width; x++) {
+        data = ((uint32_t *)s)[0];
+        data &= plane_mask;
+        v = expand2[GET_PLANE(data, 0)];
+        v |= expand2[GET_PLANE(data, 2)] << 2;
+        PUT_PIXEL2(d, 0, palette[v >> 12]);
+        PUT_PIXEL2(d, 1, palette[(v >> 8) & 0xf]);
+        PUT_PIXEL2(d, 2, palette[(v >> 4) & 0xf]);
+        PUT_PIXEL2(d, 3, palette[(v >> 0) & 0xf]);
+
+        v = expand2[GET_PLANE(data, 1)];
+        v |= expand2[GET_PLANE(data, 3)] << 2;
+        PUT_PIXEL2(d, 4, palette[v >> 12]);
+        PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
+        PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
+        PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
+        d += BPP * 16;
+        s += 4;
+    }
+}
+
 /* 
  * 16 color mode
  */
@@ -184,7 +254,62 @@ static void glue(vga_draw_line4_, DEPTH)(VGAState *s1, uint8_t *d,
 }
 
 /* 
- * 256 color mode
+ * 16 color mode, dup2 horizontal
+ */
+static void glue(vga_draw_line4d2_, DEPTH)(VGAState *s1, uint8_t *d, 
+                                           const uint8_t *s, int width)
+{
+    uint32_t plane_mask, data, v, *palette;
+    int x;
+
+    palette = s1->last_palette;
+    plane_mask = mask16[s1->ar[0x12] & 0xf];
+    width >>= 3;
+    for(x = 0; x < width; x++) {
+        data = ((uint32_t *)s)[0];
+        data &= plane_mask;
+        v = expand4[GET_PLANE(data, 0)];
+        v |= expand4[GET_PLANE(data, 1)] << 1;
+        v |= expand4[GET_PLANE(data, 2)] << 2;
+        v |= expand4[GET_PLANE(data, 3)] << 3;
+        PUT_PIXEL2(d, 0, palette[v >> 28]);
+        PUT_PIXEL2(d, 1, palette[(v >> 24) & 0xf]);
+        PUT_PIXEL2(d, 2, palette[(v >> 20) & 0xf]);
+        PUT_PIXEL2(d, 3, palette[(v >> 16) & 0xf]);
+        PUT_PIXEL2(d, 4, palette[(v >> 12) & 0xf]);
+        PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]);
+        PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]);
+        PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]);
+        d += BPP * 16;
+        s += 4;
+    }
+}
+
+/* 
+ * 256 color mode, double pixels
+ *
+ * XXX: add plane_mask support (never used in standard VGA modes)
+ */
+static void glue(vga_draw_line8d2_, DEPTH)(VGAState *s1, uint8_t *d, 
+                                           const uint8_t *s, int width)
+{
+    uint32_t *palette;
+    int x;
+
+    palette = s1->last_palette;
+    width >>= 3;
+    for(x = 0; x < width; x++) {
+        PUT_PIXEL2(d, 0, palette[s[0]]);
+        PUT_PIXEL2(d, 1, palette[s[1]]);
+        PUT_PIXEL2(d, 2, palette[s[2]]);
+        PUT_PIXEL2(d, 3, palette[s[3]]);
+        d += BPP * 8;
+        s += 4;
+    }
+}
+
+/* 
+ * standard 256 color mode
  *
  * XXX: add plane_mask support (never used in standard VGA modes)
  */
@@ -289,6 +414,7 @@ static void glue(vga_draw_line32_, DEPTH)(VGAState *s1, uint8_t *d,
 #endif
 }
 
+#undef PUT_PIXEL2
 #undef DEPTH
 #undef BPP
 #undef PIXEL_TYPE