diff mbox

[RFC,23/23] 40p: Add an 8514/A graphics card

Message ID 1308019077-61957-24-git-send-email-andreas.faerber@web.de
State New
Headers show

Commit Message

Andreas Färber June 14, 2011, 2:37 a.m. UTC
The IBM E15 is equivalent to an S3 Vision864.

Lacking S3 SDAC (86C716) support, the DAC indizes are translated
to greyscale colors. This works sufficiently to observe firmware
boot progress.

Cc: Hervé Poussineau <hpoussin@reactos.org>

Fixed off-by-one drawing issue.
Replaced hardcoded color for RECT.
Separate I/O debug output for readability.

Signed-off-by: Andreas Färber <andreas.faerber@web.de>
---
 Makefile.objs                   |    1 +
 default-configs/ppc-softmmu.mak |    1 +
 hw/pci_ids.h                    |    3 +
 hw/ppc_prep.c                   |    2 +
 hw/vga-s3.c                     |  694 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 701 insertions(+), 0 deletions(-)
 create mode 100644 hw/vga-s3.c

Comments

Roy Tam June 15, 2011, 4:33 a.m. UTC | #1
Hi all,

2011/6/14 Andreas Färber <andreas.faerber@web.de>:
> The IBM E15 is equivalent to an S3 Vision864.
>
> Lacking S3 SDAC (86C716) support, the DAC indizes are translated
> to greyscale colors. This works sufficiently to observe firmware
> boot progress.
>

IMO we can generalize it as a generic S3 Vision864 and use it in x86 guest too.
http://wiki.qemu.org/Google_Summer_of_Code_2011#Add_a_S3_Trio_or_S3_Virge
CC: Natalia Portillo <claunia@claunia.com>

> Cc: Hervé Poussineau <hpoussin@reactos.org>
>
> Fixed off-by-one drawing issue.
> Replaced hardcoded color for RECT.
> Separate I/O debug output for readability.
>
> Signed-off-by: Andreas Färber <andreas.faerber@web.de>
> ---
>  Makefile.objs                   |    1 +
>  default-configs/ppc-softmmu.mak |    1 +
>  hw/pci_ids.h                    |    3 +
>  hw/ppc_prep.c                   |    2 +
>  hw/vga-s3.c                     |  694 +++++++++++++++++++++++++++++++++++++++
>  5 files changed, 701 insertions(+), 0 deletions(-)
>  create mode 100644 hw/vga-s3.c
>
[snip]

IMO using vga-s3-864.c as the file name may be better as S3 produced
many display chips.
Andreas Färber June 15, 2011, 6:11 p.m. UTC | #2
Am 15.06.2011 um 06:33 schrieb Roy Tam:

> 2011/6/14 Andreas Färber <andreas.faerber@web.de>:
>> The IBM E15 is equivalent to an S3 Vision864.
>>
>> Lacking S3 SDAC (86C716) support, the DAC indizes are translated
>> to greyscale colors. This works sufficiently to observe firmware
>> boot progress.
>>
>
> IMO we can generalize it as a generic S3 Vision864 and use it in x86  
> guest too.

What in particular would we need to generalize? It's qdev'ified; I  
don't remember seeing anything PReP-specific in there.
It still needs VMState of course.

> http://wiki.qemu.org/Google_Summer_of_Code_2011#Add_a_S3_Trio_or_S3_Virge
> CC: Natalia Portillo <claunia@claunia.com>
>
>> Cc: Hervé Poussineau <hpoussin@reactos.org>
>>
>> Fixed off-by-one drawing issue.
>> Replaced hardcoded color for RECT.
>> Separate I/O debug output for readability.
>>
>> Signed-off-by: Andreas Färber <andreas.faerber@web.de>
>> ---
>>  Makefile.objs                   |    1 +
>>  default-configs/ppc-softmmu.mak |    1 +
>>  hw/pci_ids.h                    |    3 +
>>  hw/ppc_prep.c                   |    2 +
>>  hw/vga-s3.c                     |  694 ++++++++++++++++++++++++++++ 
>> +++++++++++
>>  5 files changed, 701 insertions(+), 0 deletions(-)
>>  create mode 100644 hw/vga-s3.c
>>
> [snip]
>
> IMO using vga-s3-864.c as the file name may be better as S3 produced
> many display chips.

Actually in the meantime I've already renamed the file to vga- 
ibm8514a.c (better: vga-ibm8514.c) and renamed the registration  
functions to reflect the s3_vision864.

I agree that rather than bundling different graphics cards by one  
vendor we should bundle different implementations of the same chipset.  
I was assuming that the IBM E15 uses a different vendor_id at least  
but could share the initialization with other cards, once mst's  
declarative PCI initialization reaches master (prepared that locally).
Supposedly the Miro 20SD is compatible with the S3 Vision864, and the  
ATI Mach8 was based on IBM 8514/A, too. Not sure about the Weitek  
P9100-based IBM S15.

Any suggestions what to do about the RAMDAC would be appreciated. The  
firmware only accesses the DAC_MASK register but does not write to the  
DAC itself, so for colors the card needs a reset handler that  
initializes the 256-color palette to the right RGB values.

Andreas
Andreas Färber June 16, 2011, 12:05 a.m. UTC | #3
Am 15.06.2011 um 20:11 schrieb Andreas Färber:

> Am 15.06.2011 um 06:33 schrieb Roy Tam:
>
>> 2011/6/14 Andreas Färber <andreas.faerber@web.de>:
>>> The IBM E15 is equivalent to an S3 Vision864.
>>>
>>> Lacking S3 SDAC (86C716) support, the DAC indizes are translated
>>> to greyscale colors. This works sufficiently to observe firmware
>>> boot progress.
>>>
>>
>> IMO we can generalize it as a generic S3 Vision864 and use it in  
>> x86 guest too.
>
> What in particular would we need to generalize? It's qdev'ified; I  
> don't remember seeing anything PReP-specific in there.

Err, be16_to_cpu()?
Roy Tam June 16, 2011, 1:22 a.m. UTC | #4
2011/6/16 Andreas Färber <andreas.faerber@web.de>:
> Am 15.06.2011 um 06:33 schrieb Roy Tam:
>
>> 2011/6/14 Andreas Färber <andreas.faerber@web.de>:
>>>
>>> The IBM E15 is equivalent to an S3 Vision864.
>>>
>>> Lacking S3 SDAC (86C716) support, the DAC indizes are translated
>>> to greyscale colors. This works sufficiently to observe firmware
>>> boot progress.
>>>
>>
>> IMO we can generalize it as a generic S3 Vision864 and use it in x86 guest
>> too.
>
> What in particular would we need to generalize? It's qdev'ified; I don't
> remember seeing anything PReP-specific in there.
> It still needs VMState of course.
>
>> http://wiki.qemu.org/Google_Summer_of_Code_2011#Add_a_S3_Trio_or_S3_Virge
>> CC: Natalia Portillo <claunia@claunia.com>
>>
>>> Cc: Hervé Poussineau <hpoussin@reactos.org>
>>>
>>> Fixed off-by-one drawing issue.
>>> Replaced hardcoded color for RECT.
>>> Separate I/O debug output for readability.
>>>
>>> Signed-off-by: Andreas Färber <andreas.faerber@web.de>
>>> ---
>>>  Makefile.objs                   |    1 +
>>>  default-configs/ppc-softmmu.mak |    1 +
>>>  hw/pci_ids.h                    |    3 +
>>>  hw/ppc_prep.c                   |    2 +
>>>  hw/vga-s3.c                     |  694
>>> +++++++++++++++++++++++++++++++++++++++
>>>  5 files changed, 701 insertions(+), 0 deletions(-)
>>>  create mode 100644 hw/vga-s3.c
>>>
>> [snip]
>>
>> IMO using vga-s3-864.c as the file name may be better as S3 produced
>> many display chips.
>
> Actually in the meantime I've already renamed the file to vga-ibm8514a.c
> (better: vga-ibm8514.c) and renamed the registration functions to reflect
> the s3_vision864.
>

Actually what I mean is that, you may make the code to be more S3
Vision864-ish because S3 Vision864 extends its display capability, not
only 1024*768 4/8bpp.

> I agree that rather than bundling different graphics cards by one vendor we
> should bundle different implementations of the same chipset. I was assuming
> that the IBM E15 uses a different vendor_id at least but could share the
> initialization with other cards, once mst's declarative PCI initialization
> reaches master (prepared that locally).
> Supposedly the Miro 20SD is compatible with the S3 Vision864, and the ATI
> Mach8 was based on IBM 8514/A, too. Not sure about the Weitek P9100-based
> IBM S15.
>
> Any suggestions what to do about the RAMDAC would be appreciated. The
> firmware only accesses the DAC_MASK register but does not write to the DAC
> itself, so for colors the card needs a reset handler that initializes the
> 256-color palette to the right RGB values.
>
> Andreas
diff mbox

Patch

diff --git a/Makefile.objs b/Makefile.objs
index 7bb6b1a..0893c85 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -270,6 +270,7 @@  hw-obj-y += qdev-addr.o
 hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o
 hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o
 hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
+hw-obj-$(CONFIG_VGA_S3) += vga-s3.o
 hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
 hw-obj-$(CONFIG_VMMOUSE) += vmmouse.o
 
diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak
index 303929f..ab74392 100644
--- a/default-configs/ppc-softmmu.mak
+++ b/default-configs/ppc-softmmu.mak
@@ -6,6 +6,7 @@  CONFIG_ISA_MMIO=y
 CONFIG_ESCC=y
 CONFIG_M48T59=y
 CONFIG_VGA_PCI=y
+CONFIG_VGA_S3=y
 CONFIG_SERIAL=y
 CONFIG_PARALLEL=y
 CONFIG_I8254=y
diff --git a/hw/pci_ids.h b/hw/pci_ids.h
index d3bef0e..821421c 100644
--- a/hw/pci_ids.h
+++ b/hw/pci_ids.h
@@ -97,6 +97,9 @@ 
 #define PCI_VENDOR_ID_FREESCALE          0x1957
 #define PCI_DEVICE_ID_MPC8533E           0x0030
 
+#define PCI_VENDOR_ID_S3                 0x5333
+#define PCI_DEVICE_ID_S3_864             0x88c0
+
 #define PCI_VENDOR_ID_INTEL              0x8086
 #define PCI_DEVICE_ID_INTEL_82378        0x0484
 #define PCI_DEVICE_ID_INTEL_82441        0x1237
diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c
index 6ae1635..9085f89 100644
--- a/hw/ppc_prep.c
+++ b/hw/ppc_prep.c
@@ -747,6 +747,8 @@  static void ibm_40p_init(ram_addr_t ram_size,
     qdev_prop_set_uint8(&isa->qdev, "board-identification", 0xfc);
     qdev_init_nofail(&isa->qdev);
 
+    pci_create_simple(pci_bus, PCI_DEVFN(2, 0), "S3-864");
+
     /* Super I/O (parallel + serial ports) */
     isa = isa_create("isa-pc87312");
     qdev_prop_set_chr(&isa->qdev, "parallel", parallel_hds[0]);
diff --git a/hw/vga-s3.c b/hw/vga-s3.c
new file mode 100644
index 0000000..9b0bc13
--- /dev/null
+++ b/hw/vga-s3.c
@@ -0,0 +1,694 @@ 
+/*
+ * QEMU PCI IBM 8514/A Emulator.
+ *
+ * Copyright (c) 2010 Hervé Poussineau
+ * Copyright (c) 2010-2011 Andreas Färber
+ *
+ * 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
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Documentation available at
+ * http://www.datasheetarchive.com/Indexer/Datasheet-06/DSA0091551.html
+ */
+
+#include "console.h"
+#include "pci.h"
+#include "vga_int.h"
+#include "pixel_ops.h"
+
+//#define DEBUG_8514
+//#define DEBUG_8514_IO
+
+#ifdef DEBUG_8514
+#define DPRINTF(fmt, ...) \
+do { printf("8514: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+#ifdef DEBUG_8514_IO
+#define DPRINTF_IO(fmt, ...) \
+do { printf("8514: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF_IO(fmt, ...) do {} while (0)
+#endif
+#define BADF(fmt, ...) \
+do { fprintf(stderr, "8514 ERROR: " fmt , ## __VA_ARGS__);} while (0)
+
+enum {
+    REG_CMD = 0x9AE8,
+    REG_PIX_TRANS = 0xE2E8,
+};
+
+#define GP_STAT_BUSY     0x0200
+
+#define CMD_WRTDATA      0x0001
+#define CMD_PLANAR       0x0002
+#define CMD_LASTPIX      0x0004
+#define CMD_LINETYPE     0x0008
+#define CMD_DRAW         0x0010
+#define CMD_INC_X        0x0020
+#define CMD_YMAJAXIS     0x0040
+#define CMD_INC_Y        0x0080
+#define CMD_PCDATA       0x0100
+#define CMD_16BIT        0x0200
+#define CMD_BYTSEQ       0x1000
+#define CMD_CMD_MASK     0xE000
+
+#define CMD_CMD_NOP    0x0000
+#define CMD_CMD_LINE   0x2000
+#define CMD_CMD_RECT   0x4000
+#define CMD_CMD_RECTV1 0x6000
+#define CMD_CMD_RECTV2 0x8000
+#define CMD_CMD_LINEAF 0xA000
+#define CMD_CMD_BITBLT 0xC000
+
+#define BKGD_MIX_BSS_MASK 0x0060
+enum {
+    BKGD_MIX_BSS_BKGD = 0x0000,
+    BKGD_MIX_BSS_FRGD = 0x0020,
+    BKGD_MIX_BSS_PIX  = 0x0040,
+    BKGD_MIX_BSS_BMP  = 0x0060,
+};
+
+#define FRGD_MIX_FSS_MASK 0x0060
+enum {
+    FRGD_MIX_FSS_BKGD = 0x0000,
+    FRGD_MIX_FSS_FRGD = 0x0020,
+    FRGD_MIX_FSS_PIX  = 0x0040,
+    FRGD_MIX_FSS_BMP  = 0x0060,
+};
+
+#define PIX_CNTL_MIXSEL_MASK    0x00C0
+enum {
+    PIX_CNTL_MIXSEL_FOREMIX = 0x0000,
+    PIX_CNTL_MIXSEL_PATTERN = 0x0040,
+    PIX_CNTL_MIXSEL_VAR     = 0x0080,
+    PIX_CNTL_MIXSEL_TRANS   = 0x00C0,
+};
+
+// 40f3 = CMD_CMD_RECT | CMD_INC_Y | CMD_YMAJAXIS | CMD_INC_X | CMD_DRAW | CMD_PLANAR | CMD_WRTDATA
+// 4331 = CMD_CMD_RECT | CMD_16BIT | CMD_PCDATA | CMD_INC_X | CMD_DRAW | CMD_WRTDATA
+// c0b3 = CMD_CMD_BITBLT | CMD_INC_Y | CMD_INC_X | CMD_DRAW | CMD_PLANAR | CMD_WRTDATA
+
+typedef struct IBM8514State {
+    VGACommonState vga;
+    uint16_t maj_axis, min_axis;
+
+    uint16_t disp_stat; /* 02e8 */
+    uint16_t h_disp; /* 06e8 */
+    uint16_t h_sync_strt; /* 0ae8 */
+    uint16_t h_sync_wid; /* 0ee8 */
+    uint16_t v_total; /* 12e8 */
+    uint16_t v_disp; /* 16e8 */
+    uint16_t v_sync_strt; /* 1ae8 */
+    uint16_t v_sync_wid; /* 1ee8 */
+    uint16_t disp_cntl; /* 22e8 */
+    uint16_t h_total; /* 26e8 */
+    uint16_t subsys_cntl; /* 42e8 (W) */
+    uint16_t subsys_stat; /* 42e8 (R) */
+    uint16_t rom_page_sel; /* 46e8 */
+    uint16_t advfunc_cntl; /* 4ae8 */
+    uint16_t cur_y; /* 82e8 */
+    uint16_t cur_x; /* 86e8 */
+    uint16_t desty_axstep; /* 8ae8 */
+    uint16_t destx_diastp; /* 8ee8 */
+    uint16_t err_term; /* 92e8 */
+    uint16_t maj_axis_pcnt; /* 96e8 */
+    uint16_t gp_stat; /* 9ae8 (R) */
+    uint16_t cmd; /* 9ae8 (W) */
+    uint16_t short_stroke; /* 9ee8 */
+    uint16_t bkgd_color; /* a2e8 */
+    uint16_t frgd_color; /* a6e8 */
+    uint16_t wrt_mask; /* aae8 */
+    uint16_t rd_mask; /* aee8 */
+    uint16_t color_cmp; /* b2e8 */
+    uint16_t bkgd_mix; /* b6e8 */
+    uint16_t frgd_mix; /* bae8 */
+    uint16_t mfc[16]; /* bee8 */
+    uint16_t pix_trans; /* e2e8 */
+} IBM8514State;
+
+#define min_axis_pcnt mfc[0]
+#define scissors_t    mfc[1]
+#define scissors_l    mfc[2]
+#define scissors_b    mfc[3]
+#define scissors_r    mfc[4]
+#define mem_cntl      mfc[5]
+#define pattern_l     mfc[8]
+#define pattern_h     mfc[9]
+#define pix_cntl      mfc[10]
+
+static inline void do_cmd_done(IBM8514State *s)
+{
+    s->gp_stat &= ~GP_STAT_BUSY;
+}
+
+static void do_cmd_write_pixel(IBM8514State *s, uint16_t value)
+{
+    uint16_t maj_axis_pcnt = s->maj_axis_pcnt + 1;
+    uint8_t* p8 = s->vga.vram_ptr + (s->cur_y * 640 + s->cur_x) * 4;
+    int dx = s->cmd & CMD_INC_X ? 1 : -1;
+    int dy = s->cmd & CMD_INC_Y ? 1 : -1;
+
+    if (!(s->gp_stat & GP_STAT_BUSY)) {
+        return;
+    }
+
+    ++s->maj_axis;
+    if ((s->maj_axis < maj_axis_pcnt) ||
+        (s->maj_axis == maj_axis_pcnt && !(s->cmd & CMD_LASTPIX))) {
+        p8[0] = p8[1] = p8[2] = p8[3] = value;
+    }
+    if (s->maj_axis < maj_axis_pcnt) {
+        s->cur_x += dx;
+    } else if (s->maj_axis == maj_axis_pcnt) {
+        if ((maj_axis_pcnt % 2 == 0) || !(s->cmd & CMD_16BIT)) {
+            s->maj_axis = 0;
+        }
+        s->cur_x -= (s->maj_axis_pcnt) * dx;
+        s->cur_y += dy;
+        s->min_axis++;
+        if (s->min_axis == s->min_axis_pcnt + 1) {
+            do_cmd_done(s);
+        }
+    } else {
+        //DPRINTF("%s: (skip - maj_axis = %u, maj_axis_pcnt = %u)\n",
+        //    __func__, s->maj_axis, maj_axis_pcnt);
+        s->maj_axis = 0;
+    }
+}
+
+static uint16_t get_source_operand(IBM8514State *s)
+{
+    switch (s->pix_cntl & PIX_CNTL_MIXSEL_MASK) {
+        case PIX_CNTL_MIXSEL_FOREMIX:
+            switch (s->frgd_mix & FRGD_MIX_FSS_MASK) {
+                case FRGD_MIX_FSS_BKGD:
+                    return s->bkgd_color & 0xff;
+                case FRGD_MIX_FSS_FRGD:
+                    return s->frgd_color & 0xff;
+                default:
+                    BADF("%s: Unimplemented FSS %x\n",
+                         __func__, (s->frgd_mix & FRGD_MIX_FSS_MASK) >> 5);
+                    return 0;
+            }
+        default:
+            BADF("%s: Unimplemented MIXSEL %x\n",
+                 __func__, (s->pix_cntl & PIX_CNTL_MIXSEL_MASK) >> 6);
+            return 0;
+    }
+}
+
+static void do_cmd_init(IBM8514State *s)
+{
+    s->gp_stat |= GP_STAT_BUSY;
+    s->maj_axis = 0;
+    s->min_axis = 0;
+
+    if ((s->cmd & CMD_CMD_MASK) == CMD_CMD_RECT) {
+        DPRINTF("cmd RECT: cur_x=%d cur_y=%d inc_x=%d inc_y=%d width=%d height=%d\n",
+            s->cur_x, s->cur_y,
+            s->cmd & CMD_INC_X ? 1 : -1, s->cmd & CMD_INC_Y ? 1 : -1,
+            s->maj_axis_pcnt, s->min_axis_pcnt);
+
+        if (!(s->cmd & CMD_PCDATA)) {
+            while (s->gp_stat & GP_STAT_BUSY) {
+                do_cmd_write_pixel(s, get_source_operand(s));
+            }
+        }
+    }
+}
+
+static void do_cmd(IBM8514State *s)
+{
+    DPRINTF("%s: execute cmd %04x\n", __func__, s->cmd);
+
+    do_cmd_init(s);
+}
+
+static uint16_t* ibm8514_get_register(IBM8514State *s, uint32_t addr, int is_write, uint32_t* val_if_write)
+{
+    uint16_t *p;
+
+    switch (addr) {
+        case 0x02e8:
+            p = is_write ? &s->h_total : &s->disp_stat;
+            break;
+        case 0x06e8:
+            p = is_write ? &s->h_disp : NULL;
+            break;
+        case 0x0ae8:
+            p = is_write ? &s->h_sync_strt : NULL;
+            break;
+        case 0x0ee8:
+            p = is_write ? &s->h_sync_wid : NULL;
+            break;
+        case 0x12e8:
+            p = is_write ? &s->v_total : NULL;
+            break;
+        case 0x16e8:
+            p = is_write ? &s->v_disp : NULL;
+            break;
+        case 0x1ae8:
+            p = is_write ? &s->v_sync_strt : NULL;
+            break;
+        case 0x1ee8:
+            p = is_write ? &s->v_sync_wid : NULL;
+            break;
+        case 0x22e8:
+            p = is_write ? &s->disp_cntl : NULL;
+            break;
+        case 0x26e8:
+            p = is_write ? NULL: &s->h_total;
+            break;
+        case 0x42e8:
+            p = is_write ? &s->subsys_cntl : &s->subsys_stat;
+            break;
+        case 0x46e8:
+            p = is_write ? &s->rom_page_sel : NULL;
+            break;
+        case 0x4ae8:
+            p = is_write ? &s->advfunc_cntl : NULL;
+            break;
+        case 0x82e8:
+            p = &s->cur_y;
+            break;
+        case 0x86e8:
+            p = &s->cur_x;
+            break;
+        case 0x8ae8:
+            p = is_write ? &s->desty_axstep : NULL;
+            break;
+        case 0x8ee8:
+            p = is_write ? &s->destx_diastp : NULL;
+            break;
+        case 0x92e8:
+            p = &s->err_term;
+            break;
+        case 0x96e8:
+            p = is_write ? &s->maj_axis_pcnt : NULL;
+            break;
+        case 0x9ae8:
+            p = is_write ? &s->cmd : &s->gp_stat;
+            break;
+        case 0x9ee8:
+            p = is_write ? &s->short_stroke : NULL;
+            break;
+        case 0xa2e8:
+            p = is_write ? &s->bkgd_color : NULL;
+            break;
+        case 0xa6e8:
+            p = is_write ? &s->frgd_color : NULL;
+            break;
+        case 0xaae8:
+            p = is_write ? &s->wrt_mask : NULL;
+            break;
+        case 0xaee8:
+            p = is_write ? &s->rd_mask : NULL;
+            break;
+        case 0xb2e8:
+            p = is_write ? &s->color_cmp : NULL;
+            break;
+        case 0xb6e8:
+            p = is_write ? &s->bkgd_mix : NULL;
+            break;
+        case 0xbae8:
+            p = is_write ? &s->frgd_mix : NULL;
+            break;
+        case 0xbee8:
+            if (is_write) {
+                p = &s->mfc[(*val_if_write >> 12) & 0xf];
+                *val_if_write &= 0x0fff;
+            } else {
+                p = NULL;
+            }
+            break;
+        case 0xe2e8:
+            p = &s->pix_trans;
+            break;
+        default:
+            BADF("%s: invalid register at 0x%x\n", __func__, addr);
+            p = NULL;
+            break;
+    }
+
+    return p;
+}
+
+static uint32_t ibm8514_ioport_readb(void *opaque, uint32_t addr)
+{
+    IBM8514State *s = opaque;
+    uint32_t val;
+    uint16_t *p;
+
+    p = ibm8514_get_register(s, addr & ~0x1, 0, NULL);
+
+    if (p) {
+        val = (be16_to_cpu(*p) >> ((~addr & 1) * 8)) & 0xff;
+    } else {
+        val = 0;
+    }
+
+    DPRINTF_IO("%s: read %x at %x\n", __func__, val, addr);
+    return val;
+}
+
+static uint32_t ibm8514_ioport_readw(void *opaque, uint32_t addr)
+{
+    IBM8514State *s = opaque;
+    uint32_t val;
+    uint16_t *p;
+
+    p = ibm8514_get_register(s, addr, 0, NULL);
+
+    if (p) {
+        val = be16_to_cpu(*p);
+    } else {
+        val = 0;
+    }
+
+    DPRINTF_IO("%s: read %x at %x\n", __func__, val, addr);
+    return val;
+}
+
+static void ibm8514_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    IBM8514State *s = opaque;
+    uint16_t *p;
+    uint8_t *c;
+
+    DPRINTF_IO("%s: write %x at %x\n", __func__, val, addr);
+    p = ibm8514_get_register(s, addr & ~0x1, 1, &val);
+
+    if (p) {
+        c = (uint8_t*)p;
+        c[~addr & 1] = val;
+    }
+    if ((addr & ~0x1) == REG_CMD) {
+        do_cmd(s);
+    } else if ((addr & ~0x1) == REG_PIX_TRANS) {
+        BADF("%s: ibm8514: 8-byte PIX_TRANS access (0x%08" PRIx32 ")\n",
+            __func__, addr);
+    }
+}
+
+static void ibm8514_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
+{
+    IBM8514State *s = opaque;
+    uint16_t *p;
+
+    val = cpu_to_be16(val);
+    DPRINTF_IO("%s: write %x at %x\n", __func__, val, addr);
+    p = ibm8514_get_register(s, addr, 1, &val);
+
+    if (p) {
+        *p = val & 0xffff;
+    }
+    if (addr == REG_CMD) {
+        do_cmd(s);
+    } else if (addr == REG_PIX_TRANS) {
+        if (!(s->cmd & CMD_16BIT)) {
+            do_cmd_write_pixel(s, val & 0xff);
+        } else if (s->cmd & CMD_BYTSEQ) {
+            do_cmd_write_pixel(s, val & 0xff);
+            do_cmd_write_pixel(s, val >> 8);
+        } else {
+            do_cmd_write_pixel(s, val >> 8);
+            do_cmd_write_pixel(s, val & 0xff);
+        }
+    }
+}
+
+static void ibm8514_register_port_one(IBM8514State *s, uint32_t addr)
+{
+    register_ioport_read(addr, 1, 1, ibm8514_ioport_readb, s);
+    register_ioport_write(addr, 1, 1, ibm8514_ioport_writeb, s);
+}
+
+static void ibm8514_register_port(IBM8514State *s, uint32_t addr)
+{
+    register_ioport_read(addr, 2, 1, ibm8514_ioport_readb, s);
+    register_ioport_read(addr, 1, 2, ibm8514_ioport_readw, s);
+    register_ioport_write(addr, 2, 1, ibm8514_ioport_writeb, s);
+    register_ioport_write(addr, 1, 2, ibm8514_ioport_writew, s);
+}
+
+static void my_update_display(void *opaque)
+{
+    VGACommonState *s = opaque;
+    int w;
+    uint8_t *vram;
+    uint8_t *data_display, *dd;
+    int x, y;
+    unsigned int (*rgb_to_pixel)(unsigned int r, unsigned int g, unsigned int b);
+
+    if (ds_get_width(s->ds) != 640 || ds_get_height(s->ds) != 480) {
+        qemu_console_resize(s->ds, 640, 480);
+    }
+
+    switch (ds_get_bits_per_pixel(s->ds)) {
+        case 8:
+            rgb_to_pixel = rgb_to_pixel8;
+            w = 1;
+            break;
+        case 15:
+            rgb_to_pixel = rgb_to_pixel15;
+            w = 2;
+            break;
+        case 16:
+            rgb_to_pixel = rgb_to_pixel16;
+            w = 2;
+            break;
+        case 32:
+            rgb_to_pixel = rgb_to_pixel32;
+            w = 4;
+            break;
+        default:
+            BADF("unknown host depth %d\n", ds_get_bits_per_pixel(s->ds));
+            return;
+    }
+
+    vram = s->vram_ptr;
+    /* XXX: out of range in vram? */
+    data_display = dd = ds_get_data(s->ds);
+    for (y = 0; y < 480; y++) {
+        for (x = 0; x < 640; x++) {
+            unsigned int color;
+            color = (*rgb_to_pixel)(vram[0], vram[1], vram[2]);
+            memcpy(dd, &color, w);
+            dd += w;
+            vram += 4;
+        }
+        data_display = dd = data_display + ds_get_linesize(s->ds);
+    }
+
+    dpy_update(s->ds, 0, 0, 640, 480);
+}
+
+static void ibm8514_init(IBM8514State *s)
+{
+    VGACommonState *vga = &s->vga;
+
+    vga->vram_size = 0x200000;
+
+    /* vga + console init */
+    vga_common_init(vga, vga->vram_size);
+    vga_init(vga);
+
+    vga->ds = graphic_console_init(/*vga->update*/my_update_display, vga->invalidate,
+                                   vga->screen_dump, vga->text_update,
+                                   vga);
+
+    ibm8514_register_port(s, 0x02e8);
+    ibm8514_register_port_one(s, 0x02ea);
+    ibm8514_register_port_one(s, 0x02eb);
+    ibm8514_register_port_one(s, 0x02ec);
+    ibm8514_register_port_one(s, 0x02ed);
+    ibm8514_register_port(s, 0x06e8);
+    ibm8514_register_port(s, 0x0ae8);
+    ibm8514_register_port(s, 0x0ee8);
+    ibm8514_register_port(s, 0x12e8);
+    ibm8514_register_port(s, 0x16e8);
+    ibm8514_register_port(s, 0x1ae8);
+    ibm8514_register_port(s, 0x1ee8);
+    ibm8514_register_port(s, 0x22e8);
+    ibm8514_register_port(s, 0x26e8);
+    ibm8514_register_port(s, 0x42e8);
+    ibm8514_register_port(s, 0x46e8);
+    ibm8514_register_port(s, 0x4ae8);
+    ibm8514_register_port(s, 0x82e8);
+    ibm8514_register_port(s, 0x86e8);
+    ibm8514_register_port(s, 0x8ae8);
+    ibm8514_register_port(s, 0x8ee8);
+    ibm8514_register_port(s, 0x92e8);
+    ibm8514_register_port(s, 0x96e8);
+    ibm8514_register_port(s, 0x9ae8);
+    ibm8514_register_port(s, 0x9ae8);
+    ibm8514_register_port(s, 0x9ee8);
+    ibm8514_register_port(s, 0xa2e8);
+    ibm8514_register_port(s, 0xa6e8);
+    ibm8514_register_port(s, 0xaae8);
+    ibm8514_register_port(s, 0xaee8);
+    ibm8514_register_port(s, 0xb2e8);
+    ibm8514_register_port(s, 0xb6e8);
+    ibm8514_register_port(s, 0xbae8);
+    ibm8514_register_port(s, 0xbee8);
+    ibm8514_register_port(s, 0xe2e8);
+
+    cpu_register_physical_memory(isa_mem_base + 0x02800000, vga->vram_size, vga->vram_offset);
+    qemu_register_coalesced_mmio(isa_mem_base + 0x02800000, vga->vram_size);
+}
+
+typedef struct PCIIBM8514State {
+    PCIDevice dev;
+    IBM8514State state;
+} PCIIBM8514State;
+
+static void pci_ibm8514_write_config(PCIDevice *d,
+                                     uint32_t address, uint32_t val, int len)
+{
+    BADF("%s: 0x%08" PRIx32 "\n", __func__, address);
+}
+
+static int pci_ibm8514_init(PCIDevice *dev)
+{
+    PCIIBM8514State *pci = DO_UPCAST(PCIIBM8514State, dev, dev);
+    IBM8514State *s = &pci->state;
+    uint8_t *pci_conf = dev->config;
+
+    DPRINTF("%s\n", __func__);
+
+    pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_S3);
+    pci_config_set_device_id(pci_conf, PCI_DEVICE_ID_S3_864);
+    pci_config_set_class(pci_conf, PCI_CLASS_DISPLAY_VGA);
+
+    ibm8514_init(s);
+
+    return 0;
+}
+
+static PCIDeviceInfo ibm8514_info = {
+    .qdev.name    = "S3-864",
+    .qdev.size    = sizeof(PCIIBM8514State),
+    //.qdev.vmsd    = &vmstate_ibm8514_pci,
+    .init         = pci_ibm8514_init,
+    .config_write = pci_ibm8514_write_config,
+    .qdev.props   = (Property[]) {
+        //DEFINE_PROP_HEX32("bios-offset", PCIVGAState, state.bios_offset, 0),
+        //DEFINE_PROP_HEX32("bios-size",   PCIVGAState, state.bios_size,   0),
+        DEFINE_PROP_END_OF_LIST()
+    }
+};
+
+static void ibm8514_register(void)
+{
+    pci_qdev_register(&ibm8514_info);
+}
+
+device_init(ibm8514_register);
+
+/*
+21:
+  DEVICE_ID
+    BusId = PCI
+    DevId = 0x41d00909 (PNP0909)
+    SerialNum = 0x00000000
+    Flags = 0x000061c5
+      : Output
+      : ConsoleOut
+      : PowerManaged
+      : Disableable
+      : Configurable
+      : Integrated
+      : Enabled
+    BaseType = DisplayController (3)
+    SubType = SVGAController (1)
+    Interface = GeneralSVGA (0)
+  BUS_ACCESS
+    info0 = 0
+    info1 = 112
+  AllocatedOffset  = 0x00000767
+    IRQ: 15
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x102 size 0x1 bytes
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x3b4 size 0x2 bytes
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x3b8 size 0x4 bytes
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x3bf size 0xc bytes
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x3cc size 0x1 bytes
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x3ce size 0x2 bytes
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x3d4 size 0x2 bytes
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x3d8 size 0x5 bytes
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x42e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x46e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x4ae8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x82e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x86e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x8ae8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x8ee8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x92e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x96e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x9ae8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0x9ee8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xa2e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xa6e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xaae8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xaee8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xb2e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xb6e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xbae8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xbee8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xe2e8 size 0x1 bytes				ok
+    LargeVendorItem: Generic Address
+      I/O address (32 bits), at 0xe2ea size 0x1 bytes
+    LargeVendorItem: Generic Address
+      Memory address (32 bits), at 0x2800000 size 0x200000 bytes	ok
+    LargeVendorItem: Display
+      01 00 80 02 e0 01 80 02 00 00 80 c2 00 00 00 00 
+      00 00 20 00 00 00 00 00 78 
+  PossibleOffset   = 0x00000a58
+  CompatibleOffset = 0x00000a59
+
+*/