diff mbox

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

Message ID 1308182564-11748-1-git-send-email-andreas.faerber@web.de
State New
Headers show

Commit Message

Andreas Färber June 16, 2011, 12:02 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.
Start cleaning up the naming s3 vs. ibm8514.
Prepare support for DAC_MASK, DAC_R_INDEX, DAC_W_INDEX, DAC_DATA regs.

Cc: Roy Tam <roytam@gmail.com>
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-ibm8514.c                |  780 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 787 insertions(+), 0 deletions(-)
 create mode 100644 hw/vga-ibm8514.c

Comments

Blue Swirl June 18, 2011, 8:42 p.m. UTC | #1
On Thu, Jun 16, 2011 at 3:02 AM, Andreas Färber <andreas.faerber@web.de> wrote:
> 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.
> Start cleaning up the naming s3 vs. ibm8514.
> Prepare support for DAC_MASK, DAC_R_INDEX, DAC_W_INDEX, DAC_DATA regs.
>
> Cc: Roy Tam <roytam@gmail.com>
> 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-ibm8514.c                |  780 +++++++++++++++++++++++++++++++++++++++
>  5 files changed, 787 insertions(+), 0 deletions(-)
>  create mode 100644 hw/vga-ibm8514.c
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 7ceeee5..95dcd91 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_IBM8514) += vga-ibm8514.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..f9c97b7 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_IBM8514=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..c215b0f 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-vision864");
> +
>     /* 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-ibm8514.c b/hw/vga-ibm8514.c
> new file mode 100644
> index 0000000..a87afe1
> --- /dev/null
> +++ b/hw/vga-ibm8514.c
> @@ -0,0 +1,780 @@
> +/*
> + * 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

C89 comments?

> +
> +typedef struct IBM8514State {
> +    VGACommonState vga;
> +    uint16_t maj_axis, min_axis;
> +
> +    uint8_t dac_mask; /* 02ea */
> +    uint8_t dac_r_index; /* 02eb */
> +    uint8_t dac_w_index; /* 02ec */
> +    uint8_t dac_state[4];
> +
> +    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 dac_byte dac_state[3]
> +
> +#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 VMStateDescription vmstate_ibm8514 = {
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .fields = (VMStateField []) {
> +        VMSTATE_END_OF_LIST()
> +    },
> +};
> +
> +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 uint32_t ibm8514_ramdac_ioport_readb(void *opaque, uint32_t addr)
> +{
> +    IBM8514State *s = opaque;
> +    uint32_t val;
> +
> +    switch (addr) {
> +        case 0x02ea:
> +            val = s->dac_mask;
> +            break;
> +        case 0x02eb:
> +            val = s->dac_r_index;
> +            break;
> +        case 0x02ec:
> +            val = s->dac_w_index;
> +            break;
> +        case 0x02ed:
> +            if (s->dac_byte == 0) {
> +                // XXX load dac_state[0-2] from palette
> +            }
> +            val = s->dac_state[s->dac_byte];
> +            s->dac_byte = (s->dac_byte + 1) % 3;
> +            if (s->dac_byte == 0) {
> +                s->dac_r_index++;
> +                s->dac_w_index++;
> +            }
> +            break;
> +        default:
> +            BADF("%s: invalid register at 0x%04x\n", __func__, addr);
> +            val = 0;
> +            break;
> +    }
> +    DPRINTF_IO("%s: read %02x at %04x\n", __func__, val, addr);
> +    return val;
> +}
> +
> +static void ibm8514_ramdac_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
> +{
> +    IBM8514State *s = opaque;
> +
> +    DPRINTF_IO("%s: write %02x at %04x\n", __func__, val, addr);
> +    switch (addr) {
> +        case 0x02ea:
> +            s->dac_mask = val & 0xff;
> +            break;
> +        case 0x02eb:
> +            s->dac_r_index = val & 0xff;
> +            s->dac_byte = 0;
> +            break;
> +        case 0x02ec:
> +            s->dac_w_index = val & 0xff;
> +            s->dac_byte = 0;
> +            break;
> +        case 0x02ed:
> +            s->dac_state[s->dac_byte] = val & 0xff;
> +            s->dac_byte = (s->dac_byte + 1) % 3;
> +            if (s->dac_byte == 0) {
> +                // XXX store s->dac_state[0-2] to palette
> +                s->dac_r_index++;
> +                s->dac_w_index++;
> +            }
> +            break;
> +        default:
> +            BADF("%s: invalid register at 0x%04x\n", __func__, addr);
> +            break;
> +    }
> +}
> +
> +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_ramdac_port(IBM8514State *s, uint32_t addr)
> +{
> +    register_ioport_read(addr, 1, 1, ibm8514_ramdac_ioport_readb, s);
> +    register_ioport_write(addr, 1, 1, ibm8514_ramdac_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);

Please take a look at tcx.c for a 8 bit mode frame buffer with palette
translation. Also VGA_DIRTY bit handling should be added to this loop
to speed it up.

> +            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_ramdac_port(s, 0x02ea);
> +    ibm8514_register_ramdac_port(s, 0x02eb);
> +    ibm8514_register_ramdac_port(s, 0x02ec);
> +    ibm8514_register_ramdac_port(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 s3_vision864_write_config(PCIDevice *d,
> +                                      uint32_t address, uint32_t val, int len)
> +{
> +    BADF("%s: 0x%08" PRIx32 "\n", __func__, address);
> +}
> +
> +static int s3_vision864_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 s3_vision864_info = {
> +    .qdev.name    = "s3-vision864",
> +    .qdev.size    = sizeof(PCIIBM8514State),
> +    .qdev.vmsd    = &vmstate_ibm8514,
> +    // XXX these depend on mst's PCI tree
> +#if 0
> +    .vendor_id    = PCI_VENDOR_ID_S3,
> +    .device_id    = PCI_DEVICE_ID_S3_864,
> +    .class_id     = PCI_CLASS_DISPLAY_VGA,
> +#endif
> +    .init         = s3_vision864_init,
> +    .config_write = s3_vision864_write_config,
> +    .qdev.props   = (Property[]) {
> +        DEFINE_PROP_END_OF_LIST()
> +    },
> +};
> +
> +static void ibm8514_register(void)
> +{
> +    pci_qdev_register(&s3_vision864_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
> +
> +*/
> --
> 1.7.5.3
>
>
>
Andreas Färber June 19, 2011, 10:04 a.m. UTC | #2
Am 18.06.2011 um 22:42 schrieb Blue Swirl:

> On Thu, Jun 16, 2011 at 3:02 AM, Andreas Färber <andreas.faerber@web.de 
> > wrote:
>> 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.
>> Start cleaning up the naming s3 vs. ibm8514.
>> Prepare support for DAC_MASK, DAC_R_INDEX, DAC_W_INDEX, DAC_DATA  
>> regs.
>>
>> Cc: Roy Tam <roytam@gmail.com>
>> 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-ibm8514.c                |  780 ++++++++++++++++++++++++++++ 
>> +++++++++++
>>  5 files changed, 787 insertions(+), 0 deletions(-)
>>  create mode 100644 hw/vga-ibm8514.c

>> diff --git a/hw/vga-ibm8514.c b/hw/vga-ibm8514.c
>> new file mode 100644
>> index 0000000..a87afe1
>> --- /dev/null
>> +++ b/hw/vga-ibm8514.c

>> +// 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
>
> C89 comments?

Yeah, and it could use an explanation: It's a decode table for debug  
output from the command register.

>> +static VMStateDescription vmstate_ibm8514 = {

Missing .name - crashes in some pstrcpy() without it. Better error  
handling would be nice.

>> +    .version_id = 1,
>> +    .minimum_version_id = 1,
>> +    .fields = (VMStateField []) {
>> +        VMSTATE_END_OF_LIST()
>> +    },
>> +};

>> +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);
>
> Please take a look at tcx.c for a 8 bit mode frame buffer with palette
> translation. Also VGA_DIRTY bit handling should be added to this loop
> to speed it up.

Will look into it.

I doubt this is causing the long delays though.
* There's an unhandled write to the PCI card's config address 0x4, for  
which I have no documentation.
* Generally, there are some unhandled writel to 0x680, which look like  
IBM progress codes (but I didn't find a manual to decode them -  
Hervé?), and
* a frequent writeb to 0x690 with value 0x1 or 0x3 (some activity LED  
maybe?).
I thought it might be trying to access the missing NCR 53C810 SCSI but  
saw no indication of that.

Andreas
Hervé Poussineau June 19, 2011, 12:10 p.m. UTC | #3
Andreas Färber a écrit :
> Am 18.06.2011 um 22:42 schrieb Blue Swirl:
>> Please take a look at tcx.c for a 8 bit mode frame buffer with palette
>> translation. Also VGA_DIRTY bit handling should be added to this loop
>> to speed it up.
>
> Will look into it.
>
> I doubt this is causing the long delays though.
> * There's an unhandled write to the PCI card's config address 0x4, for 
> which I have no documentation.
I don't have any documentation either for this graphic card :(

> * Generally, there are some unhandled writel to 0x680, which look like 
> IBM progress codes (but I didn't find a manual to decode them - 
> Hervé?), and
Port 0x680 is a diagnostic port, related to what appears in the 
"operator display".
32 bit values are written to this port, and first byte meaning can be 
found in this document : http://ps-2.kev009.com:8081/rsinfo/Fcodes.htm
With that, you can see that first long step is 0x09, ie "Copy CRC 
verification code to RAM"

> * a frequent writeb to 0x690 with value 0x1 or 0x3 (some activity LED 
> maybe?).
> I thought it might be trying to access the missing NCR 53C810 SCSI but 
> saw no indication of that.
The operator display/operator panel also contains 2 LEDs: one for Power 
On, and the other one for Disk Drive Activity. You might indeed have 
found the I/O port to control them.

Hervé
Blue Swirl June 19, 2011, 1:27 p.m. UTC | #4
On Sun, Jun 19, 2011 at 1:04 PM, Andreas Färber <andreas.faerber@web.de> wrote:
> Am 18.06.2011 um 22:42 schrieb Blue Swirl:
>
>> On Thu, Jun 16, 2011 at 3:02 AM, Andreas Färber <andreas.faerber@web.de>
>> wrote:
>>>
>>> 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.
>>> Start cleaning up the naming s3 vs. ibm8514.
>>> Prepare support for DAC_MASK, DAC_R_INDEX, DAC_W_INDEX, DAC_DATA regs.
>>>
>>> Cc: Roy Tam <roytam@gmail.com>
>>> 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-ibm8514.c                |  780
>>> +++++++++++++++++++++++++++++++++++++++
>>>  5 files changed, 787 insertions(+), 0 deletions(-)
>>>  create mode 100644 hw/vga-ibm8514.c
>
>>> diff --git a/hw/vga-ibm8514.c b/hw/vga-ibm8514.c
>>> new file mode 100644
>>> index 0000000..a87afe1
>>> --- /dev/null
>>> +++ b/hw/vga-ibm8514.c
>
>>> +// 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
>>
>> C89 comments?
>
> Yeah, and it could use an explanation: It's a decode table for debug output
> from the command register.
>
>>> +static VMStateDescription vmstate_ibm8514 = {
>
> Missing .name - crashes in some pstrcpy() without it. Better error handling
> would be nice.
>
>>> +    .version_id = 1,
>>> +    .minimum_version_id = 1,
>>> +    .fields = (VMStateField []) {
>>> +        VMSTATE_END_OF_LIST()
>>> +    },
>>> +};
>
>>> +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);
>>
>> Please take a look at tcx.c for a 8 bit mode frame buffer with palette
>> translation. Also VGA_DIRTY bit handling should be added to this loop
>> to speed it up.
>
> Will look into it.
>
> I doubt this is causing the long delays though.

The difference is that only areas which have been written after last
update are copied to display instead of updating the whole screen
every time. IIRC for TCX it was a major speedup.

> * There's an unhandled write to the PCI card's config address 0x4, for which
> I have no documentation.
> * Generally, there are some unhandled writel to 0x680, which look like IBM
> progress codes (but I didn't find a manual to decode them - Hervé?), and
> * a frequent writeb to 0x690 with value 0x1 or 0x3 (some activity LED
> maybe?).
> I thought it might be trying to access the missing NCR 53C810 SCSI but saw
> no indication of that.
>
> Andreas
Andreas Färber June 19, 2011, 6:40 p.m. UTC | #5
Am 19.06.2011 um 15:27 schrieb Blue Swirl:

> On Sun, Jun 19, 2011 at 1:04 PM, Andreas Färber <andreas.faerber@web.de 
> > wrote:
>> Am 18.06.2011 um 22:42 schrieb Blue Swirl:
>>
>>> On Thu, Jun 16, 2011 at 3:02 AM, Andreas Färber <andreas.faerber@web.de 
>>> >
>>> wrote:
>>>>
>>>> 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.
>>>> Start cleaning up the naming s3 vs. ibm8514.
>>>> Prepare support for DAC_MASK, DAC_R_INDEX, DAC_W_INDEX, DAC_DATA  
>>>> regs.
>>>>
>>>> Cc: Roy Tam <roytam@gmail.com>
>>>> 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-ibm8514.c                |  780
>>>> +++++++++++++++++++++++++++++++++++++++
>>>>  5 files changed, 787 insertions(+), 0 deletions(-)
>>>>  create mode 100644 hw/vga-ibm8514.c
>>
>>>> diff --git a/hw/vga-ibm8514.c b/hw/vga-ibm8514.c
>>>> new file mode 100644
>>>> index 0000000..a87afe1
>>>> --- /dev/null
>>>> +++ b/hw/vga-ibm8514.c

>>>> +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);
>>>
>>> Please take a look at tcx.c for a 8 bit mode frame buffer with  
>>> palette
>>> translation. Also VGA_DIRTY bit handling should be added to this  
>>> loop
>>> to speed it up.
>>
>> Will look into it.
>>
>> I doubt this is causing the long delays though.
>
> The difference is that only areas which have been written after last
> update are copied to display instead of updating the whole screen
> every time. IIRC for TCX it was a major speedup.

Some remarks:

* TCX seems to assume that TARGET_HOST_PAGE == 4 * 1024 in the  
unrolled loop in tcx_update_display(). You might want to assert that.

* The check page < page_min with page_min == -1 seems unintended in  
tcx_update_display() and would have undesired effects on resetting the  
dirty bit. I used page_min < 0 instead.

* The reset code seems wrong wrt dirty bit: after zero-ing VRAM the  
dirty bit should be set, not reset. Otherwise the screen is not redrawn.

Andreas
Blue Swirl June 19, 2011, 7:03 p.m. UTC | #6
On Sun, Jun 19, 2011 at 9:40 PM, Andreas Färber <andreas.faerber@web.de> wrote:
> Am 19.06.2011 um 15:27 schrieb Blue Swirl:
>
>> On Sun, Jun 19, 2011 at 1:04 PM, Andreas Färber <andreas.faerber@web.de>
>> wrote:
>>>
>>> Am 18.06.2011 um 22:42 schrieb Blue Swirl:
>>>
>>>> On Thu, Jun 16, 2011 at 3:02 AM, Andreas Färber <andreas.faerber@web.de>
>>>> wrote:
>>>>>
>>>>> 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.
>>>>> Start cleaning up the naming s3 vs. ibm8514.
>>>>> Prepare support for DAC_MASK, DAC_R_INDEX, DAC_W_INDEX, DAC_DATA regs.
>>>>>
>>>>> Cc: Roy Tam <roytam@gmail.com>
>>>>> 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-ibm8514.c                |  780
>>>>> +++++++++++++++++++++++++++++++++++++++
>>>>>  5 files changed, 787 insertions(+), 0 deletions(-)
>>>>>  create mode 100644 hw/vga-ibm8514.c
>>>
>>>>> diff --git a/hw/vga-ibm8514.c b/hw/vga-ibm8514.c
>>>>> new file mode 100644
>>>>> index 0000000..a87afe1
>>>>> --- /dev/null
>>>>> +++ b/hw/vga-ibm8514.c
>
>>>>> +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);
>>>>
>>>> Please take a look at tcx.c for a 8 bit mode frame buffer with palette
>>>> translation. Also VGA_DIRTY bit handling should be added to this loop
>>>> to speed it up.
>>>
>>> Will look into it.
>>>
>>> I doubt this is causing the long delays though.
>>
>> The difference is that only areas which have been written after last
>> update are copied to display instead of updating the whole screen
>> every time. IIRC for TCX it was a major speedup.
>
> Some remarks:
>
> * TCX seems to assume that TARGET_HOST_PAGE == 4 * 1024 in the unrolled loop
> in tcx_update_display(). You might want to assert that.

TCX was only used on Sparc32. Though some Ultra machines had SBus,
there the page size would be 8k (also bigger pages, but that is what
QEMU uses for TARGET_PAGE_SIZE).

> * The check page < page_min with page_min == -1 seems unintended in
> tcx_update_display() and would have undesired effects on resetting the dirty
> bit. I used page_min < 0 instead.

No, the check is correct since the type of page_min is ram_addr_t
(unsigned long).

> * The reset code seems wrong wrt dirty bit: after zero-ing VRAM the dirty
> bit should be set, not reset. Otherwise the screen is not redrawn.

Actually reset should not touch VRAM, the screen is cleared by OpenBIOS.
Andreas Färber June 19, 2011, 9:38 p.m. UTC | #7
Am 19.06.2011 um 21:03 schrieb Blue Swirl:

> On Sun, Jun 19, 2011 at 9:40 PM, Andreas Färber <andreas.faerber@web.de 
> > wrote:
>> Some remarks:
>>
>> * TCX seems to assume that TARGET_HOST_PAGE == 4 * 1024 in the  
>> unrolled loop
>> in tcx_update_display(). You might want to assert that.
>
> TCX was only used on Sparc32. Though some Ultra machines had SBus,
> there the page size would be 8k (also bigger pages, but that is what
> QEMU uses for TARGET_PAGE_SIZE).

Well, I wasn't saying it doesn't work presently, just pointing out  
that a qdev device might be instantiated by someone under  
circumstances not envisioned.

>> * The check page < page_min with page_min == -1 seems unintended in
>> tcx_update_display() and would have undesired effects on resetting  
>> the dirty
>> bit. I used page_min < 0 instead.
>
> No, the check is correct since the type of page_min is ram_addr_t
> (unsigned long).

OK

>> * The reset code seems wrong wrt dirty bit: after zero-ing VRAM the  
>> dirty
>> bit should be set, not reset. Otherwise the screen is not redrawn.
>
> Actually reset should not touch VRAM, the screen is cleared by  
> OpenBIOS.

tcx_reset() does do a memset on the VRAM.

In my case, I like sharing code, so I'm calling ibm8514_reset() on  
init to initialize registers and palette - I personally prefer a black  
screen during the one and a half minutes before the screen gets  
updated the first time.

Andreas
diff mbox

Patch

diff --git a/Makefile.objs b/Makefile.objs
index 7ceeee5..95dcd91 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_IBM8514) += vga-ibm8514.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..f9c97b7 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_IBM8514=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..c215b0f 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-vision864");
+
     /* 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-ibm8514.c b/hw/vga-ibm8514.c
new file mode 100644
index 0000000..a87afe1
--- /dev/null
+++ b/hw/vga-ibm8514.c
@@ -0,0 +1,780 @@ 
+/*
+ * 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;
+
+    uint8_t dac_mask; /* 02ea */
+    uint8_t dac_r_index; /* 02eb */
+    uint8_t dac_w_index; /* 02ec */
+    uint8_t dac_state[4];
+
+    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 dac_byte dac_state[3]
+
+#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 VMStateDescription vmstate_ibm8514 = {
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField []) {
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+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 uint32_t ibm8514_ramdac_ioport_readb(void *opaque, uint32_t addr)
+{
+    IBM8514State *s = opaque;
+    uint32_t val;
+
+    switch (addr) {
+        case 0x02ea:
+            val = s->dac_mask;
+            break;
+        case 0x02eb:
+            val = s->dac_r_index;
+            break;
+        case 0x02ec:
+            val = s->dac_w_index;
+            break;
+        case 0x02ed:
+            if (s->dac_byte == 0) {
+                // XXX load dac_state[0-2] from palette
+            }
+            val = s->dac_state[s->dac_byte];
+            s->dac_byte = (s->dac_byte + 1) % 3;
+            if (s->dac_byte == 0) {
+                s->dac_r_index++;
+                s->dac_w_index++;
+            }
+            break;
+        default:
+            BADF("%s: invalid register at 0x%04x\n", __func__, addr);
+            val = 0;
+            break;
+    }
+    DPRINTF_IO("%s: read %02x at %04x\n", __func__, val, addr);
+    return val;
+}
+
+static void ibm8514_ramdac_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
+{
+    IBM8514State *s = opaque;
+
+    DPRINTF_IO("%s: write %02x at %04x\n", __func__, val, addr);
+    switch (addr) {
+        case 0x02ea:
+            s->dac_mask = val & 0xff;
+            break;
+        case 0x02eb:
+            s->dac_r_index = val & 0xff;
+            s->dac_byte = 0;
+            break;
+        case 0x02ec:
+            s->dac_w_index = val & 0xff;
+            s->dac_byte = 0;
+            break;
+        case 0x02ed:
+            s->dac_state[s->dac_byte] = val & 0xff;
+            s->dac_byte = (s->dac_byte + 1) % 3;
+            if (s->dac_byte == 0) {
+                // XXX store s->dac_state[0-2] to palette
+                s->dac_r_index++;
+                s->dac_w_index++;
+            }
+            break;
+        default:
+            BADF("%s: invalid register at 0x%04x\n", __func__, addr);
+            break;
+    }
+}
+
+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_ramdac_port(IBM8514State *s, uint32_t addr)
+{
+    register_ioport_read(addr, 1, 1, ibm8514_ramdac_ioport_readb, s);
+    register_ioport_write(addr, 1, 1, ibm8514_ramdac_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_ramdac_port(s, 0x02ea);
+    ibm8514_register_ramdac_port(s, 0x02eb);
+    ibm8514_register_ramdac_port(s, 0x02ec);
+    ibm8514_register_ramdac_port(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 s3_vision864_write_config(PCIDevice *d,
+                                      uint32_t address, uint32_t val, int len)
+{
+    BADF("%s: 0x%08" PRIx32 "\n", __func__, address);
+}
+
+static int s3_vision864_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 s3_vision864_info = {
+    .qdev.name    = "s3-vision864",
+    .qdev.size    = sizeof(PCIIBM8514State),
+    .qdev.vmsd    = &vmstate_ibm8514,
+    // XXX these depend on mst's PCI tree
+#if 0
+    .vendor_id    = PCI_VENDOR_ID_S3,
+    .device_id    = PCI_DEVICE_ID_S3_864,
+    .class_id     = PCI_CLASS_DISPLAY_VGA,
+#endif
+    .init         = s3_vision864_init,
+    .config_write = s3_vision864_write_config,
+    .qdev.props   = (Property[]) {
+        DEFINE_PROP_END_OF_LIST()
+    },
+};
+
+static void ibm8514_register(void)
+{
+    pci_qdev_register(&s3_vision864_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
+
+*/