diff mbox

cg14

Message ID 4BFF76D3.80606@mc.net
State New
Headers show

Commit Message

Bob Breuer May 28, 2010, 7:54 a.m. UTC
Artyom Tarasenko wrote:
> 2010/5/27 Bob Breuer <breuerr@mc.net>:
>   
>> Artyom Tarasenko wrote:
>>     
>>> Was going to put some more empty slots into SS-10/20 (VSIMMs, SX)
>>> after we are done with SS-5 (due to technical limitations I can switch
>>> access from one real SS model to another one once a few days only).
>>>
>>>       
>> I have a partial implementation of the SS-20 VSIMM (cg14) that I've been
>> working on.  With the Sun firmware, I have working text console, color
>> boot logo, and programmable video resolutions up to 1600x1280.
>>     
>
> Great news! This would allow qemu booting NeXTStep! Are you planning
> to submit the patches any time soon?
>
>   
It's not in a state to be submitted yet, but I've attached a working
patch if you want to give it a try.  I need to hook it up to qdev and
fill in some more of the obviously incomplete switch cases before I'd
sign off on it.

Bob

Comments

Blue Swirl May 28, 2010, 9:06 p.m. UTC | #1
On Fri, May 28, 2010 at 7:54 AM, Bob Breuer <breuerr@mc.net> wrote:
> Artyom Tarasenko wrote:
>> 2010/5/27 Bob Breuer <breuerr@mc.net>:
>>
>>> Artyom Tarasenko wrote:
>>>
>>>> Was going to put some more empty slots into SS-10/20 (VSIMMs, SX)
>>>> after we are done with SS-5 (due to technical limitations I can switch
>>>> access from one real SS model to another one once a few days only).
>>>>
>>>>
>>> I have a partial implementation of the SS-20 VSIMM (cg14) that I've been
>>> working on.  With the Sun firmware, I have working text console, color
>>> boot logo, and programmable video resolutions up to 1600x1280.
>>>
>>
>> Great news! This would allow qemu booting NeXTStep! Are you planning
>> to submit the patches any time soon?
>>
>>
> It's not in a state to be submitted yet, but I've attached a working
> patch if you want to give it a try.  I need to hook it up to qdev and
> fill in some more of the obviously incomplete switch cases before I'd
> sign off on it.

Nice work. I have a few comments below.

This probably needs support from OpenBIOS to be usable without OBP.

>
> Bob
>
>
> diff --git a/Makefile.target b/Makefile.target
> index fda5bf3..b17b3af 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -250,6 +250,7 @@ else
>  obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>  obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>  obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
> +obj-sparc-y += cg14.o
>  endif
>
>  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
> diff --git a/hw/sun4m.c b/hw/sun4m.c
> index 7ba0f76..8b23c9b 100644
> --- a/hw/sun4m.c
> +++ b/hw/sun4m.c
> @@ -864,6 +864,13 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
>         fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
>         exit (1);
>     }
> +  if (hwdef->machine_id == 65) { /* SS-20 */

hwdef structure should contain a field for cg14. If non-zero, install
cg14. Was cg14 only available for SS-20? Was it always included? This
is also interesting for OpenBIOS, we need to detect cg14 vs. TCX.

> +    /* cg14.c */
> +    void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
> +                uint32_t vram_size);

This should go to sun4m.h or cg14.h.

> +
> +    cg14_init(0x09c000000ULL, 0x0fc000000ULL, 8<<20);
> +  } else

Please add braces and reindent.

>     tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
>              graphic_depth);
>
> --- /dev/null   Fri May 28 02:08:36 2010
> +++ hw/cg14.c   Fri May 28 01:58:49 2010
> @@ -0,0 +1,785 @@
> +/*
> + * QEMU CG14 Frame buffer
> + *
> + * Copyright (c) 2010 Bob Breuer
> + *
> + * 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.
> + */
> +
> +#include "console.h"
> +#include "sysbus.h"
> +
> +#ifdef DEBUG
> +#define DPRINTF(fmt, ...)                                       \
> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, ...)
> +#endif
> +
> +#define CG14_INFO(fmt, ...)                                     \
> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
> +#define CG14_ERROR(fmt, ...)                                    \
> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
> +
> +/*
> + * A[28:26] = slot number (4 to 7)
> + * regs: size   0x10000 @ 0x09c000000  (0x80000000 + slot * 64M)
> + * vmem: size upto 16MB @ 0x0fc000000  (0xE0000000 + slot * 64M)
> + */

If you have any links to chipset docs, it would be nice to mention those here.

> +
> +/*
> + * memory map:
> + * reg+0x0000 = control registers
> + * reg+0x1000 = cursor registers
> + * reg+0x2000 = dac registers (ADV7152)
> + * reg+0x3000 = xlut
> + * reg+0x4000 = clut1
> + * reg+0x5000 = clut2
> + * reg+0x6000 = clut3 (if implemented)
> + *
> + * mem+0x0000000 = XBGR (01234567)
> + * mem+0x1000000 = BGR  (.123.567)
> + * mem+0x2000000 = X16  (0246)
> + * mem+0x2800000 = C16  (1357)
> + * mem+0x3000000 = X32  (04)
> + * mem+0x3400000 = B32  (15)
> + * mem+0x3800000 = G32  (26)
> + * mem+0x3c00000 = R32  (37)

Interesting device. You could increase the performance a lot by making
the XBGR area ordinary memory and detecting the dirtyness with
VGA_DIRTY_FLAG like TCX. The other areas could use multiple byte
stores to the memory so dirty information would be updated.

> + */
> +
> +#define CG14_REG_SIZE         0x10000
> +#define CG14_VMEM_SLOTSIZE    (64<<20)
> +
> +#define CG14_MONID_1024x768   0
> +#define CG14_MONID_1600x1280  1
> +#define CG14_MONID_1280x1024  2
> +#define CG14_MONID_1152x900   7
> +
> +#define CG14_MONID_DEFAULT    CG14_MONID_1024x768
> +
> +#define MCR_PIXMODE_MASK  0x30
> +#define   MCR_PIXMODE_8     0x00
> +#define   MCR_PIXMODE_16    0x20  /* 8+8 (X16,C16) */
> +#define   MCR_PIXMODE_32    0x30  /* XBGR */
> +
> +
> +struct ADV7152_state {
> +    uint8_t mode;
> +    uint8_t address;
> +    int rgb_seq;
> +};
> +
> +typedef struct CG14State {
> +    SysBusDevice busdev;
> +    DisplayState *ds;
> +
> +    uint8_t *vram;
> +    uint32_t vram_amask;
> +    int width, height;
> +    int dirty, size_changed;
> +    struct {
> +        uint8_t mcr;
> +        uint8_t ppr;
> +    } ctrl;
> +    struct ADV7152_state dac;
> +    struct {
> +        uint16_t hblank_start;
> +        uint16_t hblank_clear;
> +        uint16_t vblank_start;
> +        uint16_t vblank_clear;
> +    } timing;
> +    uint8_t xlut[256];
> +    uint32_t clut1[256];
> +    uint32_t clut2[256];
> +} CG14State;
> +
> +static void cg14_screen_dump(void *opaque, const char *filename);
> +static void cg14_invalidate_display(void *opaque);
> +
> +static inline uint32_t bgr_to_rgb(uint32_t bgr)
> +{
> +    uint32_t rgb;
> +
> +    /* swap r & b */
> +    rgb = (bgr & 0x00FF00)
> +        | (bgr & 0x0000FF) << 16
> +        | (bgr & 0xFF0000) >> 16;
> +    return rgb;
> +}
> +
> +static void cg14_draw_line32(const CG14State *s, void *dst, const uint8_t *src, int pixmode, int is_bgr)
> +{
> +    int i;
> +    int x, r, g, b;
> +    uint8_t xlut_val;
> +    uint32_t dval;
> +    uint32_t abgr;
> +
> +    xlut_val = s->ctrl.ppr;
> +
> +    for (i=0; i<s->width; i++) {
> +        x = *src++;
> +        if (pixmode == 8) {

To increase performance, pixmode should not be passed at all but
instead separate functions should be added for each mode and the
function should be selected before the line loop.

> +            b = x;
> +        } else {
> +            b = *src++;
> +            xlut_val = s->xlut[x];
> +        }
> +        if (pixmode != 32) {
> +            r = g = b;
> +        } else {
> +            g = *src++;
> +            r = *src++;
> +        }
> +        if (xlut_val == 0) {
> +            abgr = b << 16 | g << 8 | r;
> +        } else if (xlut_val == 0x40) {
> +            abgr = s->clut1[x];
> +        } else {
> +            abgr = 0;
> +        }
> +        /* dac lookup ? */
> +
> +        /* to surface format */
> +        dval = is_bgr ? (abgr & 0xFFFFFF) : bgr_to_rgb(abgr);
> +        ((uint32_t*)dst)[i] = dval;
> +    }
> +}
> +
> +static void cg14_update_display(void *opaque)
> +{
> +    CG14State *s = opaque;
> +    int h, pixmode;
> +    uint8_t *pix;
> +    uint8_t *data;
> +    int new_width, new_height;
> +
> +    if (s->size_changed) {
> +        new_width = 4 * (s->timing.hblank_start - s->timing.hblank_clear);
> +        new_height = s->timing.vblank_start - s->timing.vblank_clear;
> +        s->size_changed = 0;
> +        if ((new_width != s->width || new_height != s->height) && new_width > 0 && new_height > 0) {
> +            s->width = new_width;
> +            s->height = new_height;
> +            CG14_INFO("new resolution = %d x %d\n", new_width, new_height);
> +            qemu_console_resize(s->ds, s->width, s->height);
> +            s->dirty = 1;
> +        }
> +    }
> +
> +    if (!s->dirty || !s->width || !s->height) {
> +        return;
> +    }
> +
> +    if (ds_get_bits_per_pixel(s->ds) != 32) {
> +        CG14_ERROR("cg14_update: FIXME: bpp (%d) != 32, linesize %d\n",
> +            ds_get_bits_per_pixel(s->ds), ds_get_linesize(s->ds));
> +        return;
> +    }
> +
> +    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
> +    case MCR_PIXMODE_32:
> +        pixmode = 32;
> +        break;
> +    case MCR_PIXMODE_16:
> +        pixmode = 16;
> +        break;
> +    case MCR_PIXMODE_8:
> +    default:
> +        pixmode = 8;
> +        break;
> +    }
> +
> +    pix = s->vram;
> +    data = ds_get_data(s->ds);
> +
> +    for (h=0; h<s->height; h++) {
> +        cg14_draw_line32(s, data, pix, pixmode, is_surface_bgr(s->ds->surface));
> +        pix += s->width * (pixmode / 8);
> +        data += ds_get_linesize(s->ds);
> +    }
> +    dpy_update(s->ds, 0, 0, s->width, s->height);
> +    s->dirty = 0;
> +}
> +
> +static void cg14_invalidate_display(void *opaque)
> +{
> +    CG14State *s = opaque;
> +
> +    s->dirty = 1;
> +}
> +
> +static void ADV7152_write(struct ADV7152_state *s, unsigned int reg, unsigned int val)
> +{
> +    switch (reg) {
> +    case 0: /* address register */
> +        DPRINTF("ADV7152 Write address %02x\n", val);
> +        s->address = val;
> +        s->rgb_seq = 0;
> +        break;
> +    case 1: /* look up table */
> +        DPRINTF("ADV7152 Write %02x to lookup table\n", val);
> +        s->rgb_seq++;
> +        break;
> +    case 2: /* control registers */
> +        DPRINTF("ADV7152 Write %02x to control reg %d\n", val, s->address);
> +        switch (s->address) {
> +        default:
> +            break;
> +        }
> +        break;
> +    case 3: /* mode register */
> +        CG14_INFO("ADV7152 Write mode %02x (%d bit DAC, %d bit bus)\n",
> +            val, (val & 2) ? 10 : 8, (val & 4) ? 10 : 8);
> +        if (!val & 0x01) {
> +            // reset the dac
> +            s->rgb_seq = 0;
> +        }
> +        s->mode = val;
> +        break;
> +    }
> +}
> +
> +static uint32_t cg14_reg_readb(void *opaque, target_phys_addr_t addr)
> +{
> +    CG14State *s = opaque;
> +    unsigned int val;
> +
> +    switch (addr & 0xffff) {
> +    case 0x0000:
> +        val = s->ctrl.mcr;
> +        break;
> +    case 0x0001:
> +        val = s->ctrl.ppr;
> +        break;
> +    case 0x0004: /* status ? */
> +        /* monitor code in bits 1..3 */
> +        val = CG14_MONID_DEFAULT << 1;
> +        break;
> +    case 0x0006: /* hw version */
> +        //val = 0x00; /* old version */
> +        val = 0x30;
> +        break;
> +    default:
> +        val = 0;
> +        break;
> +    }
> +    CG14_INFO("readb %02x from reg %x\n", val, (int)addr);
> +
> +    return val;
> +}
> +
> +static void cg14_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    CG14State *s = opaque;
> +    uint32_t i;
> +
> +    if ((addr & 0xfcff) == 0x2000) {
> +        i = (addr & 0x300) >> 8;
> +        ADV7152_write(&s->dac, i, val);
> +        return;
> +    }
> +    if ((addr & 0xff00) == 0x3000) {
> +        /* xlut */
> +        i = addr & 0xff;
> +        if (s->xlut[i] != val) {
> +            s->dirty = 1;
> +            s->xlut[i] = val;
> +            if (val && val != 0x40)
> +                CG14_ERROR("writeb xlut[%d] = %02x\n", i, val);
> +        }
> +        return;
> +    }
> +
> +    s->dirty = 1;
> +
> +    switch (addr & 0xffff) {
> +    case 0x0000:
> +        s->ctrl.mcr = val;
> +        break;
> +    case 0x0001:
> +        s->ctrl.ppr = val & 0xF0;
> +        break;
> +    case 0x0007:
> +        /* clock control (ICS1562AM-001) */
> +        DPRINTF("write %02x to clock control\n", val);
> +        break;
> +    default:
> +        CG14_ERROR("writeb %02x to reg %x\n", val, (int)addr);
> +        break;
> +    }
> +}
> +
> +static uint32_t cg14_reg_readw(void *opaque, target_phys_addr_t addr)
> +{
> +    CG14State *s = opaque;
> +    unsigned int val;
> +
> +    switch (addr & 0xffff) {
> +    case 0x0018:
> +        val = s->timing.hblank_start;
> +        break;
> +    case 0x001a:
> +        val = s->timing.hblank_clear;
> +        break;
> +    case 0x0022:
> +        val = s->timing.vblank_start;
> +        break;
> +    case 0x0024:
> +        val = s->timing.vblank_clear;
> +        break;
> +    default:
> +        val = 0;
> +        break;
> +    }
> +    CG14_INFO("readw 0x%08x from reg %x\n", val, (int)addr);
> +
> +    return val;
> +}
> +
> +static void cg14_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    CG14State *s = opaque;
> +
> +    CG14_INFO("writew %04x to reg %x\n", val, (int)addr);
> +
> +    /* timing registers are 16bit */
> +
> +    switch (addr & 0xffff) {
> +    case 0x0018:
> +        s->timing.hblank_start = val;
> +        break;
> +    case 0x001a:
> +        s->timing.hblank_clear = val;
> +       s->size_changed = 1;
> +        break;
> +    case 0x0022:
> +        s->timing.vblank_start = val;
> +        break;
> +    case 0x0024:
> +        s->timing.vblank_clear = val;
> +       s->size_changed = 1;
> +        break;
> +    case 0x001c: /* hsync_start */
> +    case 0x001e: /* hsync_clear */
> +    case 0x0020: /* csync_clear */
> +    case 0x0026: /* vsync_start */
> +    case 0x0028: /* vsync_clear */
> +    default:
> +        break;
> +    }
> +}
> +
> +static uint32_t cg14_reg_readl(void *opaque, target_phys_addr_t addr)
> +{
> +    CG14State *s = opaque;
> +    uint32_t val;
> +    uint32_t i;
> +
> +    i = (addr & 0x3ff) >> 2;
> +    switch (addr & 0xfc00) {
> +    case 0x4000:
> +        val = s->clut1[i];
> +        break;
> +    case 0x5000:
> +        val = s->clut2[i];
> +        break;
> +    default:
> +        val = 0;
> +        CG14_ERROR("readl %08x from reg %x\n", val, (int)addr);
> +        break;
> +    }
> +
> +    return val;
> +}
> +
> +static void cg14_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    CG14State *s = opaque;
> +    uint32_t i;
> +
> +    s->dirty = 1;
> +
> +    i = (addr & 0x3ff) >> 2;
> +    switch (addr & 0xfc00) {
> +    case 0x4000:
> +        s->clut1[i] = val;
> +        break;
> +    case 0x5000:
> +        s->clut2[i] = val;
> +        break;
> +    default:
> +        CG14_ERROR("writel %08x to reg %x\n", val, (int)addr);
> +        break;
> +    }
> +}
> +
> +static CPUReadMemoryFunc *cg14_reg_read[3] = {
> +    cg14_reg_readb,
> +    cg14_reg_readw,
> +    cg14_reg_readl,
> +};
> +
> +static CPUWriteMemoryFunc *cg14_reg_write[3] = {
> +    cg14_reg_writeb,
> +    cg14_reg_writew,
> +    cg14_reg_writel,
> +};
> +
> +static uint32_t cg14_vram_readb(void *opaque, target_phys_addr_t addr)
> +{
> +    CG14State *s = opaque;
> +    uint32_t offset;
> +    uint32_t val = 0;
> +
> +    switch (addr & 0x3000000) {
> +    case 0x0000000:
> +        offset = addr & s->vram_amask;
> +        val =  ldub_p(s->vram+offset);
> +        break;
> +    case 0x1000000:
> +        offset = addr & s->vram_amask;
> +        val = 0; // FIXME
> +        break;
> +    case 0x2000000:
> +        offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1);
> +        val =  ldub_p(s->vram+offset);
> +        break;
> +    case 0x3000000:
> +        offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3);
> +        val =  ldub_p(s->vram+offset);
> +        break;
> +    }
> +    CG14_INFO("readb %02x from vram %x\n", val, (int)addr);
> +
> +    return val;
> +}
> +
> +static void cg14_vram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    CG14State *s = opaque;
> +    uint32_t offset;
> +
> +    switch (addr & 0x3000000) {
> +    case 0x0000000:
> +        offset = addr & s->vram_amask;
> +        stb_p(s->vram+offset, val);
> +        if (offset < 4 * s->width * s->height) {
> +            s->dirty = 1;
> +        }
> +        break;
> +    default:
> +        CG14_ERROR("writeb %02x to vram %x\n", val, (int)addr);
> +        break;
> +    }
> +}
> +
> +static uint32_t cg14_vram_readw(void *opaque, target_phys_addr_t addr)
> +{
> +    CG14State *s = opaque;
> +    uint32_t offset;
> +    uint32_t val;
> +
> +    switch (addr & 0x3000000) {
> +    default:
> +        offset = addr & s->vram_amask;
> +        val = 0;
> +        break;
> +    }
> +    CG14_ERROR("readw %04x from vram %x\n", val, (int)addr);
> +
> +    return val;
> +}
> +
> +static void cg14_vram_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    CG14State *s = opaque;
> +
> +    CG14_ERROR("writew %04x to vram %x\n", val, (int)addr);
> +
> +    s->dirty = 1;
> +
> +    switch (addr & 0x3000000) {
> +    default:
> +        break;
> +    }
> +}
> +
> +static uint32_t cg14_vram_readl(void *opaque, target_phys_addr_t addr)
> +{
> +    CG14State *s = opaque;
> +    uint32_t offset;
> +    uint32_t val = 0;
> +
> +    switch (addr & 0x3000000) {
> +    case 0x0000000:
> +        offset = addr & s->vram_amask;
> +        val = ldl_be_p(s->vram+offset);
> +        break;
> +    case 0x1000000:
> +    case 0x2000000:
> +    case 0x3000000:
> +        CG14_ERROR("readl %08x from vram %x\n", val, (int)addr);
> +        break;
> +    }
> +
> +    return val;
> +}
> +
> +static void cg14_vram_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    CG14State *s = opaque;
> +    uint32_t offset;
> +
> +    switch (addr & 0x3000000) {
> +    case 0x0000000:
> +        offset = addr & s->vram_amask;
> +        stl_be_p(s->vram+offset, val);
> +        if (offset < 4 * s->width * s->height) {
> +            s->dirty = 1;
> +        }
> +        break;
> +    case 0x1000000:
> +    case 0x2000000:
> +    case 0x3000000:
> +        CG14_ERROR("writel %08x to vram %x\n", val, (int)addr);
> +        break;
> +    }
> +}
> +
> +static CPUReadMemoryFunc *cg14_vram_read[3] = {
> +    cg14_vram_readb,
> +    cg14_vram_readw,
> +    cg14_vram_readl,
> +};
> +
> +static CPUWriteMemoryFunc *cg14_vram_write[3] = {
> +    cg14_vram_writeb,
> +    cg14_vram_writew,
> +    cg14_vram_writel,
> +};
> +
> +
> +/******** SX *********/
> +
> +static uint32_t sx_reg_readb(void *opaque, target_phys_addr_t addr)
> +{
> +    //CG14State *s = opaque;
> +    int val;
> +
> +    printf("SX readb reg " TARGET_FMT_plx "\n", addr);
> +
> +    switch (addr & 0xffff) {
> +    default:
> +        val = 0;
> +        break;
> +    }
> +    return val;
> +}
> +
> +static void sx_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    //CG14State *s = opaque;
> +
> +    printf("SX writeb %02x to reg " TARGET_FMT_plx "\n", val, addr);
> +
> +    switch (addr & 0xffff) {
> +    default:
> +        break;
> +    }
> +}
> +
> +static uint32_t sx_reg_readw(void *opaque, target_phys_addr_t addr)
> +{
> +    //CG14State *s = opaque;
> +    int val;
> +
> +    printf("SX readw reg " TARGET_FMT_plx "\n", addr);
> +
> +    switch (addr & 0xffff) {
> +    default:
> +        val = 0;
> +        break;
> +    }
> +    return val;
> +}
> +
> +static void sx_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    //CG14State *s = opaque;
> +
> +    printf("SX writew %04x to reg " TARGET_FMT_plx "\n", val, addr);
> +
> +    switch (addr & 0xffff) {
> +    default:
> +        break;
> +    }
> +}
> +
> +static uint32_t sx_reg_readl(void *opaque, target_phys_addr_t addr)
> +{
> +    //CG14State *s = opaque;
> +    int val;
> +
> +    printf("SX readl reg " TARGET_FMT_plx "\n", addr);
> +
> +    switch (addr & 0xffff) {
> +    default:
> +        val = 0;
> +        break;
> +    }
> +    return val;
> +}
> +
> +static void sx_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    //CG14State *s = opaque;
> +
> +    printf("SX writel %08x to reg " TARGET_FMT_plx "\n", val, addr);
> +
> +    switch (addr & 0xffff) {
> +    default:
> +        break;
> +    }
> +}
> +
> +static CPUReadMemoryFunc *sx_reg_read[3] = {
> +    sx_reg_readb,
> +    sx_reg_readw,
> +    sx_reg_readl,
> +};
> +
> +static CPUWriteMemoryFunc *sx_reg_write[3] = {
> +    sx_reg_writeb,
> +    sx_reg_writew,
> +    sx_reg_writel,
> +};
> +
> +/*********************/
> +
> +static uint32_t bad_mem_read(void *opaque, target_phys_addr_t addr)
> +{
> +    printf("Bad read from " TARGET_FMT_plx "\n", addr);
> +    //cpu_abort(cpu_single_env, "bad ram read access at " TARGET_FMT_plx "\n", addr);
> +    return 0;
> +}
> +static void bad_mem_write(void *opaque, target_phys_addr_t addr, uint32_t val)
> +{
> +    printf("Bad write of 0x%02x to " TARGET_FMT_plx "\n", val, addr);
> +    //cpu_abort(cpu_single_env, "bad ram write access at " TARGET_FMT_plx "\n", addr);
> +}
> +static CPUReadMemoryFunc *bad_memr[3] = { bad_mem_read, bad_mem_read, bad_mem_read };
> +static CPUWriteMemoryFunc *bad_memw[3] = { bad_mem_write, bad_mem_write, bad_mem_write };
> +
> +void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
> +                uint32_t vram_size)
> +{
> +//    DeviceState *dev;
> +//    SysBusDevice *s;
> +
> +//    dev = qdev_create(NULL, "SUNW,cg14");
> +//    qdev_init(dev);
> +//    s = sysbus_from_qdev(dev);
> +//}
> +
> +//static void cg14_init1(SysBusDevice *dev)
> +//{
> +    CG14State *s;// = FROM_SYSBUS(CG14State, dev);
> +    ram_addr_t vram_offset;
> +    uint8_t *vram;
> +    int ctrl_memory, vram_memory;
> +    int sx_registers;
> +    int bad_mem;
> +
> +    s = qemu_mallocz(sizeof(CG14State));
> +
> +    vram_offset = qemu_ram_alloc(vram_size);
> +    vram = qemu_get_ram_ptr(vram_offset);
> +
> +    s->vram = vram;
> +    s->vram_amask = vram_size - 1;
> +
> +    ctrl_memory = cpu_register_io_memory(cg14_reg_read, cg14_reg_write, s);
> +    cpu_register_physical_memory_offset(ctrl_base, CG14_REG_SIZE, ctrl_memory, ctrl_base);
> +
> +    vram_memory = cpu_register_io_memory(cg14_vram_read, cg14_vram_write, s);
> +    cpu_register_physical_memory_offset(vram_base, CG14_VMEM_SLOTSIZE, vram_memory, vram_base);
> +
> +    s->ds = graphic_console_init(cg14_update_display,
> +                                 cg14_invalidate_display,
> +                                 cg14_screen_dump, NULL, s);
> +
> +    s->width = 640;
> +    s->height = 480;
> +    qemu_console_resize(s->ds, s->width, s->height);
> +
> +    /* SX or SPAM (Sun Pixel Arithmetic Memory) */
> +    sx_registers = cpu_register_io_memory(sx_reg_read, sx_reg_write, s);
> +    cpu_register_physical_memory(0xf80000000ULL, 0x2000, sx_registers);
> +
> +    bad_mem = cpu_register_io_memory(bad_memr, bad_memw, s);
> +    /* missing vsimms */
> +    cpu_register_physical_memory_offset(0x90000000, 0x2000, bad_mem, 0x90000000);
> +    cpu_register_physical_memory_offset(0x94000000, 0x2000, bad_mem, 0x94000000);
> +    cpu_register_physical_memory_offset(0x98000000, 0x2000, bad_mem, 0x98000000);

Can't we just have more VSIMMS? The empty_slot device may be useful here.

> +    /* DBRI (audio) */
> +    cpu_register_physical_memory_offset(0xEE0001000ULL, 0x10000, bad_mem, 0xE0001000);

Please add a new DBRI device ;-).

> +}
> +
> +/* save to file */
> +static void cg14_screen_dump(void *opaque, const char *filename)
> +{
> +    CG14State *s = opaque;
> +    FILE *f;
> +    int y, pixmode, linesize;
> +    void *buf;
> +    uint8_t *pix;
> +
> +    f = fopen(filename, "wb");
> +    if (!f) {
> +        return;
> +    }
> +    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
> +
> +    linesize = s->width * 3;
> +    buf = qemu_mallocz(linesize);
> +    pix = s->vram;
> +
> +    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
> +    case MCR_PIXMODE_32:
> +        pixmode = 32;
> +        break;
> +    case MCR_PIXMODE_16:
> +        pixmode = 16;
> +        break;
> +    case MCR_PIXMODE_8:
> +    default:
> +        pixmode = 8;
> +        break;
> +    }
> +
> +    for (y=0; y<s->height; y++) {
> +        // cg14_draw_line24_bgr(s, buf, pix, pixmode);
> +        fwrite(buf, 1, linesize, f);
> +        pix += s->width * (pixmode / 8);
> +    }
> +
> +    qemu_free(buf);
> +    fclose(f);
> +}
>
>
Artyom Tarasenko May 28, 2010, 10:48 p.m. UTC | #2
2010/5/28 Blue Swirl <blauwirbel@gmail.com>:
> On Fri, May 28, 2010 at 7:54 AM, Bob Breuer <breuerr@mc.net> wrote:
>> Artyom Tarasenko wrote:
>>> 2010/5/27 Bob Breuer <breuerr@mc.net>:
>>>
>>>> Artyom Tarasenko wrote:
>>>>
>>>>> Was going to put some more empty slots into SS-10/20 (VSIMMs, SX)
>>>>> after we are done with SS-5 (due to technical limitations I can switch
>>>>> access from one real SS model to another one once a few days only).
>>>>>
>>>>>
>>>> I have a partial implementation of the SS-20 VSIMM (cg14) that I've been
>>>> working on.  With the Sun firmware, I have working text console, color
>>>> boot logo, and programmable video resolutions up to 1600x1280.
>>>>
>>>
>>> Great news! This would allow qemu booting NeXTStep! Are you planning
>>> to submit the patches any time soon?
>>>
>>>
>> It's not in a state to be submitted yet, but I've attached a working
>> patch if you want to give it a try.  I need to hook it up to qdev and
>> fill in some more of the obviously incomplete switch cases before I'd
>> sign off on it.
>
> Nice work. I have a few comments below.
>
> This probably needs support from OpenBIOS to be usable without OBP.

Maybe it can be used as a second adapter without OpenBIOS support? At
least under some OSes?

>>
>> Bob
>>
>>
>> diff --git a/Makefile.target b/Makefile.target
>> index fda5bf3..b17b3af 100644
>> --- a/Makefile.target
>> +++ b/Makefile.target
>> @@ -250,6 +250,7 @@ else
>>  obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>  obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>>  obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>> +obj-sparc-y += cg14.o
>>  endif
>>
>>  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>> diff --git a/hw/sun4m.c b/hw/sun4m.c
>> index 7ba0f76..8b23c9b 100644
>> --- a/hw/sun4m.c
>> +++ b/hw/sun4m.c
>> @@ -864,6 +864,13 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
>>         fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
>>         exit (1);
>>     }
>> +  if (hwdef->machine_id == 65) { /* SS-20 */
>
> hwdef structure should contain a field for cg14. If non-zero, install
> cg14. Was cg14 only available for SS-20? Was it always included? This
> is also interesting for OpenBIOS, we need to detect cg14 vs. TCX.
>
>> +    /* cg14.c */
>> +    void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
>> +                uint32_t vram_size);
>
> This should go to sun4m.h or cg14.h.
>
>> +
>> +    cg14_init(0x09c000000ULL, 0x0fc000000ULL, 8<<20);
>> +  } else
>
> Please add braces and reindent.
>
>>     tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
>>              graphic_depth);
>>
>> --- /dev/null   Fri May 28 02:08:36 2010
>> +++ hw/cg14.c   Fri May 28 01:58:49 2010
>> @@ -0,0 +1,785 @@
>> +/*
>> + * QEMU CG14 Frame buffer
>> + *
>> + * Copyright (c) 2010 Bob Breuer
>> + *
>> + * 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.
>> + */
>> +
>> +#include "console.h"
>> +#include "sysbus.h"
>> +
>> +#ifdef DEBUG

DEBUG_CG14 ?

>> +#define DPRINTF(fmt, ...)                                       \
>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>> +#else
>> +#define DPRINTF(fmt, ...)
>> +#endif
>> +
>> +#define CG14_INFO(fmt, ...)                                     \
>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>> +#define CG14_ERROR(fmt, ...)                                    \
>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>> +
>> +/*
>> + * A[28:26] = slot number (4 to 7)
>> + * regs: size   0x10000 @ 0x09c000000  (0x80000000 + slot * 64M)
>> + * vmem: size upto 16MB @ 0x0fc000000  (0xE0000000 + slot * 64M)
>> + */
>
> If you have any links to chipset docs, it would be nice to mention those here.
>
>> +
>> +/*
>> + * memory map:
>> + * reg+0x0000 = control registers
>> + * reg+0x1000 = cursor registers
>> + * reg+0x2000 = dac registers (ADV7152)
>> + * reg+0x3000 = xlut
>> + * reg+0x4000 = clut1
>> + * reg+0x5000 = clut2
>> + * reg+0x6000 = clut3 (if implemented)
>> + *
>> + * mem+0x0000000 = XBGR (01234567)
>> + * mem+0x1000000 = BGR  (.123.567)
>> + * mem+0x2000000 = X16  (0246)
>> + * mem+0x2800000 = C16  (1357)
>> + * mem+0x3000000 = X32  (04)
>> + * mem+0x3400000 = B32  (15)
>> + * mem+0x3800000 = G32  (26)
>> + * mem+0x3c00000 = R32  (37)
>
> Interesting device. You could increase the performance a lot by making
> the XBGR area ordinary memory and detecting the dirtyness with
> VGA_DIRTY_FLAG like TCX. The other areas could use multiple byte
> stores to the memory so dirty information would be updated.
>
>> + */
>> +
>> +#define CG14_REG_SIZE         0x10000
>> +#define CG14_VMEM_SLOTSIZE    (64<<20)
>> +
>> +#define CG14_MONID_1024x768   0
>> +#define CG14_MONID_1600x1280  1
>> +#define CG14_MONID_1280x1024  2
>> +#define CG14_MONID_1152x900   7
>> +
>> +#define CG14_MONID_DEFAULT    CG14_MONID_1024x768
>> +
>> +#define MCR_PIXMODE_MASK  0x30
>> +#define   MCR_PIXMODE_8     0x00
>> +#define   MCR_PIXMODE_16    0x20  /* 8+8 (X16,C16) */
>> +#define   MCR_PIXMODE_32    0x30  /* XBGR */
>> +
>> +
>> +struct ADV7152_state {
>> +    uint8_t mode;
>> +    uint8_t address;
>> +    int rgb_seq;
>> +};
>> +
>> +typedef struct CG14State {
>> +    SysBusDevice busdev;
>> +    DisplayState *ds;
>> +
>> +    uint8_t *vram;
>> +    uint32_t vram_amask;
>> +    int width, height;
>> +    int dirty, size_changed;
>> +    struct {
>> +        uint8_t mcr;
>> +        uint8_t ppr;
>> +    } ctrl;
>> +    struct ADV7152_state dac;
>> +    struct {
>> +        uint16_t hblank_start;
>> +        uint16_t hblank_clear;
>> +        uint16_t vblank_start;
>> +        uint16_t vblank_clear;
>> +    } timing;
>> +    uint8_t xlut[256];
>> +    uint32_t clut1[256];
>> +    uint32_t clut2[256];
>> +} CG14State;
>> +
>> +static void cg14_screen_dump(void *opaque, const char *filename);
>> +static void cg14_invalidate_display(void *opaque);
>> +
>> +static inline uint32_t bgr_to_rgb(uint32_t bgr)
>> +{
>> +    uint32_t rgb;
>> +
>> +    /* swap r & b */
>> +    rgb = (bgr & 0x00FF00)
>> +        | (bgr & 0x0000FF) << 16
>> +        | (bgr & 0xFF0000) >> 16;
>> +    return rgb;
>> +}
>> +
>> +static void cg14_draw_line32(const CG14State *s, void *dst, const uint8_t *src, int pixmode, int is_bgr)
>> +{
>> +    int i;
>> +    int x, r, g, b;
>> +    uint8_t xlut_val;
>> +    uint32_t dval;
>> +    uint32_t abgr;
>> +
>> +    xlut_val = s->ctrl.ppr;
>> +
>> +    for (i=0; i<s->width; i++) {
>> +        x = *src++;
>> +        if (pixmode == 8) {
>
> To increase performance, pixmode should not be passed at all but
> instead separate functions should be added for each mode and the
> function should be selected before the line loop.
>
>> +            b = x;
>> +        } else {
>> +            b = *src++;
>> +            xlut_val = s->xlut[x];
>> +        }
>> +        if (pixmode != 32) {
>> +            r = g = b;
>> +        } else {
>> +            g = *src++;
>> +            r = *src++;
>> +        }
>> +        if (xlut_val == 0) {
>> +            abgr = b << 16 | g << 8 | r;
>> +        } else if (xlut_val == 0x40) {
>> +            abgr = s->clut1[x];
>> +        } else {
>> +            abgr = 0;
>> +        }
>> +        /* dac lookup ? */
>> +
>> +        /* to surface format */
>> +        dval = is_bgr ? (abgr & 0xFFFFFF) : bgr_to_rgb(abgr);
>> +        ((uint32_t*)dst)[i] = dval;
>> +    }
>> +}
>> +
>> +static void cg14_update_display(void *opaque)
>> +{
>> +    CG14State *s = opaque;
>> +    int h, pixmode;
>> +    uint8_t *pix;
>> +    uint8_t *data;
>> +    int new_width, new_height;
>> +
>> +    if (s->size_changed) {
>> +        new_width = 4 * (s->timing.hblank_start - s->timing.hblank_clear);
>> +        new_height = s->timing.vblank_start - s->timing.vblank_clear;
>> +        s->size_changed = 0;
>> +        if ((new_width != s->width || new_height != s->height) && new_width > 0 && new_height > 0) {
>> +            s->width = new_width;
>> +            s->height = new_height;
>> +            CG14_INFO("new resolution = %d x %d\n", new_width, new_height);
>> +            qemu_console_resize(s->ds, s->width, s->height);
>> +            s->dirty = 1;
>> +        }
>> +    }
>> +
>> +    if (!s->dirty || !s->width || !s->height) {
>> +        return;
>> +    }
>> +
>> +    if (ds_get_bits_per_pixel(s->ds) != 32) {
>> +        CG14_ERROR("cg14_update: FIXME: bpp (%d) != 32, linesize %d\n",
>> +            ds_get_bits_per_pixel(s->ds), ds_get_linesize(s->ds));
>> +        return;
>> +    }
>> +
>> +    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
>> +    case MCR_PIXMODE_32:
>> +        pixmode = 32;
>> +        break;
>> +    case MCR_PIXMODE_16:
>> +        pixmode = 16;
>> +        break;
>> +    case MCR_PIXMODE_8:
>> +    default:
>> +        pixmode = 8;
>> +        break;
>> +    }
>> +
>> +    pix = s->vram;
>> +    data = ds_get_data(s->ds);
>> +
>> +    for (h=0; h<s->height; h++) {
>> +        cg14_draw_line32(s, data, pix, pixmode, is_surface_bgr(s->ds->surface));
>> +        pix += s->width * (pixmode / 8);
>> +        data += ds_get_linesize(s->ds);
>> +    }
>> +    dpy_update(s->ds, 0, 0, s->width, s->height);
>> +    s->dirty = 0;
>> +}
>> +
>> +static void cg14_invalidate_display(void *opaque)
>> +{
>> +    CG14State *s = opaque;
>> +
>> +    s->dirty = 1;
>> +}
>> +
>> +static void ADV7152_write(struct ADV7152_state *s, unsigned int reg, unsigned int val)
>> +{
>> +    switch (reg) {
>> +    case 0: /* address register */
>> +        DPRINTF("ADV7152 Write address %02x\n", val);
>> +        s->address = val;
>> +        s->rgb_seq = 0;
>> +        break;
>> +    case 1: /* look up table */
>> +        DPRINTF("ADV7152 Write %02x to lookup table\n", val);
>> +        s->rgb_seq++;
>> +        break;
>> +    case 2: /* control registers */
>> +        DPRINTF("ADV7152 Write %02x to control reg %d\n", val, s->address);
>> +        switch (s->address) {
>> +        default:
>> +            break;
>> +        }
>> +        break;
>> +    case 3: /* mode register */
>> +        CG14_INFO("ADV7152 Write mode %02x (%d bit DAC, %d bit bus)\n",
>> +            val, (val & 2) ? 10 : 8, (val & 4) ? 10 : 8);
>> +        if (!val & 0x01) {
>> +            // reset the dac
>> +            s->rgb_seq = 0;
>> +        }
>> +        s->mode = val;
>> +        break;
>> +    }
>> +}
>> +
>> +static uint32_t cg14_reg_readb(void *opaque, target_phys_addr_t addr)
>> +{
>> +    CG14State *s = opaque;
>> +    unsigned int val;
>> +
>> +    switch (addr & 0xffff) {
>> +    case 0x0000:
>> +        val = s->ctrl.mcr;
>> +        break;
>> +    case 0x0001:
>> +        val = s->ctrl.ppr;
>> +        break;
>> +    case 0x0004: /* status ? */
>> +        /* monitor code in bits 1..3 */
>> +        val = CG14_MONID_DEFAULT << 1;
>> +        break;
>> +    case 0x0006: /* hw version */
>> +        //val = 0x00; /* old version */
>> +        val = 0x30;
>> +        break;
>> +    default:
>> +        val = 0;
>> +        break;
>> +    }
>> +    CG14_INFO("readb %02x from reg %x\n", val, (int)addr);
>> +
>> +    return val;
>> +}
>> +
>> +static void cg14_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    CG14State *s = opaque;
>> +    uint32_t i;
>> +
>> +    if ((addr & 0xfcff) == 0x2000) {
>> +        i = (addr & 0x300) >> 8;
>> +        ADV7152_write(&s->dac, i, val);
>> +        return;
>> +    }
>> +    if ((addr & 0xff00) == 0x3000) {
>> +        /* xlut */
>> +        i = addr & 0xff;
>> +        if (s->xlut[i] != val) {
>> +            s->dirty = 1;
>> +            s->xlut[i] = val;
>> +            if (val && val != 0x40)
>> +                CG14_ERROR("writeb xlut[%d] = %02x\n", i, val);
>> +        }
>> +        return;
>> +    }
>> +
>> +    s->dirty = 1;
>> +
>> +    switch (addr & 0xffff) {
>> +    case 0x0000:
>> +        s->ctrl.mcr = val;
>> +        break;
>> +    case 0x0001:
>> +        s->ctrl.ppr = val & 0xF0;
>> +        break;
>> +    case 0x0007:
>> +        /* clock control (ICS1562AM-001) */
>> +        DPRINTF("write %02x to clock control\n", val);
>> +        break;
>> +    default:
>> +        CG14_ERROR("writeb %02x to reg %x\n", val, (int)addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static uint32_t cg14_reg_readw(void *opaque, target_phys_addr_t addr)
>> +{
>> +    CG14State *s = opaque;
>> +    unsigned int val;
>> +
>> +    switch (addr & 0xffff) {
>> +    case 0x0018:
>> +        val = s->timing.hblank_start;
>> +        break;
>> +    case 0x001a:
>> +        val = s->timing.hblank_clear;
>> +        break;
>> +    case 0x0022:
>> +        val = s->timing.vblank_start;
>> +        break;
>> +    case 0x0024:
>> +        val = s->timing.vblank_clear;
>> +        break;
>> +    default:
>> +        val = 0;
>> +        break;
>> +    }
>> +    CG14_INFO("readw 0x%08x from reg %x\n", val, (int)addr);
>> +
>> +    return val;
>> +}
>> +
>> +static void cg14_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    CG14State *s = opaque;
>> +
>> +    CG14_INFO("writew %04x to reg %x\n", val, (int)addr);
>> +
>> +    /* timing registers are 16bit */
>> +
>> +    switch (addr & 0xffff) {
>> +    case 0x0018:
>> +        s->timing.hblank_start = val;
>> +        break;
>> +    case 0x001a:
>> +        s->timing.hblank_clear = val;
>> +       s->size_changed = 1;
>> +        break;
>> +    case 0x0022:
>> +        s->timing.vblank_start = val;
>> +        break;
>> +    case 0x0024:
>> +        s->timing.vblank_clear = val;
>> +       s->size_changed = 1;
>> +        break;
>> +    case 0x001c: /* hsync_start */
>> +    case 0x001e: /* hsync_clear */
>> +    case 0x0020: /* csync_clear */
>> +    case 0x0026: /* vsync_start */
>> +    case 0x0028: /* vsync_clear */
>> +    default:
>> +        break;
>> +    }
>> +}
>> +
>> +static uint32_t cg14_reg_readl(void *opaque, target_phys_addr_t addr)
>> +{
>> +    CG14State *s = opaque;
>> +    uint32_t val;
>> +    uint32_t i;
>> +
>> +    i = (addr & 0x3ff) >> 2;
>> +    switch (addr & 0xfc00) {
>> +    case 0x4000:
>> +        val = s->clut1[i];
>> +        break;
>> +    case 0x5000:
>> +        val = s->clut2[i];
>> +        break;
>> +    default:
>> +        val = 0;
>> +        CG14_ERROR("readl %08x from reg %x\n", val, (int)addr);
>> +        break;
>> +    }
>> +
>> +    return val;
>> +}
>> +
>> +static void cg14_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    CG14State *s = opaque;
>> +    uint32_t i;
>> +
>> +    s->dirty = 1;
>> +
>> +    i = (addr & 0x3ff) >> 2;
>> +    switch (addr & 0xfc00) {
>> +    case 0x4000:
>> +        s->clut1[i] = val;
>> +        break;
>> +    case 0x5000:
>> +        s->clut2[i] = val;
>> +        break;
>> +    default:
>> +        CG14_ERROR("writel %08x to reg %x\n", val, (int)addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static CPUReadMemoryFunc *cg14_reg_read[3] = {
>> +    cg14_reg_readb,
>> +    cg14_reg_readw,
>> +    cg14_reg_readl,
>> +};
>> +
>> +static CPUWriteMemoryFunc *cg14_reg_write[3] = {
>> +    cg14_reg_writeb,
>> +    cg14_reg_writew,
>> +    cg14_reg_writel,
>> +};
>> +
>> +static uint32_t cg14_vram_readb(void *opaque, target_phys_addr_t addr)
>> +{
>> +    CG14State *s = opaque;
>> +    uint32_t offset;
>> +    uint32_t val = 0;
>> +
>> +    switch (addr & 0x3000000) {
>> +    case 0x0000000:
>> +        offset = addr & s->vram_amask;
>> +        val =  ldub_p(s->vram+offset);
>> +        break;
>> +    case 0x1000000:
>> +        offset = addr & s->vram_amask;
>> +        val = 0; // FIXME
>> +        break;
>> +    case 0x2000000:
>> +        offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1);
>> +        val =  ldub_p(s->vram+offset);
>> +        break;
>> +    case 0x3000000:
>> +        offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3);
>> +        val =  ldub_p(s->vram+offset);
>> +        break;
>> +    }
>> +    CG14_INFO("readb %02x from vram %x\n", val, (int)addr);
>> +
>> +    return val;
>> +}
>> +
>> +static void cg14_vram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    CG14State *s = opaque;
>> +    uint32_t offset;
>> +
>> +    switch (addr & 0x3000000) {
>> +    case 0x0000000:
>> +        offset = addr & s->vram_amask;
>> +        stb_p(s->vram+offset, val);
>> +        if (offset < 4 * s->width * s->height) {
>> +            s->dirty = 1;
>> +        }
>> +        break;
>> +    default:
>> +        CG14_ERROR("writeb %02x to vram %x\n", val, (int)addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static uint32_t cg14_vram_readw(void *opaque, target_phys_addr_t addr)
>> +{
>> +    CG14State *s = opaque;
>> +    uint32_t offset;
>> +    uint32_t val;
>> +
>> +    switch (addr & 0x3000000) {
>> +    default:
>> +        offset = addr & s->vram_amask;
>> +        val = 0;
>> +        break;
>> +    }
>> +    CG14_ERROR("readw %04x from vram %x\n", val, (int)addr);
>> +
>> +    return val;
>> +}
>> +
>> +static void cg14_vram_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    CG14State *s = opaque;
>> +
>> +    CG14_ERROR("writew %04x to vram %x\n", val, (int)addr);
>> +
>> +    s->dirty = 1;
>> +
>> +    switch (addr & 0x3000000) {
>> +    default:
>> +        break;
>> +    }
>> +}
>> +
>> +static uint32_t cg14_vram_readl(void *opaque, target_phys_addr_t addr)
>> +{
>> +    CG14State *s = opaque;
>> +    uint32_t offset;
>> +    uint32_t val = 0;
>> +
>> +    switch (addr & 0x3000000) {
>> +    case 0x0000000:
>> +        offset = addr & s->vram_amask;
>> +        val = ldl_be_p(s->vram+offset);
>> +        break;
>> +    case 0x1000000:
>> +    case 0x2000000:
>> +    case 0x3000000:
>> +        CG14_ERROR("readl %08x from vram %x\n", val, (int)addr);
>> +        break;
>> +    }
>> +
>> +    return val;
>> +}
>> +
>> +static void cg14_vram_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    CG14State *s = opaque;
>> +    uint32_t offset;
>> +
>> +    switch (addr & 0x3000000) {
>> +    case 0x0000000:
>> +        offset = addr & s->vram_amask;
>> +        stl_be_p(s->vram+offset, val);
>> +        if (offset < 4 * s->width * s->height) {
>> +            s->dirty = 1;
>> +        }
>> +        break;
>> +    case 0x1000000:
>> +    case 0x2000000:
>> +    case 0x3000000:
>> +        CG14_ERROR("writel %08x to vram %x\n", val, (int)addr);
>> +        break;
>> +    }
>> +}
>> +
>> +static CPUReadMemoryFunc *cg14_vram_read[3] = {
>> +    cg14_vram_readb,
>> +    cg14_vram_readw,
>> +    cg14_vram_readl,
>> +};
>> +
>> +static CPUWriteMemoryFunc *cg14_vram_write[3] = {
>> +    cg14_vram_writeb,
>> +    cg14_vram_writew,
>> +    cg14_vram_writel,
>> +};
>> +
>> +
>> +/******** SX *********/
>> +
>> +static uint32_t sx_reg_readb(void *opaque, target_phys_addr_t addr)
>> +{
>> +    //CG14State *s = opaque;
>> +    int val;
>> +
>> +    printf("SX readb reg " TARGET_FMT_plx "\n", addr);
>> +
>> +    switch (addr & 0xffff) {
>> +    default:
>> +        val = 0;
>> +        break;
>> +    }
>> +    return val;
>> +}
>> +
>> +static void sx_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    //CG14State *s = opaque;
>> +
>> +    printf("SX writeb %02x to reg " TARGET_FMT_plx "\n", val, addr);
>> +
>> +    switch (addr & 0xffff) {
>> +    default:
>> +        break;
>> +    }
>> +}
>> +
>> +static uint32_t sx_reg_readw(void *opaque, target_phys_addr_t addr)
>> +{
>> +    //CG14State *s = opaque;
>> +    int val;
>> +
>> +    printf("SX readw reg " TARGET_FMT_plx "\n", addr);
>> +
>> +    switch (addr & 0xffff) {
>> +    default:
>> +        val = 0;
>> +        break;
>> +    }
>> +    return val;
>> +}
>> +
>> +static void sx_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    //CG14State *s = opaque;
>> +
>> +    printf("SX writew %04x to reg " TARGET_FMT_plx "\n", val, addr);
>> +
>> +    switch (addr & 0xffff) {
>> +    default:
>> +        break;
>> +    }
>> +}
>> +
>> +static uint32_t sx_reg_readl(void *opaque, target_phys_addr_t addr)
>> +{
>> +    //CG14State *s = opaque;
>> +    int val;
>> +
>> +    printf("SX readl reg " TARGET_FMT_plx "\n", addr);
>> +
>> +    switch (addr & 0xffff) {
>> +    default:
>> +        val = 0;
>> +        break;
>> +    }
>> +    return val;
>> +}
>> +
>> +static void sx_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    //CG14State *s = opaque;
>> +
>> +    printf("SX writel %08x to reg " TARGET_FMT_plx "\n", val, addr);
>> +
>> +    switch (addr & 0xffff) {
>> +    default:
>> +        break;
>> +    }
>> +}
>> +
>> +static CPUReadMemoryFunc *sx_reg_read[3] = {
>> +    sx_reg_readb,
>> +    sx_reg_readw,
>> +    sx_reg_readl,
>> +};
>> +
>> +static CPUWriteMemoryFunc *sx_reg_write[3] = {
>> +    sx_reg_writeb,
>> +    sx_reg_writew,
>> +    sx_reg_writel,
>> +};
>> +
>> +/*********************/
>> +
>> +static uint32_t bad_mem_read(void *opaque, target_phys_addr_t addr)
>> +{
>> +    printf("Bad read from " TARGET_FMT_plx "\n", addr);
>> +    //cpu_abort(cpu_single_env, "bad ram read access at " TARGET_FMT_plx "\n", addr);
>> +    return 0;
>> +}
>> +static void bad_mem_write(void *opaque, target_phys_addr_t addr, uint32_t val)
>> +{
>> +    printf("Bad write of 0x%02x to " TARGET_FMT_plx "\n", val, addr);
>> +    //cpu_abort(cpu_single_env, "bad ram write access at " TARGET_FMT_plx "\n", addr);
>> +}
>> +static CPUReadMemoryFunc *bad_memr[3] = { bad_mem_read, bad_mem_read, bad_mem_read };
>> +static CPUWriteMemoryFunc *bad_memw[3] = { bad_mem_write, bad_mem_write, bad_mem_write };
>> +
>> +void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
>> +                uint32_t vram_size)
>> +{
>> +//    DeviceState *dev;
>> +//    SysBusDevice *s;
>> +
>> +//    dev = qdev_create(NULL, "SUNW,cg14");
>> +//    qdev_init(dev);
>> +//    s = sysbus_from_qdev(dev);
>> +//}
>> +
>> +//static void cg14_init1(SysBusDevice *dev)
>> +//{
>> +    CG14State *s;// = FROM_SYSBUS(CG14State, dev);
>> +    ram_addr_t vram_offset;
>> +    uint8_t *vram;
>> +    int ctrl_memory, vram_memory;
>> +    int sx_registers;
>> +    int bad_mem;
>> +
>> +    s = qemu_mallocz(sizeof(CG14State));
>> +
>> +    vram_offset = qemu_ram_alloc(vram_size);
>> +    vram = qemu_get_ram_ptr(vram_offset);
>> +
>> +    s->vram = vram;
>> +    s->vram_amask = vram_size - 1;
>> +
>> +    ctrl_memory = cpu_register_io_memory(cg14_reg_read, cg14_reg_write, s);
>> +    cpu_register_physical_memory_offset(ctrl_base, CG14_REG_SIZE, ctrl_memory, ctrl_base);
>> +
>> +    vram_memory = cpu_register_io_memory(cg14_vram_read, cg14_vram_write, s);
>> +    cpu_register_physical_memory_offset(vram_base, CG14_VMEM_SLOTSIZE, vram_memory, vram_base);
>> +
>> +    s->ds = graphic_console_init(cg14_update_display,
>> +                                 cg14_invalidate_display,
>> +                                 cg14_screen_dump, NULL, s);
>> +
>> +    s->width = 640;
>> +    s->height = 480;
>> +    qemu_console_resize(s->ds, s->width, s->height);
>> +
>> +    /* SX or SPAM (Sun Pixel Arithmetic Memory) */
>> +    sx_registers = cpu_register_io_memory(sx_reg_read, sx_reg_write, s);
>> +    cpu_register_physical_memory(0xf80000000ULL, 0x2000, sx_registers);
>> +
>> +    bad_mem = cpu_register_io_memory(bad_memr, bad_memw, s);
>> +    /* missing vsimms */
>> +    cpu_register_physical_memory_offset(0x90000000, 0x2000, bad_mem, 0x90000000);
>> +    cpu_register_physical_memory_offset(0x94000000, 0x2000, bad_mem, 0x94000000);
>> +    cpu_register_physical_memory_offset(0x98000000, 0x2000, bad_mem, 0x98000000);
>
> Can't we just have more VSIMMS? The empty_slot device may be useful here.

And btw, VSIMMs addresses may currently overlap with RAM. Maybe we
should keep "SS-20" (and SS-10) compatible with original, and rename
the current machine definition to "SS-20qemu" or SS-20-60G?

>
>> +    /* DBRI (audio) */
>> +    cpu_register_physical_memory_offset(0xEE0001000ULL, 0x10000, bad_mem, 0xE0001000);
>
> Please add a new DBRI device ;-).

Or maybe just a field in hwdef + empty_slot? :-)

>
>> +}
>> +
>> +/* save to file */
>> +static void cg14_screen_dump(void *opaque, const char *filename)
>> +{
>> +    CG14State *s = opaque;
>> +    FILE *f;
>> +    int y, pixmode, linesize;
>> +    void *buf;
>> +    uint8_t *pix;
>> +
>> +    f = fopen(filename, "wb");
>> +    if (!f) {
>> +        return;
>> +    }
>> +    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
>> +
>> +    linesize = s->width * 3;
>> +    buf = qemu_mallocz(linesize);
>> +    pix = s->vram;
>> +
>> +    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
>> +    case MCR_PIXMODE_32:
>> +        pixmode = 32;
>> +        break;
>> +    case MCR_PIXMODE_16:
>> +        pixmode = 16;
>> +        break;
>> +    case MCR_PIXMODE_8:
>> +    default:
>> +        pixmode = 8;
>> +        break;
>> +    }
>> +
>> +    for (y=0; y<s->height; y++) {
>> +        // cg14_draw_line24_bgr(s, buf, pix, pixmode);
>> +        fwrite(buf, 1, linesize, f);
>> +        pix += s->width * (pixmode / 8);
>> +    }
>> +
>> +    qemu_free(buf);
>> +    fclose(f);
>> +}
>>
>>
>
Bob Breuer May 29, 2010, 5:15 a.m. UTC | #3
Artyom Tarasenko wrote:
> 2010/5/28 Blue Swirl <blauwirbel@gmail.com>:
>   
>> On Fri, May 28, 2010 at 7:54 AM, Bob Breuer <breuerr@mc.net> wrote:
>>     
>>> Artyom Tarasenko wrote:
>>>       
>>>> 2010/5/27 Bob Breuer <breuerr@mc.net>:
>>>>
>>>>         
>>>>> Artyom Tarasenko wrote:
>>>>>
>>>>>           
>>>>>> Was going to put some more empty slots into SS-10/20 (VSIMMs, SX)
>>>>>> after we are done with SS-5 (due to technical limitations I can switch
>>>>>> access from one real SS model to another one once a few days only).
>>>>>>
>>>>>>
>>>>>>             
>>>>> I have a partial implementation of the SS-20 VSIMM (cg14) that I've been
>>>>> working on.  With the Sun firmware, I have working text console, color
>>>>> boot logo, and programmable video resolutions up to 1600x1280.
>>>>>
>>>>>           
>>>> Great news! This would allow qemu booting NeXTStep! Are you planning
>>>> to submit the patches any time soon?
>>>>
>>>>
>>>>         
>>> It's not in a state to be submitted yet, but I've attached a working
>>> patch if you want to give it a try.  I need to hook it up to qdev and
>>> fill in some more of the obviously incomplete switch cases before I'd
>>> sign off on it.
>>>       
>> Nice work. I have a few comments below.
>>
>> This probably needs support from OpenBIOS to be usable without OBP.
>>     
>
> Maybe it can be used as a second adapter without OpenBIOS support? At
> least under some OSes?
>
>   
Probably won't be used without at least being in the firmware device
tree.  One area that OpenBIOS could enhance would be a larger memory
size option.  The real hardware was only available in 4M and 8M options,
but the memory map allows for 16M.  OBP will identify a 16M VSIMM but
won't do anything else with it, and with 16M of vram it would allow for
a potential 2560x1600 32bit resolution.
>>> Bob
>>>
>>>
>>> diff --git a/Makefile.target b/Makefile.target
>>> index fda5bf3..b17b3af 100644
>>> --- a/Makefile.target
>>> +++ b/Makefile.target
>>> @@ -250,6 +250,7 @@ else
>>>  obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>>  obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>>>  obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>>> +obj-sparc-y += cg14.o
>>>  endif
>>>
>>>  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>>> diff --git a/hw/sun4m.c b/hw/sun4m.c
>>> index 7ba0f76..8b23c9b 100644
>>> --- a/hw/sun4m.c
>>> +++ b/hw/sun4m.c
>>> @@ -864,6 +864,13 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
>>>         fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
>>>         exit (1);
>>>     }
>>> +  if (hwdef->machine_id == 65) { /* SS-20 */
>>>       
>> hwdef structure should contain a field for cg14. If non-zero, install
>> cg14. Was cg14 only available for SS-20? Was it always included? This
>> is also interesting for OpenBIOS, we need to detect cg14 vs. TCX.
>>     
The cg14 was only an option for SS-20 and the rare SS-10SX, but not the
regular SS-10, though the SS-10 chipset may have been capable of
supporting it.  Each cg14 vsimm takes the place of a stick of memory
with 2 slots physically capable of holding a vsimm.

Is there a way to pass the framebuffer type and/or address to OpenBIOS? 
I would be inclined to have the SS-20 machine default to cg14 instead of
TCX, but it will blow a hole in the support of more than 2G of emulated
system ram.
>>> +    /* cg14.c */
>>> +    void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
>>> +                uint32_t vram_size);
>>>       
>> This should go to sun4m.h or cg14.h.
>>
>>     
>>> +
>>> +    cg14_init(0x09c000000ULL, 0x0fc000000ULL, 8<<20);
>>> +  } else
>>>       
>> Please add braces and reindent.
>>
>>     
>>>     tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
>>>              graphic_depth);
>>>
>>> --- /dev/null   Fri May 28 02:08:36 2010
>>> +++ hw/cg14.c   Fri May 28 01:58:49 2010
>>> @@ -0,0 +1,785 @@
>>> +/*
>>> + * QEMU CG14 Frame buffer
>>> + *
>>> + * Copyright (c) 2010 Bob Breuer
>>> + *
>>> + * 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.
>>> + */
>>> +
>>> +#include "console.h"
>>> +#include "sysbus.h"
>>> +
>>> +#ifdef DEBUG
>>>       
>
> DEBUG_CG14 ?
>
>   
>>> +#define DPRINTF(fmt, ...)                                       \
>>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>>> +#else
>>> +#define DPRINTF(fmt, ...)
>>> +#endif
>>> +
>>> +#define CG14_INFO(fmt, ...)                                     \
>>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>>> +#define CG14_ERROR(fmt, ...)                                    \
>>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>>> +
>>> +/*
>>> + * A[28:26] = slot number (4 to 7)
>>> + * regs: size   0x10000 @ 0x09c000000  (0x80000000 + slot * 64M)
>>> + * vmem: size upto 16MB @ 0x0fc000000  (0xE0000000 + slot * 64M)
>>> + */
>>>       
>> If you have any links to chipset docs, it would be nice to mention those here.
>>     
Chipset docs are hard to come by.  But here's what I've found:
    "Sun-4M System Architecture" section A.II.3 briefly covers VSIMM and
DSIMM size detection.
    Linux kernel 2.6 drivers/video/cg14.c has most of the registers named.
    US Patent 5815137 covers the cursor implementation, but it includes
drawings which show the various color and lookup table muxing and
blending capability.  I will not be implementing the cursor functionality.

>>> +
>>> +/*
>>> + * memory map:
>>> + * reg+0x0000 = control registers
>>> + * reg+0x1000 = cursor registers
>>> + * reg+0x2000 = dac registers (ADV7152)
>>> + * reg+0x3000 = xlut
>>> + * reg+0x4000 = clut1
>>> + * reg+0x5000 = clut2
>>> + * reg+0x6000 = clut3 (if implemented)
>>> + *
>>> + * mem+0x0000000 = XBGR (01234567)
>>> + * mem+0x1000000 = BGR  (.123.567)
>>> + * mem+0x2000000 = X16  (0246)
>>> + * mem+0x2800000 = C16  (1357)
>>> + * mem+0x3000000 = X32  (04)
>>> + * mem+0x3400000 = B32  (15)
>>> + * mem+0x3800000 = G32  (26)
>>> + * mem+0x3c00000 = R32  (37)
>>>       
>> Interesting device. You could increase the performance a lot by making
>> the XBGR area ordinary memory and detecting the dirtyness with
>> VGA_DIRTY_FLAG like TCX. The other areas could use multiple byte
>> stores to the memory so dirty information would be updated.
>>     

Hmm, interesting.  Might be cumbersome if the width isn't 1024 though.

>>> + */
>>> +
>>> +#define CG14_REG_SIZE         0x10000
>>> +#define CG14_VMEM_SLOTSIZE    (64<<20)
>>> +
>>> +#define CG14_MONID_1024x768   0
>>> +#define CG14_MONID_1600x1280  1
>>> +#define CG14_MONID_1280x1024  2
>>> +#define CG14_MONID_1152x900   7
>>> +
>>> +#define CG14_MONID_DEFAULT    CG14_MONID_1024x768
>>> +
>>> +#define MCR_PIXMODE_MASK  0x30
>>> +#define   MCR_PIXMODE_8     0x00
>>> +#define   MCR_PIXMODE_16    0x20  /* 8+8 (X16,C16) */
>>> +#define   MCR_PIXMODE_32    0x30  /* XBGR */
>>> +
>>> +
>>> +struct ADV7152_state {
>>> +    uint8_t mode;
>>> +    uint8_t address;
>>> +    int rgb_seq;
>>> +};
>>> +
>>> +typedef struct CG14State {
>>> +    SysBusDevice busdev;
>>> +    DisplayState *ds;
>>> +
>>> +    uint8_t *vram;
>>> +    uint32_t vram_amask;
>>> +    int width, height;
>>> +    int dirty, size_changed;
>>> +    struct {
>>> +        uint8_t mcr;
>>> +        uint8_t ppr;
>>> +    } ctrl;
>>> +    struct ADV7152_state dac;
>>> +    struct {
>>> +        uint16_t hblank_start;
>>> +        uint16_t hblank_clear;
>>> +        uint16_t vblank_start;
>>> +        uint16_t vblank_clear;
>>> +    } timing;
>>> +    uint8_t xlut[256];
>>> +    uint32_t clut1[256];
>>> +    uint32_t clut2[256];
>>> +} CG14State;
>>> +
>>> +static void cg14_screen_dump(void *opaque, const char *filename);
>>> +static void cg14_invalidate_display(void *opaque);
>>> +
>>> +static inline uint32_t bgr_to_rgb(uint32_t bgr)
>>> +{
>>> +    uint32_t rgb;
>>> +
>>> +    /* swap r & b */
>>> +    rgb = (bgr & 0x00FF00)
>>> +        | (bgr & 0x0000FF) << 16
>>> +        | (bgr & 0xFF0000) >> 16;
>>> +    return rgb;
>>> +}
>>> +
>>> +static void cg14_draw_line32(const CG14State *s, void *dst, const uint8_t *src, int pixmode, int is_bgr)
>>> +{
>>> +    int i;
>>> +    int x, r, g, b;
>>> +    uint8_t xlut_val;
>>> +    uint32_t dval;
>>> +    uint32_t abgr;
>>> +
>>> +    xlut_val = s->ctrl.ppr;
>>> +
>>> +    for (i=0; i<s->width; i++) {
>>> +        x = *src++;
>>> +        if (pixmode == 8) {
>>>       
>> To increase performance, pixmode should not be passed at all but
>> instead separate functions should be added for each mode and the
>> function should be selected before the line loop.
>>     

Yes, at least for 8bit mode.  The per-pixel xlut value offers a lot of
flexibility that may be hard to speed up when fully implemented.  The
hardware supports simultaneous true-color and palette lookups to
generate 2 color values to blend together for every displayed pixel.

>>> +            b = x;
>>> +        } else {
>>> +            b = *src++;
>>> +            xlut_val = s->xlut[x];
>>> +        }
>>> +        if (pixmode != 32) {
>>> +            r = g = b;
>>> +        } else {
>>> +            g = *src++;
>>> +            r = *src++;
>>> +        }
>>> +        if (xlut_val == 0) {
>>> +            abgr = b << 16 | g << 8 | r;
>>> +        } else if (xlut_val == 0x40) {
>>> +            abgr = s->clut1[x];
>>> +        } else {
>>> +            abgr = 0;
>>> +        }
>>> +        /* dac lookup ? */
>>> +
>>> +        /* to surface format */
>>> +        dval = is_bgr ? (abgr & 0xFFFFFF) : bgr_to_rgb(abgr);
>>> +        ((uint32_t*)dst)[i] = dval;
>>> +    }
>>> +}
>>> +
>>> +static void cg14_update_display(void *opaque)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    int h, pixmode;
>>> +    uint8_t *pix;
>>> +    uint8_t *data;
>>> +    int new_width, new_height;
>>> +
>>> +    if (s->size_changed) {
>>> +        new_width = 4 * (s->timing.hblank_start - s->timing.hblank_clear);
>>> +        new_height = s->timing.vblank_start - s->timing.vblank_clear;
>>> +        s->size_changed = 0;
>>> +        if ((new_width != s->width || new_height != s->height) && new_width > 0 && new_height > 0) {
>>> +            s->width = new_width;
>>> +            s->height = new_height;
>>> +            CG14_INFO("new resolution = %d x %d\n", new_width, new_height);
>>> +            qemu_console_resize(s->ds, s->width, s->height);
>>> +            s->dirty = 1;
>>> +        }
>>> +    }
>>> +
>>> +    if (!s->dirty || !s->width || !s->height) {
>>> +        return;
>>> +    }
>>> +
>>> +    if (ds_get_bits_per_pixel(s->ds) != 32) {
>>> +        CG14_ERROR("cg14_update: FIXME: bpp (%d) != 32, linesize %d\n",
>>> +            ds_get_bits_per_pixel(s->ds), ds_get_linesize(s->ds));
>>> +        return;
>>> +    }
>>> +
>>> +    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
>>> +    case MCR_PIXMODE_32:
>>> +        pixmode = 32;
>>> +        break;
>>> +    case MCR_PIXMODE_16:
>>> +        pixmode = 16;
>>> +        break;
>>> +    case MCR_PIXMODE_8:
>>> +    default:
>>> +        pixmode = 8;
>>> +        break;
>>> +    }
>>> +
>>> +    pix = s->vram;
>>> +    data = ds_get_data(s->ds);
>>> +
>>> +    for (h=0; h<s->height; h++) {
>>> +        cg14_draw_line32(s, data, pix, pixmode, is_surface_bgr(s->ds->surface));
>>> +        pix += s->width * (pixmode / 8);
>>> +        data += ds_get_linesize(s->ds);
>>> +    }
>>> +    dpy_update(s->ds, 0, 0, s->width, s->height);
>>> +    s->dirty = 0;
>>> +}
>>> +
>>> +static void cg14_invalidate_display(void *opaque)
>>> +{
>>> +    CG14State *s = opaque;
>>> +
>>> +    s->dirty = 1;
>>> +}
>>> +
>>> +static void ADV7152_write(struct ADV7152_state *s, unsigned int reg, unsigned int val)
>>> +{
>>> +    switch (reg) {
>>> +    case 0: /* address register */
>>> +        DPRINTF("ADV7152 Write address %02x\n", val);
>>> +        s->address = val;
>>> +        s->rgb_seq = 0;
>>> +        break;
>>> +    case 1: /* look up table */
>>> +        DPRINTF("ADV7152 Write %02x to lookup table\n", val);
>>> +        s->rgb_seq++;
>>> +        break;
>>> +    case 2: /* control registers */
>>> +        DPRINTF("ADV7152 Write %02x to control reg %d\n", val, s->address);
>>> +        switch (s->address) {
>>> +        default:
>>> +            break;
>>> +        }
>>> +        break;
>>> +    case 3: /* mode register */
>>> +        CG14_INFO("ADV7152 Write mode %02x (%d bit DAC, %d bit bus)\n",
>>> +            val, (val & 2) ? 10 : 8, (val & 4) ? 10 : 8);
>>> +        if (!val & 0x01) {
>>> +            // reset the dac
>>> +            s->rgb_seq = 0;
>>> +        }
>>> +        s->mode = val;
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static uint32_t cg14_reg_readb(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    unsigned int val;
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    case 0x0000:
>>> +        val = s->ctrl.mcr;
>>> +        break;
>>> +    case 0x0001:
>>> +        val = s->ctrl.ppr;
>>> +        break;
>>> +    case 0x0004: /* status ? */
>>> +        /* monitor code in bits 1..3 */
>>> +        val = CG14_MONID_DEFAULT << 1;
>>> +        break;
>>> +    case 0x0006: /* hw version */
>>> +        //val = 0x00; /* old version */
>>> +        val = 0x30;
>>> +        break;
>>> +    default:
>>> +        val = 0;
>>> +        break;
>>> +    }
>>> +    CG14_INFO("readb %02x from reg %x\n", val, (int)addr);
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +static void cg14_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    uint32_t i;
>>> +
>>> +    if ((addr & 0xfcff) == 0x2000) {
>>> +        i = (addr & 0x300) >> 8;
>>> +        ADV7152_write(&s->dac, i, val);
>>> +        return;
>>> +    }
>>> +    if ((addr & 0xff00) == 0x3000) {
>>> +        /* xlut */
>>> +        i = addr & 0xff;
>>> +        if (s->xlut[i] != val) {
>>> +            s->dirty = 1;
>>> +            s->xlut[i] = val;
>>> +            if (val && val != 0x40)
>>> +                CG14_ERROR("writeb xlut[%d] = %02x\n", i, val);
>>> +        }
>>> +        return;
>>> +    }
>>> +
>>> +    s->dirty = 1;
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    case 0x0000:
>>> +        s->ctrl.mcr = val;
>>> +        break;
>>> +    case 0x0001:
>>> +        s->ctrl.ppr = val & 0xF0;
>>> +        break;
>>> +    case 0x0007:
>>> +        /* clock control (ICS1562AM-001) */
>>> +        DPRINTF("write %02x to clock control\n", val);
>>> +        break;
>>> +    default:
>>> +        CG14_ERROR("writeb %02x to reg %x\n", val, (int)addr);
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static uint32_t cg14_reg_readw(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    unsigned int val;
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    case 0x0018:
>>> +        val = s->timing.hblank_start;
>>> +        break;
>>> +    case 0x001a:
>>> +        val = s->timing.hblank_clear;
>>> +        break;
>>> +    case 0x0022:
>>> +        val = s->timing.vblank_start;
>>> +        break;
>>> +    case 0x0024:
>>> +        val = s->timing.vblank_clear;
>>> +        break;
>>> +    default:
>>> +        val = 0;
>>> +        break;
>>> +    }
>>> +    CG14_INFO("readw 0x%08x from reg %x\n", val, (int)addr);
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +static void cg14_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    CG14State *s = opaque;
>>> +
>>> +    CG14_INFO("writew %04x to reg %x\n", val, (int)addr);
>>> +
>>> +    /* timing registers are 16bit */
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    case 0x0018:
>>> +        s->timing.hblank_start = val;
>>> +        break;
>>> +    case 0x001a:
>>> +        s->timing.hblank_clear = val;
>>> +       s->size_changed = 1;
>>> +        break;
>>> +    case 0x0022:
>>> +        s->timing.vblank_start = val;
>>> +        break;
>>> +    case 0x0024:
>>> +        s->timing.vblank_clear = val;
>>> +       s->size_changed = 1;
>>> +        break;
>>> +    case 0x001c: /* hsync_start */
>>> +    case 0x001e: /* hsync_clear */
>>> +    case 0x0020: /* csync_clear */
>>> +    case 0x0026: /* vsync_start */
>>> +    case 0x0028: /* vsync_clear */
>>> +    default:
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static uint32_t cg14_reg_readl(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    uint32_t val;
>>> +    uint32_t i;
>>> +
>>> +    i = (addr & 0x3ff) >> 2;
>>> +    switch (addr & 0xfc00) {
>>> +    case 0x4000:
>>> +        val = s->clut1[i];
>>> +        break;
>>> +    case 0x5000:
>>> +        val = s->clut2[i];
>>> +        break;
>>> +    default:
>>> +        val = 0;
>>> +        CG14_ERROR("readl %08x from reg %x\n", val, (int)addr);
>>> +        break;
>>> +    }
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +static void cg14_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    uint32_t i;
>>> +
>>> +    s->dirty = 1;
>>> +
>>> +    i = (addr & 0x3ff) >> 2;
>>> +    switch (addr & 0xfc00) {
>>> +    case 0x4000:
>>> +        s->clut1[i] = val;
>>> +        break;
>>> +    case 0x5000:
>>> +        s->clut2[i] = val;
>>> +        break;
>>> +    default:
>>> +        CG14_ERROR("writel %08x to reg %x\n", val, (int)addr);
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static CPUReadMemoryFunc *cg14_reg_read[3] = {
>>> +    cg14_reg_readb,
>>> +    cg14_reg_readw,
>>> +    cg14_reg_readl,
>>> +};
>>> +
>>> +static CPUWriteMemoryFunc *cg14_reg_write[3] = {
>>> +    cg14_reg_writeb,
>>> +    cg14_reg_writew,
>>> +    cg14_reg_writel,
>>> +};
>>> +
>>> +static uint32_t cg14_vram_readb(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    uint32_t offset;
>>> +    uint32_t val = 0;
>>> +
>>> +    switch (addr & 0x3000000) {
>>> +    case 0x0000000:
>>> +        offset = addr & s->vram_amask;
>>> +        val =  ldub_p(s->vram+offset);
>>> +        break;
>>> +    case 0x1000000:
>>> +        offset = addr & s->vram_amask;
>>> +        val = 0; // FIXME
>>> +        break;
>>> +    case 0x2000000:
>>> +        offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1);
>>> +        val =  ldub_p(s->vram+offset);
>>> +        break;
>>> +    case 0x3000000:
>>> +        offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3);
>>> +        val =  ldub_p(s->vram+offset);
>>> +        break;
>>> +    }
>>> +    CG14_INFO("readb %02x from vram %x\n", val, (int)addr);
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +static void cg14_vram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    uint32_t offset;
>>> +
>>> +    switch (addr & 0x3000000) {
>>> +    case 0x0000000:
>>> +        offset = addr & s->vram_amask;
>>> +        stb_p(s->vram+offset, val);
>>> +        if (offset < 4 * s->width * s->height) {
>>> +            s->dirty = 1;
>>> +        }
>>> +        break;
>>> +    default:
>>> +        CG14_ERROR("writeb %02x to vram %x\n", val, (int)addr);
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static uint32_t cg14_vram_readw(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    uint32_t offset;
>>> +    uint32_t val;
>>> +
>>> +    switch (addr & 0x3000000) {
>>> +    default:
>>> +        offset = addr & s->vram_amask;
>>> +        val = 0;
>>> +        break;
>>> +    }
>>> +    CG14_ERROR("readw %04x from vram %x\n", val, (int)addr);
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +static void cg14_vram_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    CG14State *s = opaque;
>>> +
>>> +    CG14_ERROR("writew %04x to vram %x\n", val, (int)addr);
>>> +
>>> +    s->dirty = 1;
>>> +
>>> +    switch (addr & 0x3000000) {
>>> +    default:
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static uint32_t cg14_vram_readl(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    uint32_t offset;
>>> +    uint32_t val = 0;
>>> +
>>> +    switch (addr & 0x3000000) {
>>> +    case 0x0000000:
>>> +        offset = addr & s->vram_amask;
>>> +        val = ldl_be_p(s->vram+offset);
>>> +        break;
>>> +    case 0x1000000:
>>> +    case 0x2000000:
>>> +    case 0x3000000:
>>> +        CG14_ERROR("readl %08x from vram %x\n", val, (int)addr);
>>> +        break;
>>> +    }
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +static void cg14_vram_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    uint32_t offset;
>>> +
>>> +    switch (addr & 0x3000000) {
>>> +    case 0x0000000:
>>> +        offset = addr & s->vram_amask;
>>> +        stl_be_p(s->vram+offset, val);
>>> +        if (offset < 4 * s->width * s->height) {
>>> +            s->dirty = 1;
>>> +        }
>>> +        break;
>>> +    case 0x1000000:
>>> +    case 0x2000000:
>>> +    case 0x3000000:
>>> +        CG14_ERROR("writel %08x to vram %x\n", val, (int)addr);
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static CPUReadMemoryFunc *cg14_vram_read[3] = {
>>> +    cg14_vram_readb,
>>> +    cg14_vram_readw,
>>> +    cg14_vram_readl,
>>> +};
>>> +
>>> +static CPUWriteMemoryFunc *cg14_vram_write[3] = {
>>> +    cg14_vram_writeb,
>>> +    cg14_vram_writew,
>>> +    cg14_vram_writel,
>>> +};
>>> +
>>> +
>>> +/******** SX *********/
>>> +
>>> +static uint32_t sx_reg_readb(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    //CG14State *s = opaque;
>>> +    int val;
>>> +
>>> +    printf("SX readb reg " TARGET_FMT_plx "\n", addr);
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    default:
>>> +        val = 0;
>>> +        break;
>>> +    }
>>> +    return val;
>>> +}
>>> +
>>> +static void sx_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    //CG14State *s = opaque;
>>> +
>>> +    printf("SX writeb %02x to reg " TARGET_FMT_plx "\n", val, addr);
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    default:
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static uint32_t sx_reg_readw(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    //CG14State *s = opaque;
>>> +    int val;
>>> +
>>> +    printf("SX readw reg " TARGET_FMT_plx "\n", addr);
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    default:
>>> +        val = 0;
>>> +        break;
>>> +    }
>>> +    return val;
>>> +}
>>> +
>>> +static void sx_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    //CG14State *s = opaque;
>>> +
>>> +    printf("SX writew %04x to reg " TARGET_FMT_plx "\n", val, addr);
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    default:
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static uint32_t sx_reg_readl(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    //CG14State *s = opaque;
>>> +    int val;
>>> +
>>> +    printf("SX readl reg " TARGET_FMT_plx "\n", addr);
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    default:
>>> +        val = 0;
>>> +        break;
>>> +    }
>>> +    return val;
>>> +}
>>> +
>>> +static void sx_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    //CG14State *s = opaque;
>>> +
>>> +    printf("SX writel %08x to reg " TARGET_FMT_plx "\n", val, addr);
>>> +
>>> +    switch (addr & 0xffff) {
>>> +    default:
>>> +        break;
>>> +    }
>>> +}
>>> +
>>> +static CPUReadMemoryFunc *sx_reg_read[3] = {
>>> +    sx_reg_readb,
>>> +    sx_reg_readw,
>>> +    sx_reg_readl,
>>> +};
>>> +
>>> +static CPUWriteMemoryFunc *sx_reg_write[3] = {
>>> +    sx_reg_writeb,
>>> +    sx_reg_writew,
>>> +    sx_reg_writel,
>>> +};
>>> +
>>> +/*********************/
>>> +
>>> +static uint32_t bad_mem_read(void *opaque, target_phys_addr_t addr)
>>> +{
>>> +    printf("Bad read from " TARGET_FMT_plx "\n", addr);
>>> +    //cpu_abort(cpu_single_env, "bad ram read access at " TARGET_FMT_plx "\n", addr);
>>> +    return 0;
>>> +}
>>> +static void bad_mem_write(void *opaque, target_phys_addr_t addr, uint32_t val)
>>> +{
>>> +    printf("Bad write of 0x%02x to " TARGET_FMT_plx "\n", val, addr);
>>> +    //cpu_abort(cpu_single_env, "bad ram write access at " TARGET_FMT_plx "\n", addr);
>>> +}
>>> +static CPUReadMemoryFunc *bad_memr[3] = { bad_mem_read, bad_mem_read, bad_mem_read };
>>> +static CPUWriteMemoryFunc *bad_memw[3] = { bad_mem_write, bad_mem_write, bad_mem_write };
>>> +
>>> +void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
>>> +                uint32_t vram_size)
>>> +{
>>> +//    DeviceState *dev;
>>> +//    SysBusDevice *s;
>>> +
>>> +//    dev = qdev_create(NULL, "SUNW,cg14");
>>> +//    qdev_init(dev);
>>> +//    s = sysbus_from_qdev(dev);
>>> +//}
>>> +
>>> +//static void cg14_init1(SysBusDevice *dev)
>>> +//{
>>> +    CG14State *s;// = FROM_SYSBUS(CG14State, dev);
>>> +    ram_addr_t vram_offset;
>>> +    uint8_t *vram;
>>> +    int ctrl_memory, vram_memory;
>>> +    int sx_registers;
>>> +    int bad_mem;
>>> +
>>> +    s = qemu_mallocz(sizeof(CG14State));
>>> +
>>> +    vram_offset = qemu_ram_alloc(vram_size);
>>> +    vram = qemu_get_ram_ptr(vram_offset);
>>> +
>>> +    s->vram = vram;
>>> +    s->vram_amask = vram_size - 1;
>>> +
>>> +    ctrl_memory = cpu_register_io_memory(cg14_reg_read, cg14_reg_write, s);
>>> +    cpu_register_physical_memory_offset(ctrl_base, CG14_REG_SIZE, ctrl_memory, ctrl_base);
>>> +
>>> +    vram_memory = cpu_register_io_memory(cg14_vram_read, cg14_vram_write, s);
>>> +    cpu_register_physical_memory_offset(vram_base, CG14_VMEM_SLOTSIZE, vram_memory, vram_base);
>>> +
>>> +    s->ds = graphic_console_init(cg14_update_display,
>>> +                                 cg14_invalidate_display,
>>> +                                 cg14_screen_dump, NULL, s);
>>> +
>>> +    s->width = 640;
>>> +    s->height = 480;
>>> +    qemu_console_resize(s->ds, s->width, s->height);
>>> +
>>> +    /* SX or SPAM (Sun Pixel Arithmetic Memory) */
>>> +    sx_registers = cpu_register_io_memory(sx_reg_read, sx_reg_write, s);
>>> +    cpu_register_physical_memory(0xf80000000ULL, 0x2000, sx_registers);
>>> +
>>> +    bad_mem = cpu_register_io_memory(bad_memr, bad_memw, s);
>>> +    /* missing vsimms */
>>> +    cpu_register_physical_memory_offset(0x90000000, 0x2000, bad_mem, 0x90000000);
>>> +    cpu_register_physical_memory_offset(0x94000000, 0x2000, bad_mem, 0x94000000);
>>> +    cpu_register_physical_memory_offset(0x98000000, 0x2000, bad_mem, 0x98000000);
>>>       
>> Can't we just have more VSIMMS? The empty_slot device may be useful here.
>>     
Each VSIMM would have it's own monitor attached.  Does Qemu and/or
OpenBIOS support multiple displays?  I think the limit for a real SS-20
is 5 displays - 2 VSIMMs and 3 GX/TGX cards.  I'll just go with
empty_slot for the extras.
>
> And btw, VSIMMs addresses may currently overlap with RAM. Maybe we
> should keep "SS-20" (and SS-10) compatible with original, and rename
> the current machine definition to "SS-20qemu" or SS-20-60G?
>   
Well, the real machines are limited to 512M, so dropping the framebuffer
registers into the middle of the first 4G wasn't an issue back then.  I
think the SX accelerator also limits the system and video memory to the
lower 4G of address space.

>>> +    /* DBRI (audio) */
>>> +    cpu_register_physical_memory_offset(0xEE0001000ULL, 0x10000, bad_mem, 0xE0001000);
>>>       
>> Please add a new DBRI device ;-).
>>     
>
> Or maybe just a field in hwdef + empty_slot? :-)
>   
Yes, empty_slot should work fine.  I was trying to hack in enough to get
it to boot, but no luck yet.  The cpu models are still lacking - with
the SS-20 v2.25 rom, SuperSparc 61 fails before initializing the
display, SuperSparc 60 has a data access error when trying to boot, and
Ross 625 is mis-detected as an incompatible cpu type.

>>> +}
>>> +
>>> +/* save to file */
>>> +static void cg14_screen_dump(void *opaque, const char *filename)
>>> +{
>>> +    CG14State *s = opaque;
>>> +    FILE *f;
>>> +    int y, pixmode, linesize;
>>> +    void *buf;
>>> +    uint8_t *pix;
>>> +
>>> +    f = fopen(filename, "wb");
>>> +    if (!f) {
>>> +        return;
>>> +    }
>>> +    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
>>> +
>>> +    linesize = s->width * 3;
>>> +    buf = qemu_mallocz(linesize);
>>> +    pix = s->vram;
>>> +
>>> +    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
>>> +    case MCR_PIXMODE_32:
>>> +        pixmode = 32;
>>> +        break;
>>> +    case MCR_PIXMODE_16:
>>> +        pixmode = 16;
>>> +        break;
>>> +    case MCR_PIXMODE_8:
>>> +    default:
>>> +        pixmode = 8;
>>> +        break;
>>> +    }
>>> +
>>> +    for (y=0; y<s->height; y++) {
>>> +        // cg14_draw_line24_bgr(s, buf, pix, pixmode);
>>> +        fwrite(buf, 1, linesize, f);
>>> +        pix += s->width * (pixmode / 8);
>>> +    }
>>> +
>>> +    qemu_free(buf);
>>> +    fclose(f);
>>> +}
>>>
>>>
Blue Swirl May 29, 2010, 8:55 a.m. UTC | #4
On Sat, May 29, 2010 at 5:15 AM, Bob Breuer <breuerr@mc.net> wrote:
> Artyom Tarasenko wrote:
>> 2010/5/28 Blue Swirl <blauwirbel@gmail.com>:
>>
>>> On Fri, May 28, 2010 at 7:54 AM, Bob Breuer <breuerr@mc.net> wrote:
>>>
>>>> Artyom Tarasenko wrote:
>>>>
>>>>> 2010/5/27 Bob Breuer <breuerr@mc.net>:
>>>>>
>>>>>
>>>>>> Artyom Tarasenko wrote:
>>>>>>
>>>>>>
>>>>>>> Was going to put some more empty slots into SS-10/20 (VSIMMs, SX)
>>>>>>> after we are done with SS-5 (due to technical limitations I can switch
>>>>>>> access from one real SS model to another one once a few days only).
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> I have a partial implementation of the SS-20 VSIMM (cg14) that I've been
>>>>>> working on.  With the Sun firmware, I have working text console, color
>>>>>> boot logo, and programmable video resolutions up to 1600x1280.
>>>>>>
>>>>>>
>>>>> Great news! This would allow qemu booting NeXTStep! Are you planning
>>>>> to submit the patches any time soon?
>>>>>
>>>>>
>>>>>
>>>> It's not in a state to be submitted yet, but I've attached a working
>>>> patch if you want to give it a try.  I need to hook it up to qdev and
>>>> fill in some more of the obviously incomplete switch cases before I'd
>>>> sign off on it.
>>>>
>>> Nice work. I have a few comments below.
>>>
>>> This probably needs support from OpenBIOS to be usable without OBP.
>>>
>>
>> Maybe it can be used as a second adapter without OpenBIOS support? At
>> least under some OSes?
>>
>>
> Probably won't be used without at least being in the firmware device
> tree.  One area that OpenBIOS could enhance would be a larger memory
> size option.  The real hardware was only available in 4M and 8M options,
> but the memory map allows for 16M.  OBP will identify a 16M VSIMM but
> won't do anything else with it, and with 16M of vram it would allow for
> a potential 2560x1600 32bit resolution.

Awesome, PC with Cirrus VGA can't compete.

>>>> Bob
>>>>
>>>>
>>>> diff --git a/Makefile.target b/Makefile.target
>>>> index fda5bf3..b17b3af 100644
>>>> --- a/Makefile.target
>>>> +++ b/Makefile.target
>>>> @@ -250,6 +250,7 @@ else
>>>>  obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>>>  obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>>>>  obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>>>> +obj-sparc-y += cg14.o
>>>>  endif
>>>>
>>>>  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>>>> diff --git a/hw/sun4m.c b/hw/sun4m.c
>>>> index 7ba0f76..8b23c9b 100644
>>>> --- a/hw/sun4m.c
>>>> +++ b/hw/sun4m.c
>>>> @@ -864,6 +864,13 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
>>>>         fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
>>>>         exit (1);
>>>>     }
>>>> +  if (hwdef->machine_id == 65) { /* SS-20 */
>>>>
>>> hwdef structure should contain a field for cg14. If non-zero, install
>>> cg14. Was cg14 only available for SS-20? Was it always included? This
>>> is also interesting for OpenBIOS, we need to detect cg14 vs. TCX.
>>>
> The cg14 was only an option for SS-20 and the rare SS-10SX, but not the
> regular SS-10, though the SS-10 chipset may have been capable of
> supporting it.  Each cg14 vsimm takes the place of a stick of memory
> with 2 slots physically capable of holding a vsimm.
>
> Is there a way to pass the framebuffer type and/or address to OpenBIOS?

Currently there's fw_cfg and we can also set NVRAM variables.

> I would be inclined to have the SS-20 machine default to cg14 instead of
> TCX, but it will blow a hole in the support of more than 2G of emulated
> system ram.

I'm not so worried about the RAM hole, the real machines didn't have
so much RAM and we can also always add more RAM at some other
location.

But the machine default is a bit of a problem because we would not be
compatible to old versions. Adding a new machine for each new
configuration does not sound viable in longer term either. In this
case it makes a lot of sense for SS-10SX and we could deprecate SS-20
with TCX.

A generic solution for major device changes could be to pass a device
tree (FDT), like some KVM PPC machines do. Another solution, which
would give most realistic emulation, would be to add FCode ROMs which
is used to probe and add the devices by OBP or OpenBIOS.

>>>> +    /* cg14.c */
>>>> +    void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
>>>> +                uint32_t vram_size);
>>>>
>>> This should go to sun4m.h or cg14.h.
>>>
>>>
>>>> +
>>>> +    cg14_init(0x09c000000ULL, 0x0fc000000ULL, 8<<20);
>>>> +  } else
>>>>
>>> Please add braces and reindent.
>>>
>>>
>>>>     tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
>>>>              graphic_depth);
>>>>
>>>> --- /dev/null   Fri May 28 02:08:36 2010
>>>> +++ hw/cg14.c   Fri May 28 01:58:49 2010
>>>> @@ -0,0 +1,785 @@
>>>> +/*
>>>> + * QEMU CG14 Frame buffer
>>>> + *
>>>> + * Copyright (c) 2010 Bob Breuer
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +
>>>> +#include "console.h"
>>>> +#include "sysbus.h"
>>>> +
>>>> +#ifdef DEBUG
>>>>
>>
>> DEBUG_CG14 ?
>>
>>
>>>> +#define DPRINTF(fmt, ...)                                       \
>>>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>>>> +#else
>>>> +#define DPRINTF(fmt, ...)
>>>> +#endif
>>>> +
>>>> +#define CG14_INFO(fmt, ...)                                     \
>>>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>>>> +#define CG14_ERROR(fmt, ...)                                    \
>>>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>>>> +
>>>> +/*
>>>> + * A[28:26] = slot number (4 to 7)
>>>> + * regs: size   0x10000 @ 0x09c000000  (0x80000000 + slot * 64M)
>>>> + * vmem: size upto 16MB @ 0x0fc000000  (0xE0000000 + slot * 64M)
>>>> + */
>>>>
>>> If you have any links to chipset docs, it would be nice to mention those here.
>>>
> Chipset docs are hard to come by.  But here's what I've found:
>    "Sun-4M System Architecture" section A.II.3 briefly covers VSIMM and
> DSIMM size detection.
>    Linux kernel 2.6 drivers/video/cg14.c has most of the registers named.

These references would be much better than nothing.

>    US Patent 5815137 covers the cursor implementation, but it includes
> drawings which show the various color and lookup table muxing and
> blending capability.  I will not be implementing the cursor functionality.

IANAL, would it be a patent violation to implement in SW similar
functions as described only in hardware terms in a patent?

>>>> +
>>>> +/*
>>>> + * memory map:
>>>> + * reg+0x0000 = control registers
>>>> + * reg+0x1000 = cursor registers
>>>> + * reg+0x2000 = dac registers (ADV7152)
>>>> + * reg+0x3000 = xlut
>>>> + * reg+0x4000 = clut1
>>>> + * reg+0x5000 = clut2
>>>> + * reg+0x6000 = clut3 (if implemented)
>>>> + *
>>>> + * mem+0x0000000 = XBGR (01234567)
>>>> + * mem+0x1000000 = BGR  (.123.567)
>>>> + * mem+0x2000000 = X16  (0246)
>>>> + * mem+0x2800000 = C16  (1357)
>>>> + * mem+0x3000000 = X32  (04)
>>>> + * mem+0x3400000 = B32  (15)
>>>> + * mem+0x3800000 = G32  (26)
>>>> + * mem+0x3c00000 = R32  (37)
>>>>
>>> Interesting device. You could increase the performance a lot by making
>>> the XBGR area ordinary memory and detecting the dirtyness with
>>> VGA_DIRTY_FLAG like TCX. The other areas could use multiple byte
>>> stores to the memory so dirty information would be updated.
>>>
>
> Hmm, interesting.  Might be cumbersome if the width isn't 1024 though.

It is, TCX can take some shortcuts because of this. For a full
implementation handling other cases too, please take a look at VGA.

>
>>>> + */
>>>> +
>>>> +#define CG14_REG_SIZE         0x10000
>>>> +#define CG14_VMEM_SLOTSIZE    (64<<20)
>>>> +
>>>> +#define CG14_MONID_1024x768   0
>>>> +#define CG14_MONID_1600x1280  1
>>>> +#define CG14_MONID_1280x1024  2
>>>> +#define CG14_MONID_1152x900   7
>>>> +
>>>> +#define CG14_MONID_DEFAULT    CG14_MONID_1024x768
>>>> +
>>>> +#define MCR_PIXMODE_MASK  0x30
>>>> +#define   MCR_PIXMODE_8     0x00
>>>> +#define   MCR_PIXMODE_16    0x20  /* 8+8 (X16,C16) */
>>>> +#define   MCR_PIXMODE_32    0x30  /* XBGR */
>>>> +
>>>> +
>>>> +struct ADV7152_state {
>>>> +    uint8_t mode;
>>>> +    uint8_t address;
>>>> +    int rgb_seq;
>>>> +};
>>>> +
>>>> +typedef struct CG14State {
>>>> +    SysBusDevice busdev;
>>>> +    DisplayState *ds;
>>>> +
>>>> +    uint8_t *vram;
>>>> +    uint32_t vram_amask;
>>>> +    int width, height;
>>>> +    int dirty, size_changed;
>>>> +    struct {
>>>> +        uint8_t mcr;
>>>> +        uint8_t ppr;
>>>> +    } ctrl;
>>>> +    struct ADV7152_state dac;
>>>> +    struct {
>>>> +        uint16_t hblank_start;
>>>> +        uint16_t hblank_clear;
>>>> +        uint16_t vblank_start;
>>>> +        uint16_t vblank_clear;
>>>> +    } timing;
>>>> +    uint8_t xlut[256];
>>>> +    uint32_t clut1[256];
>>>> +    uint32_t clut2[256];
>>>> +} CG14State;
>>>> +
>>>> +static void cg14_screen_dump(void *opaque, const char *filename);
>>>> +static void cg14_invalidate_display(void *opaque);
>>>> +
>>>> +static inline uint32_t bgr_to_rgb(uint32_t bgr)
>>>> +{
>>>> +    uint32_t rgb;
>>>> +
>>>> +    /* swap r & b */
>>>> +    rgb = (bgr & 0x00FF00)
>>>> +        | (bgr & 0x0000FF) << 16
>>>> +        | (bgr & 0xFF0000) >> 16;
>>>> +    return rgb;
>>>> +}
>>>> +
>>>> +static void cg14_draw_line32(const CG14State *s, void *dst, const uint8_t *src, int pixmode, int is_bgr)
>>>> +{
>>>> +    int i;
>>>> +    int x, r, g, b;
>>>> +    uint8_t xlut_val;
>>>> +    uint32_t dval;
>>>> +    uint32_t abgr;
>>>> +
>>>> +    xlut_val = s->ctrl.ppr;
>>>> +
>>>> +    for (i=0; i<s->width; i++) {
>>>> +        x = *src++;
>>>> +        if (pixmode == 8) {
>>>>
>>> To increase performance, pixmode should not be passed at all but
>>> instead separate functions should be added for each mode and the
>>> function should be selected before the line loop.
>>>
>
> Yes, at least for 8bit mode.  The per-pixel xlut value offers a lot of
> flexibility that may be hard to speed up when fully implemented.  The
> hardware supports simultaneous true-color and palette lookups to
> generate 2 color values to blend together for every displayed pixel.

Does this mode apply to whole screen? Or can the mode be enabled on a
pixel by pixel basis, like 24 bit TCX?

>>>> +            b = x;
>>>> +        } else {
>>>> +            b = *src++;
>>>> +            xlut_val = s->xlut[x];
>>>> +        }
>>>> +        if (pixmode != 32) {
>>>> +            r = g = b;
>>>> +        } else {
>>>> +            g = *src++;
>>>> +            r = *src++;
>>>> +        }
>>>> +        if (xlut_val == 0) {
>>>> +            abgr = b << 16 | g << 8 | r;
>>>> +        } else if (xlut_val == 0x40) {
>>>> +            abgr = s->clut1[x];
>>>> +        } else {
>>>> +            abgr = 0;
>>>> +        }
>>>> +        /* dac lookup ? */
>>>> +
>>>> +        /* to surface format */
>>>> +        dval = is_bgr ? (abgr & 0xFFFFFF) : bgr_to_rgb(abgr);
>>>> +        ((uint32_t*)dst)[i] = dval;
>>>> +    }
>>>> +}
>>>> +
>>>> +static void cg14_update_display(void *opaque)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    int h, pixmode;
>>>> +    uint8_t *pix;
>>>> +    uint8_t *data;
>>>> +    int new_width, new_height;
>>>> +
>>>> +    if (s->size_changed) {
>>>> +        new_width = 4 * (s->timing.hblank_start - s->timing.hblank_clear);
>>>> +        new_height = s->timing.vblank_start - s->timing.vblank_clear;
>>>> +        s->size_changed = 0;
>>>> +        if ((new_width != s->width || new_height != s->height) && new_width > 0 && new_height > 0) {
>>>> +            s->width = new_width;
>>>> +            s->height = new_height;
>>>> +            CG14_INFO("new resolution = %d x %d\n", new_width, new_height);
>>>> +            qemu_console_resize(s->ds, s->width, s->height);
>>>> +            s->dirty = 1;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    if (!s->dirty || !s->width || !s->height) {
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (ds_get_bits_per_pixel(s->ds) != 32) {
>>>> +        CG14_ERROR("cg14_update: FIXME: bpp (%d) != 32, linesize %d\n",
>>>> +            ds_get_bits_per_pixel(s->ds), ds_get_linesize(s->ds));
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
>>>> +    case MCR_PIXMODE_32:
>>>> +        pixmode = 32;
>>>> +        break;
>>>> +    case MCR_PIXMODE_16:
>>>> +        pixmode = 16;
>>>> +        break;
>>>> +    case MCR_PIXMODE_8:
>>>> +    default:
>>>> +        pixmode = 8;
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    pix = s->vram;
>>>> +    data = ds_get_data(s->ds);
>>>> +
>>>> +    for (h=0; h<s->height; h++) {
>>>> +        cg14_draw_line32(s, data, pix, pixmode, is_surface_bgr(s->ds->surface));
>>>> +        pix += s->width * (pixmode / 8);
>>>> +        data += ds_get_linesize(s->ds);
>>>> +    }
>>>> +    dpy_update(s->ds, 0, 0, s->width, s->height);
>>>> +    s->dirty = 0;
>>>> +}
>>>> +
>>>> +static void cg14_invalidate_display(void *opaque)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +
>>>> +    s->dirty = 1;
>>>> +}
>>>> +
>>>> +static void ADV7152_write(struct ADV7152_state *s, unsigned int reg, unsigned int val)
>>>> +{
>>>> +    switch (reg) {
>>>> +    case 0: /* address register */
>>>> +        DPRINTF("ADV7152 Write address %02x\n", val);
>>>> +        s->address = val;
>>>> +        s->rgb_seq = 0;
>>>> +        break;
>>>> +    case 1: /* look up table */
>>>> +        DPRINTF("ADV7152 Write %02x to lookup table\n", val);
>>>> +        s->rgb_seq++;
>>>> +        break;
>>>> +    case 2: /* control registers */
>>>> +        DPRINTF("ADV7152 Write %02x to control reg %d\n", val, s->address);
>>>> +        switch (s->address) {
>>>> +        default:
>>>> +            break;
>>>> +        }
>>>> +        break;
>>>> +    case 3: /* mode register */
>>>> +        CG14_INFO("ADV7152 Write mode %02x (%d bit DAC, %d bit bus)\n",
>>>> +            val, (val & 2) ? 10 : 8, (val & 4) ? 10 : 8);
>>>> +        if (!val & 0x01) {
>>>> +            // reset the dac
>>>> +            s->rgb_seq = 0;
>>>> +        }
>>>> +        s->mode = val;
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_reg_readb(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    unsigned int val;
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    case 0x0000:
>>>> +        val = s->ctrl.mcr;
>>>> +        break;
>>>> +    case 0x0001:
>>>> +        val = s->ctrl.ppr;
>>>> +        break;
>>>> +    case 0x0004: /* status ? */
>>>> +        /* monitor code in bits 1..3 */
>>>> +        val = CG14_MONID_DEFAULT << 1;
>>>> +        break;
>>>> +    case 0x0006: /* hw version */
>>>> +        //val = 0x00; /* old version */
>>>> +        val = 0x30;
>>>> +        break;
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    CG14_INFO("readb %02x from reg %x\n", val, (int)addr);
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t i;
>>>> +
>>>> +    if ((addr & 0xfcff) == 0x2000) {
>>>> +        i = (addr & 0x300) >> 8;
>>>> +        ADV7152_write(&s->dac, i, val);
>>>> +        return;
>>>> +    }
>>>> +    if ((addr & 0xff00) == 0x3000) {
>>>> +        /* xlut */
>>>> +        i = addr & 0xff;
>>>> +        if (s->xlut[i] != val) {
>>>> +            s->dirty = 1;
>>>> +            s->xlut[i] = val;
>>>> +            if (val && val != 0x40)
>>>> +                CG14_ERROR("writeb xlut[%d] = %02x\n", i, val);
>>>> +        }
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    s->dirty = 1;
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    case 0x0000:
>>>> +        s->ctrl.mcr = val;
>>>> +        break;
>>>> +    case 0x0001:
>>>> +        s->ctrl.ppr = val & 0xF0;
>>>> +        break;
>>>> +    case 0x0007:
>>>> +        /* clock control (ICS1562AM-001) */
>>>> +        DPRINTF("write %02x to clock control\n", val);
>>>> +        break;
>>>> +    default:
>>>> +        CG14_ERROR("writeb %02x to reg %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_reg_readw(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    unsigned int val;
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    case 0x0018:
>>>> +        val = s->timing.hblank_start;
>>>> +        break;
>>>> +    case 0x001a:
>>>> +        val = s->timing.hblank_clear;
>>>> +        break;
>>>> +    case 0x0022:
>>>> +        val = s->timing.vblank_start;
>>>> +        break;
>>>> +    case 0x0024:
>>>> +        val = s->timing.vblank_clear;
>>>> +        break;
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    CG14_INFO("readw 0x%08x from reg %x\n", val, (int)addr);
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +
>>>> +    CG14_INFO("writew %04x to reg %x\n", val, (int)addr);
>>>> +
>>>> +    /* timing registers are 16bit */
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    case 0x0018:
>>>> +        s->timing.hblank_start = val;
>>>> +        break;
>>>> +    case 0x001a:
>>>> +        s->timing.hblank_clear = val;
>>>> +       s->size_changed = 1;
>>>> +        break;
>>>> +    case 0x0022:
>>>> +        s->timing.vblank_start = val;
>>>> +        break;
>>>> +    case 0x0024:
>>>> +        s->timing.vblank_clear = val;
>>>> +       s->size_changed = 1;
>>>> +        break;
>>>> +    case 0x001c: /* hsync_start */
>>>> +    case 0x001e: /* hsync_clear */
>>>> +    case 0x0020: /* csync_clear */
>>>> +    case 0x0026: /* vsync_start */
>>>> +    case 0x0028: /* vsync_clear */
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_reg_readl(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t val;
>>>> +    uint32_t i;
>>>> +
>>>> +    i = (addr & 0x3ff) >> 2;
>>>> +    switch (addr & 0xfc00) {
>>>> +    case 0x4000:
>>>> +        val = s->clut1[i];
>>>> +        break;
>>>> +    case 0x5000:
>>>> +        val = s->clut2[i];
>>>> +        break;
>>>> +    default:
>>>> +        val = 0;
>>>> +        CG14_ERROR("readl %08x from reg %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t i;
>>>> +
>>>> +    s->dirty = 1;
>>>> +
>>>> +    i = (addr & 0x3ff) >> 2;
>>>> +    switch (addr & 0xfc00) {
>>>> +    case 0x4000:
>>>> +        s->clut1[i] = val;
>>>> +        break;
>>>> +    case 0x5000:
>>>> +        s->clut2[i] = val;
>>>> +        break;
>>>> +    default:
>>>> +        CG14_ERROR("writel %08x to reg %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static CPUReadMemoryFunc *cg14_reg_read[3] = {
>>>> +    cg14_reg_readb,
>>>> +    cg14_reg_readw,
>>>> +    cg14_reg_readl,
>>>> +};
>>>> +
>>>> +static CPUWriteMemoryFunc *cg14_reg_write[3] = {
>>>> +    cg14_reg_writeb,
>>>> +    cg14_reg_writew,
>>>> +    cg14_reg_writel,
>>>> +};
>>>> +
>>>> +static uint32_t cg14_vram_readb(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +    uint32_t val = 0;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    case 0x0000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        val =  ldub_p(s->vram+offset);
>>>> +        break;
>>>> +    case 0x1000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        val = 0; // FIXME
>>>> +        break;
>>>> +    case 0x2000000:
>>>> +        offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1);
>>>> +        val =  ldub_p(s->vram+offset);
>>>> +        break;
>>>> +    case 0x3000000:
>>>> +        offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3);
>>>> +        val =  ldub_p(s->vram+offset);
>>>> +        break;
>>>> +    }
>>>> +    CG14_INFO("readb %02x from vram %x\n", val, (int)addr);
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_vram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    case 0x0000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        stb_p(s->vram+offset, val);
>>>> +        if (offset < 4 * s->width * s->height) {
>>>> +            s->dirty = 1;
>>>> +        }
>>>> +        break;
>>>> +    default:
>>>> +        CG14_ERROR("writeb %02x to vram %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_vram_readw(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +    uint32_t val;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    default:
>>>> +        offset = addr & s->vram_amask;
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    CG14_ERROR("readw %04x from vram %x\n", val, (int)addr);
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_vram_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +
>>>> +    CG14_ERROR("writew %04x to vram %x\n", val, (int)addr);
>>>> +
>>>> +    s->dirty = 1;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_vram_readl(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +    uint32_t val = 0;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    case 0x0000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        val = ldl_be_p(s->vram+offset);
>>>> +        break;
>>>> +    case 0x1000000:
>>>> +    case 0x2000000:
>>>> +    case 0x3000000:
>>>> +        CG14_ERROR("readl %08x from vram %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_vram_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    case 0x0000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        stl_be_p(s->vram+offset, val);
>>>> +        if (offset < 4 * s->width * s->height) {
>>>> +            s->dirty = 1;
>>>> +        }
>>>> +        break;
>>>> +    case 0x1000000:
>>>> +    case 0x2000000:
>>>> +    case 0x3000000:
>>>> +        CG14_ERROR("writel %08x to vram %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static CPUReadMemoryFunc *cg14_vram_read[3] = {
>>>> +    cg14_vram_readb,
>>>> +    cg14_vram_readw,
>>>> +    cg14_vram_readl,
>>>> +};
>>>> +
>>>> +static CPUWriteMemoryFunc *cg14_vram_write[3] = {
>>>> +    cg14_vram_writeb,
>>>> +    cg14_vram_writew,
>>>> +    cg14_vram_writel,
>>>> +};
>>>> +
>>>> +
>>>> +/******** SX *********/
>>>> +
>>>> +static uint32_t sx_reg_readb(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +    int val;
>>>> +
>>>> +    printf("SX readb reg " TARGET_FMT_plx "\n", addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void sx_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +
>>>> +    printf("SX writeb %02x to reg " TARGET_FMT_plx "\n", val, addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t sx_reg_readw(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +    int val;
>>>> +
>>>> +    printf("SX readw reg " TARGET_FMT_plx "\n", addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void sx_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +
>>>> +    printf("SX writew %04x to reg " TARGET_FMT_plx "\n", val, addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t sx_reg_readl(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +    int val;
>>>> +
>>>> +    printf("SX readl reg " TARGET_FMT_plx "\n", addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void sx_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +
>>>> +    printf("SX writel %08x to reg " TARGET_FMT_plx "\n", val, addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static CPUReadMemoryFunc *sx_reg_read[3] = {
>>>> +    sx_reg_readb,
>>>> +    sx_reg_readw,
>>>> +    sx_reg_readl,
>>>> +};
>>>> +
>>>> +static CPUWriteMemoryFunc *sx_reg_write[3] = {
>>>> +    sx_reg_writeb,
>>>> +    sx_reg_writew,
>>>> +    sx_reg_writel,
>>>> +};
>>>> +
>>>> +/*********************/
>>>> +
>>>> +static uint32_t bad_mem_read(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    printf("Bad read from " TARGET_FMT_plx "\n", addr);
>>>> +    //cpu_abort(cpu_single_env, "bad ram read access at " TARGET_FMT_plx "\n", addr);
>>>> +    return 0;
>>>> +}
>>>> +static void bad_mem_write(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    printf("Bad write of 0x%02x to " TARGET_FMT_plx "\n", val, addr);
>>>> +    //cpu_abort(cpu_single_env, "bad ram write access at " TARGET_FMT_plx "\n", addr);
>>>> +}
>>>> +static CPUReadMemoryFunc *bad_memr[3] = { bad_mem_read, bad_mem_read, bad_mem_read };
>>>> +static CPUWriteMemoryFunc *bad_memw[3] = { bad_mem_write, bad_mem_write, bad_mem_write };
>>>> +
>>>> +void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
>>>> +                uint32_t vram_size)
>>>> +{
>>>> +//    DeviceState *dev;
>>>> +//    SysBusDevice *s;
>>>> +
>>>> +//    dev = qdev_create(NULL, "SUNW,cg14");
>>>> +//    qdev_init(dev);
>>>> +//    s = sysbus_from_qdev(dev);
>>>> +//}
>>>> +
>>>> +//static void cg14_init1(SysBusDevice *dev)
>>>> +//{
>>>> +    CG14State *s;// = FROM_SYSBUS(CG14State, dev);
>>>> +    ram_addr_t vram_offset;
>>>> +    uint8_t *vram;
>>>> +    int ctrl_memory, vram_memory;
>>>> +    int sx_registers;
>>>> +    int bad_mem;
>>>> +
>>>> +    s = qemu_mallocz(sizeof(CG14State));
>>>> +
>>>> +    vram_offset = qemu_ram_alloc(vram_size);
>>>> +    vram = qemu_get_ram_ptr(vram_offset);
>>>> +
>>>> +    s->vram = vram;
>>>> +    s->vram_amask = vram_size - 1;
>>>> +
>>>> +    ctrl_memory = cpu_register_io_memory(cg14_reg_read, cg14_reg_write, s);
>>>> +    cpu_register_physical_memory_offset(ctrl_base, CG14_REG_SIZE, ctrl_memory, ctrl_base);
>>>> +
>>>> +    vram_memory = cpu_register_io_memory(cg14_vram_read, cg14_vram_write, s);
>>>> +    cpu_register_physical_memory_offset(vram_base, CG14_VMEM_SLOTSIZE, vram_memory, vram_base);
>>>> +
>>>> +    s->ds = graphic_console_init(cg14_update_display,
>>>> +                                 cg14_invalidate_display,
>>>> +                                 cg14_screen_dump, NULL, s);
>>>> +
>>>> +    s->width = 640;
>>>> +    s->height = 480;
>>>> +    qemu_console_resize(s->ds, s->width, s->height);
>>>> +
>>>> +    /* SX or SPAM (Sun Pixel Arithmetic Memory) */
>>>> +    sx_registers = cpu_register_io_memory(sx_reg_read, sx_reg_write, s);
>>>> +    cpu_register_physical_memory(0xf80000000ULL, 0x2000, sx_registers);
>>>> +
>>>> +    bad_mem = cpu_register_io_memory(bad_memr, bad_memw, s);
>>>> +    /* missing vsimms */
>>>> +    cpu_register_physical_memory_offset(0x90000000, 0x2000, bad_mem, 0x90000000);
>>>> +    cpu_register_physical_memory_offset(0x94000000, 0x2000, bad_mem, 0x94000000);
>>>> +    cpu_register_physical_memory_offset(0x98000000, 0x2000, bad_mem, 0x98000000);
>>>>
>>> Can't we just have more VSIMMS? The empty_slot device may be useful here.
>>>
> Each VSIMM would have it's own monitor attached.  Does Qemu and/or
> OpenBIOS support multiple displays?  I think the limit for a real SS-20
> is 5 displays - 2 VSIMMs and 3 GX/TGX cards.  I'll just go with
> empty_slot for the extras.
>>
>> And btw, VSIMMs addresses may currently overlap with RAM. Maybe we
>> should keep "SS-20" (and SS-10) compatible with original, and rename
>> the current machine definition to "SS-20qemu" or SS-20-60G?
>>
> Well, the real machines are limited to 512M, so dropping the framebuffer
> registers into the middle of the first 4G wasn't an issue back then.  I
> think the SX accelerator also limits the system and video memory to the
> lower 4G of address space.
>
>>>> +    /* DBRI (audio) */
>>>> +    cpu_register_physical_memory_offset(0xEE0001000ULL, 0x10000, bad_mem, 0xE0001000);
>>>>
>>> Please add a new DBRI device ;-).
>>>
>>
>> Or maybe just a field in hwdef + empty_slot? :-)
>>
> Yes, empty_slot should work fine.  I was trying to hack in enough to get
> it to boot, but no luck yet.  The cpu models are still lacking - with
> the SS-20 v2.25 rom, SuperSparc 61 fails before initializing the
> display, SuperSparc 60 has a data access error when trying to boot, and
> Ross 625 is mis-detected as an incompatible cpu type.

Robert Reif did some improvements to SuperSparc emulation, but the
work was not finished. That should be a good starting point.

>>>> +}
>>>> +
>>>> +/* save to file */
>>>> +static void cg14_screen_dump(void *opaque, const char *filename)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    FILE *f;
>>>> +    int y, pixmode, linesize;
>>>> +    void *buf;
>>>> +    uint8_t *pix;
>>>> +
>>>> +    f = fopen(filename, "wb");
>>>> +    if (!f) {
>>>> +        return;
>>>> +    }
>>>> +    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
>>>> +
>>>> +    linesize = s->width * 3;
>>>> +    buf = qemu_mallocz(linesize);
>>>> +    pix = s->vram;
>>>> +
>>>> +    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
>>>> +    case MCR_PIXMODE_32:
>>>> +        pixmode = 32;
>>>> +        break;
>>>> +    case MCR_PIXMODE_16:
>>>> +        pixmode = 16;
>>>> +        break;
>>>> +    case MCR_PIXMODE_8:
>>>> +    default:
>>>> +        pixmode = 8;
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    for (y=0; y<s->height; y++) {
>>>> +        // cg14_draw_line24_bgr(s, buf, pix, pixmode);
>>>> +        fwrite(buf, 1, linesize, f);
>>>> +        pix += s->width * (pixmode / 8);
>>>> +    }
>>>> +
>>>> +    qemu_free(buf);
>>>> +    fclose(f);
>>>> +}
>>>>
>>>>
>
Artyom Tarasenko May 29, 2010, 1:26 p.m. UTC | #5
2010/5/29 Bob Breuer <breuerr@mc.net>:
> Artyom Tarasenko wrote:
>> 2010/5/28 Blue Swirl <blauwirbel@gmail.com>:
>>
>>> On Fri, May 28, 2010 at 7:54 AM, Bob Breuer <breuerr@mc.net> wrote:
>>>
>>>> Artyom Tarasenko wrote:
>>>>
>>>>> 2010/5/27 Bob Breuer <breuerr@mc.net>:
>>>>>
>>>>>
>>>>>> Artyom Tarasenko wrote:
>>>>>>
>>>>>>
>>>>>>> Was going to put some more empty slots into SS-10/20 (VSIMMs, SX)
>>>>>>> after we are done with SS-5 (due to technical limitations I can switch
>>>>>>> access from one real SS model to another one once a few days only).
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> I have a partial implementation of the SS-20 VSIMM (cg14) that I've been
>>>>>> working on.  With the Sun firmware, I have working text console, color
>>>>>> boot logo, and programmable video resolutions up to 1600x1280.
>>>>>>
>>>>>>
>>>>> Great news! This would allow qemu booting NeXTStep! Are you planning
>>>>> to submit the patches any time soon?
>>>>>
>>>>>
>>>>>
>>>> It's not in a state to be submitted yet, but I've attached a working
>>>> patch if you want to give it a try.  I need to hook it up to qdev and
>>>> fill in some more of the obviously incomplete switch cases before I'd
>>>> sign off on it.
>>>>
>>> Nice work. I have a few comments below.
>>>
>>> This probably needs support from OpenBIOS to be usable without OBP.
>>>
>>
>> Maybe it can be used as a second adapter without OpenBIOS support? At
>> least under some OSes?
>>
>>
> Probably won't be used without at least being in the firmware device
> tree.  One area that OpenBIOS could enhance would be a larger memory
> size option.  The real hardware was only available in 4M and 8M options,
> but the memory map allows for 16M.  OBP will identify a 16M VSIMM but
> won't do anything else with it, and with 16M of vram it would allow for
> a potential 2560x1600 32bit resolution.
>>>> Bob
>>>>
>>>>
>>>> diff --git a/Makefile.target b/Makefile.target
>>>> index fda5bf3..b17b3af 100644
>>>> --- a/Makefile.target
>>>> +++ b/Makefile.target
>>>> @@ -250,6 +250,7 @@ else
>>>>  obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
>>>>  obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
>>>>  obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
>>>> +obj-sparc-y += cg14.o
>>>>  endif
>>>>
>>>>  obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
>>>> diff --git a/hw/sun4m.c b/hw/sun4m.c
>>>> index 7ba0f76..8b23c9b 100644
>>>> --- a/hw/sun4m.c
>>>> +++ b/hw/sun4m.c
>>>> @@ -864,6 +864,13 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
>>>>         fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
>>>>         exit (1);
>>>>     }
>>>> +  if (hwdef->machine_id == 65) { /* SS-20 */
>>>>
>>> hwdef structure should contain a field for cg14. If non-zero, install
>>> cg14. Was cg14 only available for SS-20? Was it always included? This
>>> is also interesting for OpenBIOS, we need to detect cg14 vs. TCX.
>>>
> The cg14 was only an option for SS-20 and the rare SS-10SX, but not the
> regular SS-10, though the SS-10 chipset may have been capable of
> supporting it.  Each cg14 vsimm takes the place of a stick of memory
> with 2 slots physically capable of holding a vsimm.

The few SS-10 OBP versions I have probe for VSIMMs. So we need to put
either empty_slot (when the -nographics option is used) or VSIMMs
there.

> Is there a way to pass the framebuffer type and/or address to OpenBIOS?
> I would be inclined to have the SS-20 machine default to cg14 instead of
> TCX, but it will blow a hole in the support of more than 2G of emulated
> system ram.
>>>> +    /* cg14.c */
>>>> +    void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
>>>> +                uint32_t vram_size);
>>>>
>>> This should go to sun4m.h or cg14.h.
>>>
>>>
>>>> +
>>>> +    cg14_init(0x09c000000ULL, 0x0fc000000ULL, 8<<20);
>>>> +  } else
>>>>
>>> Please add braces and reindent.
>>>
>>>
>>>>     tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
>>>>              graphic_depth);
>>>>
>>>> --- /dev/null   Fri May 28 02:08:36 2010
>>>> +++ hw/cg14.c   Fri May 28 01:58:49 2010
>>>> @@ -0,0 +1,785 @@
>>>> +/*
>>>> + * QEMU CG14 Frame buffer
>>>> + *
>>>> + * Copyright (c) 2010 Bob Breuer
>>>> + *
>>>> + * 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.
>>>> + */
>>>> +
>>>> +#include "console.h"
>>>> +#include "sysbus.h"
>>>> +
>>>> +#ifdef DEBUG
>>>>
>>
>> DEBUG_CG14 ?
>>
>>
>>>> +#define DPRINTF(fmt, ...)                                       \
>>>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>>>> +#else
>>>> +#define DPRINTF(fmt, ...)
>>>> +#endif
>>>> +
>>>> +#define CG14_INFO(fmt, ...)                                     \
>>>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>>>> +#define CG14_ERROR(fmt, ...)                                    \
>>>> +    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
>>>> +
>>>> +/*
>>>> + * A[28:26] = slot number (4 to 7)
>>>> + * regs: size   0x10000 @ 0x09c000000  (0x80000000 + slot * 64M)
>>>> + * vmem: size upto 16MB @ 0x0fc000000  (0xE0000000 + slot * 64M)
>>>> + */
>>>>
>>> If you have any links to chipset docs, it would be nice to mention those here.
>>>
> Chipset docs are hard to come by.  But here's what I've found:
>    "Sun-4M System Architecture" section A.II.3 briefly covers VSIMM and
> DSIMM size detection.
>    Linux kernel 2.6 drivers/video/cg14.c has most of the registers named.
>    US Patent 5815137 covers the cursor implementation, but it includes
> drawings which show the various color and lookup table muxing and
> blending capability.  I will not be implementing the cursor functionality.
>
>>>> +
>>>> +/*
>>>> + * memory map:
>>>> + * reg+0x0000 = control registers
>>>> + * reg+0x1000 = cursor registers
>>>> + * reg+0x2000 = dac registers (ADV7152)
>>>> + * reg+0x3000 = xlut
>>>> + * reg+0x4000 = clut1
>>>> + * reg+0x5000 = clut2
>>>> + * reg+0x6000 = clut3 (if implemented)
>>>> + *
>>>> + * mem+0x0000000 = XBGR (01234567)
>>>> + * mem+0x1000000 = BGR  (.123.567)
>>>> + * mem+0x2000000 = X16  (0246)
>>>> + * mem+0x2800000 = C16  (1357)
>>>> + * mem+0x3000000 = X32  (04)
>>>> + * mem+0x3400000 = B32  (15)
>>>> + * mem+0x3800000 = G32  (26)
>>>> + * mem+0x3c00000 = R32  (37)
>>>>
>>> Interesting device. You could increase the performance a lot by making
>>> the XBGR area ordinary memory and detecting the dirtyness with
>>> VGA_DIRTY_FLAG like TCX. The other areas could use multiple byte
>>> stores to the memory so dirty information would be updated.
>>>
>
> Hmm, interesting.  Might be cumbersome if the width isn't 1024 though.
>
>>>> + */
>>>> +
>>>> +#define CG14_REG_SIZE         0x10000
>>>> +#define CG14_VMEM_SLOTSIZE    (64<<20)
>>>> +
>>>> +#define CG14_MONID_1024x768   0
>>>> +#define CG14_MONID_1600x1280  1
>>>> +#define CG14_MONID_1280x1024  2
>>>> +#define CG14_MONID_1152x900   7
>>>> +
>>>> +#define CG14_MONID_DEFAULT    CG14_MONID_1024x768
>>>> +
>>>> +#define MCR_PIXMODE_MASK  0x30
>>>> +#define   MCR_PIXMODE_8     0x00
>>>> +#define   MCR_PIXMODE_16    0x20  /* 8+8 (X16,C16) */
>>>> +#define   MCR_PIXMODE_32    0x30  /* XBGR */
>>>> +
>>>> +
>>>> +struct ADV7152_state {
>>>> +    uint8_t mode;
>>>> +    uint8_t address;
>>>> +    int rgb_seq;
>>>> +};
>>>> +
>>>> +typedef struct CG14State {
>>>> +    SysBusDevice busdev;
>>>> +    DisplayState *ds;
>>>> +
>>>> +    uint8_t *vram;
>>>> +    uint32_t vram_amask;
>>>> +    int width, height;
>>>> +    int dirty, size_changed;
>>>> +    struct {
>>>> +        uint8_t mcr;
>>>> +        uint8_t ppr;
>>>> +    } ctrl;
>>>> +    struct ADV7152_state dac;
>>>> +    struct {
>>>> +        uint16_t hblank_start;
>>>> +        uint16_t hblank_clear;
>>>> +        uint16_t vblank_start;
>>>> +        uint16_t vblank_clear;
>>>> +    } timing;
>>>> +    uint8_t xlut[256];
>>>> +    uint32_t clut1[256];
>>>> +    uint32_t clut2[256];
>>>> +} CG14State;
>>>> +
>>>> +static void cg14_screen_dump(void *opaque, const char *filename);
>>>> +static void cg14_invalidate_display(void *opaque);
>>>> +
>>>> +static inline uint32_t bgr_to_rgb(uint32_t bgr)
>>>> +{
>>>> +    uint32_t rgb;
>>>> +
>>>> +    /* swap r & b */
>>>> +    rgb = (bgr & 0x00FF00)
>>>> +        | (bgr & 0x0000FF) << 16
>>>> +        | (bgr & 0xFF0000) >> 16;
>>>> +    return rgb;
>>>> +}
>>>> +
>>>> +static void cg14_draw_line32(const CG14State *s, void *dst, const uint8_t *src, int pixmode, int is_bgr)
>>>> +{
>>>> +    int i;
>>>> +    int x, r, g, b;
>>>> +    uint8_t xlut_val;
>>>> +    uint32_t dval;
>>>> +    uint32_t abgr;
>>>> +
>>>> +    xlut_val = s->ctrl.ppr;
>>>> +
>>>> +    for (i=0; i<s->width; i++) {
>>>> +        x = *src++;
>>>> +        if (pixmode == 8) {
>>>>
>>> To increase performance, pixmode should not be passed at all but
>>> instead separate functions should be added for each mode and the
>>> function should be selected before the line loop.
>>>
>
> Yes, at least for 8bit mode.  The per-pixel xlut value offers a lot of
> flexibility that may be hard to speed up when fully implemented.  The
> hardware supports simultaneous true-color and palette lookups to
> generate 2 color values to blend together for every displayed pixel.
>
>>>> +            b = x;
>>>> +        } else {
>>>> +            b = *src++;
>>>> +            xlut_val = s->xlut[x];
>>>> +        }
>>>> +        if (pixmode != 32) {
>>>> +            r = g = b;
>>>> +        } else {
>>>> +            g = *src++;
>>>> +            r = *src++;
>>>> +        }
>>>> +        if (xlut_val == 0) {
>>>> +            abgr = b << 16 | g << 8 | r;
>>>> +        } else if (xlut_val == 0x40) {
>>>> +            abgr = s->clut1[x];
>>>> +        } else {
>>>> +            abgr = 0;
>>>> +        }
>>>> +        /* dac lookup ? */
>>>> +
>>>> +        /* to surface format */
>>>> +        dval = is_bgr ? (abgr & 0xFFFFFF) : bgr_to_rgb(abgr);
>>>> +        ((uint32_t*)dst)[i] = dval;
>>>> +    }
>>>> +}
>>>> +
>>>> +static void cg14_update_display(void *opaque)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    int h, pixmode;
>>>> +    uint8_t *pix;
>>>> +    uint8_t *data;
>>>> +    int new_width, new_height;
>>>> +
>>>> +    if (s->size_changed) {
>>>> +        new_width = 4 * (s->timing.hblank_start - s->timing.hblank_clear);
>>>> +        new_height = s->timing.vblank_start - s->timing.vblank_clear;
>>>> +        s->size_changed = 0;
>>>> +        if ((new_width != s->width || new_height != s->height) && new_width > 0 && new_height > 0) {
>>>> +            s->width = new_width;
>>>> +            s->height = new_height;
>>>> +            CG14_INFO("new resolution = %d x %d\n", new_width, new_height);
>>>> +            qemu_console_resize(s->ds, s->width, s->height);
>>>> +            s->dirty = 1;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    if (!s->dirty || !s->width || !s->height) {
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    if (ds_get_bits_per_pixel(s->ds) != 32) {
>>>> +        CG14_ERROR("cg14_update: FIXME: bpp (%d) != 32, linesize %d\n",
>>>> +            ds_get_bits_per_pixel(s->ds), ds_get_linesize(s->ds));
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
>>>> +    case MCR_PIXMODE_32:
>>>> +        pixmode = 32;
>>>> +        break;
>>>> +    case MCR_PIXMODE_16:
>>>> +        pixmode = 16;
>>>> +        break;
>>>> +    case MCR_PIXMODE_8:
>>>> +    default:
>>>> +        pixmode = 8;
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    pix = s->vram;
>>>> +    data = ds_get_data(s->ds);
>>>> +
>>>> +    for (h=0; h<s->height; h++) {
>>>> +        cg14_draw_line32(s, data, pix, pixmode, is_surface_bgr(s->ds->surface));
>>>> +        pix += s->width * (pixmode / 8);
>>>> +        data += ds_get_linesize(s->ds);
>>>> +    }
>>>> +    dpy_update(s->ds, 0, 0, s->width, s->height);
>>>> +    s->dirty = 0;
>>>> +}
>>>> +
>>>> +static void cg14_invalidate_display(void *opaque)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +
>>>> +    s->dirty = 1;
>>>> +}
>>>> +
>>>> +static void ADV7152_write(struct ADV7152_state *s, unsigned int reg, unsigned int val)
>>>> +{
>>>> +    switch (reg) {
>>>> +    case 0: /* address register */
>>>> +        DPRINTF("ADV7152 Write address %02x\n", val);
>>>> +        s->address = val;
>>>> +        s->rgb_seq = 0;
>>>> +        break;
>>>> +    case 1: /* look up table */
>>>> +        DPRINTF("ADV7152 Write %02x to lookup table\n", val);
>>>> +        s->rgb_seq++;
>>>> +        break;
>>>> +    case 2: /* control registers */
>>>> +        DPRINTF("ADV7152 Write %02x to control reg %d\n", val, s->address);
>>>> +        switch (s->address) {
>>>> +        default:
>>>> +            break;
>>>> +        }
>>>> +        break;
>>>> +    case 3: /* mode register */
>>>> +        CG14_INFO("ADV7152 Write mode %02x (%d bit DAC, %d bit bus)\n",
>>>> +            val, (val & 2) ? 10 : 8, (val & 4) ? 10 : 8);
>>>> +        if (!val & 0x01) {
>>>> +            // reset the dac
>>>> +            s->rgb_seq = 0;
>>>> +        }
>>>> +        s->mode = val;
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_reg_readb(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    unsigned int val;
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    case 0x0000:
>>>> +        val = s->ctrl.mcr;
>>>> +        break;
>>>> +    case 0x0001:
>>>> +        val = s->ctrl.ppr;
>>>> +        break;
>>>> +    case 0x0004: /* status ? */
>>>> +        /* monitor code in bits 1..3 */
>>>> +        val = CG14_MONID_DEFAULT << 1;
>>>> +        break;
>>>> +    case 0x0006: /* hw version */
>>>> +        //val = 0x00; /* old version */
>>>> +        val = 0x30;
>>>> +        break;
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    CG14_INFO("readb %02x from reg %x\n", val, (int)addr);
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t i;
>>>> +
>>>> +    if ((addr & 0xfcff) == 0x2000) {
>>>> +        i = (addr & 0x300) >> 8;
>>>> +        ADV7152_write(&s->dac, i, val);
>>>> +        return;
>>>> +    }
>>>> +    if ((addr & 0xff00) == 0x3000) {
>>>> +        /* xlut */
>>>> +        i = addr & 0xff;
>>>> +        if (s->xlut[i] != val) {
>>>> +            s->dirty = 1;
>>>> +            s->xlut[i] = val;
>>>> +            if (val && val != 0x40)
>>>> +                CG14_ERROR("writeb xlut[%d] = %02x\n", i, val);
>>>> +        }
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    s->dirty = 1;
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    case 0x0000:
>>>> +        s->ctrl.mcr = val;
>>>> +        break;
>>>> +    case 0x0001:
>>>> +        s->ctrl.ppr = val & 0xF0;
>>>> +        break;
>>>> +    case 0x0007:
>>>> +        /* clock control (ICS1562AM-001) */
>>>> +        DPRINTF("write %02x to clock control\n", val);
>>>> +        break;
>>>> +    default:
>>>> +        CG14_ERROR("writeb %02x to reg %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_reg_readw(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    unsigned int val;
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    case 0x0018:
>>>> +        val = s->timing.hblank_start;
>>>> +        break;
>>>> +    case 0x001a:
>>>> +        val = s->timing.hblank_clear;
>>>> +        break;
>>>> +    case 0x0022:
>>>> +        val = s->timing.vblank_start;
>>>> +        break;
>>>> +    case 0x0024:
>>>> +        val = s->timing.vblank_clear;
>>>> +        break;
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    CG14_INFO("readw 0x%08x from reg %x\n", val, (int)addr);
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +
>>>> +    CG14_INFO("writew %04x to reg %x\n", val, (int)addr);
>>>> +
>>>> +    /* timing registers are 16bit */
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    case 0x0018:
>>>> +        s->timing.hblank_start = val;
>>>> +        break;
>>>> +    case 0x001a:
>>>> +        s->timing.hblank_clear = val;
>>>> +       s->size_changed = 1;
>>>> +        break;
>>>> +    case 0x0022:
>>>> +        s->timing.vblank_start = val;
>>>> +        break;
>>>> +    case 0x0024:
>>>> +        s->timing.vblank_clear = val;
>>>> +       s->size_changed = 1;
>>>> +        break;
>>>> +    case 0x001c: /* hsync_start */
>>>> +    case 0x001e: /* hsync_clear */
>>>> +    case 0x0020: /* csync_clear */
>>>> +    case 0x0026: /* vsync_start */
>>>> +    case 0x0028: /* vsync_clear */
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_reg_readl(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t val;
>>>> +    uint32_t i;
>>>> +
>>>> +    i = (addr & 0x3ff) >> 2;
>>>> +    switch (addr & 0xfc00) {
>>>> +    case 0x4000:
>>>> +        val = s->clut1[i];
>>>> +        break;
>>>> +    case 0x5000:
>>>> +        val = s->clut2[i];
>>>> +        break;
>>>> +    default:
>>>> +        val = 0;
>>>> +        CG14_ERROR("readl %08x from reg %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t i;
>>>> +
>>>> +    s->dirty = 1;
>>>> +
>>>> +    i = (addr & 0x3ff) >> 2;
>>>> +    switch (addr & 0xfc00) {
>>>> +    case 0x4000:
>>>> +        s->clut1[i] = val;
>>>> +        break;
>>>> +    case 0x5000:
>>>> +        s->clut2[i] = val;
>>>> +        break;
>>>> +    default:
>>>> +        CG14_ERROR("writel %08x to reg %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static CPUReadMemoryFunc *cg14_reg_read[3] = {
>>>> +    cg14_reg_readb,
>>>> +    cg14_reg_readw,
>>>> +    cg14_reg_readl,
>>>> +};
>>>> +
>>>> +static CPUWriteMemoryFunc *cg14_reg_write[3] = {
>>>> +    cg14_reg_writeb,
>>>> +    cg14_reg_writew,
>>>> +    cg14_reg_writel,
>>>> +};
>>>> +
>>>> +static uint32_t cg14_vram_readb(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +    uint32_t val = 0;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    case 0x0000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        val =  ldub_p(s->vram+offset);
>>>> +        break;
>>>> +    case 0x1000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        val = 0; // FIXME
>>>> +        break;
>>>> +    case 0x2000000:
>>>> +        offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1);
>>>> +        val =  ldub_p(s->vram+offset);
>>>> +        break;
>>>> +    case 0x3000000:
>>>> +        offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3);
>>>> +        val =  ldub_p(s->vram+offset);
>>>> +        break;
>>>> +    }
>>>> +    CG14_INFO("readb %02x from vram %x\n", val, (int)addr);
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_vram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    case 0x0000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        stb_p(s->vram+offset, val);
>>>> +        if (offset < 4 * s->width * s->height) {
>>>> +            s->dirty = 1;
>>>> +        }
>>>> +        break;
>>>> +    default:
>>>> +        CG14_ERROR("writeb %02x to vram %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_vram_readw(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +    uint32_t val;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    default:
>>>> +        offset = addr & s->vram_amask;
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    CG14_ERROR("readw %04x from vram %x\n", val, (int)addr);
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_vram_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +
>>>> +    CG14_ERROR("writew %04x to vram %x\n", val, (int)addr);
>>>> +
>>>> +    s->dirty = 1;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t cg14_vram_readl(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +    uint32_t val = 0;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    case 0x0000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        val = ldl_be_p(s->vram+offset);
>>>> +        break;
>>>> +    case 0x1000000:
>>>> +    case 0x2000000:
>>>> +    case 0x3000000:
>>>> +        CG14_ERROR("readl %08x from vram %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void cg14_vram_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    CG14State *s = opaque;
>>>> +    uint32_t offset;
>>>> +
>>>> +    switch (addr & 0x3000000) {
>>>> +    case 0x0000000:
>>>> +        offset = addr & s->vram_amask;
>>>> +        stl_be_p(s->vram+offset, val);
>>>> +        if (offset < 4 * s->width * s->height) {
>>>> +            s->dirty = 1;
>>>> +        }
>>>> +        break;
>>>> +    case 0x1000000:
>>>> +    case 0x2000000:
>>>> +    case 0x3000000:
>>>> +        CG14_ERROR("writel %08x to vram %x\n", val, (int)addr);
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static CPUReadMemoryFunc *cg14_vram_read[3] = {
>>>> +    cg14_vram_readb,
>>>> +    cg14_vram_readw,
>>>> +    cg14_vram_readl,
>>>> +};
>>>> +
>>>> +static CPUWriteMemoryFunc *cg14_vram_write[3] = {
>>>> +    cg14_vram_writeb,
>>>> +    cg14_vram_writew,
>>>> +    cg14_vram_writel,
>>>> +};
>>>> +
>>>> +
>>>> +/******** SX *********/
>>>> +
>>>> +static uint32_t sx_reg_readb(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +    int val;
>>>> +
>>>> +    printf("SX readb reg " TARGET_FMT_plx "\n", addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void sx_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +
>>>> +    printf("SX writeb %02x to reg " TARGET_FMT_plx "\n", val, addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t sx_reg_readw(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +    int val;
>>>> +
>>>> +    printf("SX readw reg " TARGET_FMT_plx "\n", addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void sx_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +
>>>> +    printf("SX writew %04x to reg " TARGET_FMT_plx "\n", val, addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static uint32_t sx_reg_readl(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +    int val;
>>>> +
>>>> +    printf("SX readl reg " TARGET_FMT_plx "\n", addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        val = 0;
>>>> +        break;
>>>> +    }
>>>> +    return val;
>>>> +}
>>>> +
>>>> +static void sx_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    //CG14State *s = opaque;
>>>> +
>>>> +    printf("SX writel %08x to reg " TARGET_FMT_plx "\n", val, addr);
>>>> +
>>>> +    switch (addr & 0xffff) {
>>>> +    default:
>>>> +        break;
>>>> +    }
>>>> +}
>>>> +
>>>> +static CPUReadMemoryFunc *sx_reg_read[3] = {
>>>> +    sx_reg_readb,
>>>> +    sx_reg_readw,
>>>> +    sx_reg_readl,
>>>> +};
>>>> +
>>>> +static CPUWriteMemoryFunc *sx_reg_write[3] = {
>>>> +    sx_reg_writeb,
>>>> +    sx_reg_writew,
>>>> +    sx_reg_writel,
>>>> +};
>>>> +
>>>> +/*********************/
>>>> +
>>>> +static uint32_t bad_mem_read(void *opaque, target_phys_addr_t addr)
>>>> +{
>>>> +    printf("Bad read from " TARGET_FMT_plx "\n", addr);
>>>> +    //cpu_abort(cpu_single_env, "bad ram read access at " TARGET_FMT_plx "\n", addr);
>>>> +    return 0;
>>>> +}
>>>> +static void bad_mem_write(void *opaque, target_phys_addr_t addr, uint32_t val)
>>>> +{
>>>> +    printf("Bad write of 0x%02x to " TARGET_FMT_plx "\n", val, addr);
>>>> +    //cpu_abort(cpu_single_env, "bad ram write access at " TARGET_FMT_plx "\n", addr);
>>>> +}
>>>> +static CPUReadMemoryFunc *bad_memr[3] = { bad_mem_read, bad_mem_read, bad_mem_read };
>>>> +static CPUWriteMemoryFunc *bad_memw[3] = { bad_mem_write, bad_mem_write, bad_mem_write };
>>>> +
>>>> +void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
>>>> +                uint32_t vram_size)
>>>> +{
>>>> +//    DeviceState *dev;
>>>> +//    SysBusDevice *s;
>>>> +
>>>> +//    dev = qdev_create(NULL, "SUNW,cg14");
>>>> +//    qdev_init(dev);
>>>> +//    s = sysbus_from_qdev(dev);
>>>> +//}
>>>> +
>>>> +//static void cg14_init1(SysBusDevice *dev)
>>>> +//{
>>>> +    CG14State *s;// = FROM_SYSBUS(CG14State, dev);
>>>> +    ram_addr_t vram_offset;
>>>> +    uint8_t *vram;
>>>> +    int ctrl_memory, vram_memory;
>>>> +    int sx_registers;
>>>> +    int bad_mem;
>>>> +
>>>> +    s = qemu_mallocz(sizeof(CG14State));
>>>> +
>>>> +    vram_offset = qemu_ram_alloc(vram_size);
>>>> +    vram = qemu_get_ram_ptr(vram_offset);
>>>> +
>>>> +    s->vram = vram;
>>>> +    s->vram_amask = vram_size - 1;
>>>> +
>>>> +    ctrl_memory = cpu_register_io_memory(cg14_reg_read, cg14_reg_write, s);
>>>> +    cpu_register_physical_memory_offset(ctrl_base, CG14_REG_SIZE, ctrl_memory, ctrl_base);
>>>> +
>>>> +    vram_memory = cpu_register_io_memory(cg14_vram_read, cg14_vram_write, s);
>>>> +    cpu_register_physical_memory_offset(vram_base, CG14_VMEM_SLOTSIZE, vram_memory, vram_base);
>>>> +
>>>> +    s->ds = graphic_console_init(cg14_update_display,
>>>> +                                 cg14_invalidate_display,
>>>> +                                 cg14_screen_dump, NULL, s);
>>>> +
>>>> +    s->width = 640;
>>>> +    s->height = 480;
>>>> +    qemu_console_resize(s->ds, s->width, s->height);
>>>> +
>>>> +    /* SX or SPAM (Sun Pixel Arithmetic Memory) */
>>>> +    sx_registers = cpu_register_io_memory(sx_reg_read, sx_reg_write, s);
>>>> +    cpu_register_physical_memory(0xf80000000ULL, 0x2000, sx_registers);
>>>> +
>>>> +    bad_mem = cpu_register_io_memory(bad_memr, bad_memw, s);
>>>> +    /* missing vsimms */
>>>> +    cpu_register_physical_memory_offset(0x90000000, 0x2000, bad_mem, 0x90000000);
>>>> +    cpu_register_physical_memory_offset(0x94000000, 0x2000, bad_mem, 0x94000000);
>>>> +    cpu_register_physical_memory_offset(0x98000000, 0x2000, bad_mem, 0x98000000);
>>>>
>>> Can't we just have more VSIMMS? The empty_slot device may be useful here.
>>>
> Each VSIMM would have it's own monitor attached.  Does Qemu and/or
> OpenBIOS support multiple displays?  I think the limit for a real SS-20
> is 5 displays - 2 VSIMMs and 3 GX/TGX cards.  I'll just go with
> empty_slot for the extras.
>>
>> And btw, VSIMMs addresses may currently overlap with RAM. Maybe we
>> should keep "SS-20" (and SS-10) compatible with original, and rename
>> the current machine definition to "SS-20qemu" or SS-20-60G?
>>
> Well, the real machines are limited to 512M, so dropping the framebuffer
> registers into the middle of the first 4G wasn't an issue back then.  I
> think the SX accelerator also limits the system and video memory to the
> lower 4G of address space.
>
>>>> +    /* DBRI (audio) */
>>>> +    cpu_register_physical_memory_offset(0xEE0001000ULL, 0x10000, bad_mem, 0xE0001000);
>>>>
>>> Please add a new DBRI device ;-).
>>>
>>
>> Or maybe just a field in hwdef + empty_slot? :-)
>>
> Yes, empty_slot should work fine.  I was trying to hack in enough to get
> it to boot, but no luck yet.

I have a trivial hack which allows booting. It wasn't interesting
before, but now it is,
cause with the hack Solaris 2.3 - 2.5.1 can be started in a single user mode.
Didn't check the multi-user yet. Will submit the patch.

> The cpu models are still lacking - with
> the SS-20 v2.25 rom, SuperSparc 61 fails before initializing the
> display, SuperSparc 60 has a data access error when trying to boot, and
> Ross 625 is mis-detected as an incompatible cpu type.

Yes, one MXCC register is missing in qemu SuperSPARCs with MXCC.
So, I prefer using CPUs without MXCC: "Ross RT620" and "TI SuperSparc 50".
diff mbox

Patch

diff --git a/Makefile.target b/Makefile.target
index fda5bf3..b17b3af 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -250,6 +250,7 @@  else
 obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
 obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
 obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
+obj-sparc-y += cg14.o
 endif
 
 obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
diff --git a/hw/sun4m.c b/hw/sun4m.c
index 7ba0f76..8b23c9b 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -864,6 +864,13 @@  static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ram_addr_t RAM_size,
         fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
         exit (1);
     }
+  if (hwdef->machine_id == 65) { /* SS-20 */
+    /* cg14.c */
+    void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
+                uint32_t vram_size);
+
+    cg14_init(0x09c000000ULL, 0x0fc000000ULL, 8<<20);
+  } else
     tcx_init(hwdef->tcx_base, 0x00100000, graphic_width, graphic_height,
              graphic_depth);
 
--- /dev/null	Fri May 28 02:08:36 2010
+++ hw/cg14.c	Fri May 28 01:58:49 2010
@@ -0,0 +1,785 @@ 
+/*
+ * QEMU CG14 Frame buffer
+ *
+ * Copyright (c) 2010 Bob Breuer
+ *
+ * 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.
+ */
+
+#include "console.h"
+#include "sysbus.h"
+
+#ifdef DEBUG
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+#define CG14_INFO(fmt, ...)                                     \
+    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
+#define CG14_ERROR(fmt, ...)                                    \
+    do { printf("CG14: " fmt , ## __VA_ARGS__); } while (0)
+
+/*
+ * A[28:26] = slot number (4 to 7)
+ * regs: size   0x10000 @ 0x09c000000  (0x80000000 + slot * 64M)
+ * vmem: size upto 16MB @ 0x0fc000000  (0xE0000000 + slot * 64M)
+ */
+
+/*
+ * memory map:
+ * reg+0x0000 = control registers
+ * reg+0x1000 = cursor registers
+ * reg+0x2000 = dac registers (ADV7152)
+ * reg+0x3000 = xlut
+ * reg+0x4000 = clut1
+ * reg+0x5000 = clut2
+ * reg+0x6000 = clut3 (if implemented)
+ *
+ * mem+0x0000000 = XBGR (01234567)
+ * mem+0x1000000 = BGR  (.123.567)
+ * mem+0x2000000 = X16  (0246)
+ * mem+0x2800000 = C16  (1357)
+ * mem+0x3000000 = X32  (04)
+ * mem+0x3400000 = B32  (15)
+ * mem+0x3800000 = G32  (26)
+ * mem+0x3c00000 = R32  (37)
+ */
+
+#define CG14_REG_SIZE         0x10000
+#define CG14_VMEM_SLOTSIZE    (64<<20)
+
+#define CG14_MONID_1024x768   0
+#define CG14_MONID_1600x1280  1
+#define CG14_MONID_1280x1024  2
+#define CG14_MONID_1152x900   7
+
+#define CG14_MONID_DEFAULT    CG14_MONID_1024x768
+
+#define MCR_PIXMODE_MASK  0x30
+#define   MCR_PIXMODE_8     0x00
+#define   MCR_PIXMODE_16    0x20  /* 8+8 (X16,C16) */
+#define   MCR_PIXMODE_32    0x30  /* XBGR */
+
+
+struct ADV7152_state {
+    uint8_t mode;
+    uint8_t address;
+    int rgb_seq;
+};
+
+typedef struct CG14State {
+    SysBusDevice busdev;
+    DisplayState *ds;
+
+    uint8_t *vram;
+    uint32_t vram_amask;
+    int width, height;
+    int dirty, size_changed;
+    struct {
+        uint8_t mcr;
+        uint8_t ppr;
+    } ctrl;
+    struct ADV7152_state dac;
+    struct {
+        uint16_t hblank_start;
+        uint16_t hblank_clear;
+        uint16_t vblank_start;
+        uint16_t vblank_clear;
+    } timing;
+    uint8_t xlut[256];
+    uint32_t clut1[256];
+    uint32_t clut2[256];
+} CG14State;
+
+static void cg14_screen_dump(void *opaque, const char *filename);
+static void cg14_invalidate_display(void *opaque);
+
+static inline uint32_t bgr_to_rgb(uint32_t bgr)
+{
+    uint32_t rgb;
+
+    /* swap r & b */
+    rgb = (bgr & 0x00FF00)
+        | (bgr & 0x0000FF) << 16
+        | (bgr & 0xFF0000) >> 16;
+    return rgb;
+}
+
+static void cg14_draw_line32(const CG14State *s, void *dst, const uint8_t *src, int pixmode, int is_bgr)
+{
+    int i;
+    int x, r, g, b;
+    uint8_t xlut_val;
+    uint32_t dval;
+    uint32_t abgr;
+
+    xlut_val = s->ctrl.ppr;
+
+    for (i=0; i<s->width; i++) {
+        x = *src++;
+        if (pixmode == 8) {
+            b = x;
+        } else {
+            b = *src++;
+            xlut_val = s->xlut[x];
+        }
+        if (pixmode != 32) {
+            r = g = b;
+        } else {
+            g = *src++;
+            r = *src++;
+        }
+        if (xlut_val == 0) {
+            abgr = b << 16 | g << 8 | r;
+        } else if (xlut_val == 0x40) {
+            abgr = s->clut1[x];
+        } else {
+            abgr = 0;
+        }
+        /* dac lookup ? */
+
+        /* to surface format */
+        dval = is_bgr ? (abgr & 0xFFFFFF) : bgr_to_rgb(abgr);
+        ((uint32_t*)dst)[i] = dval;
+    }
+}
+
+static void cg14_update_display(void *opaque)
+{
+    CG14State *s = opaque;
+    int h, pixmode;
+    uint8_t *pix;
+    uint8_t *data;
+    int new_width, new_height;
+
+    if (s->size_changed) {
+        new_width = 4 * (s->timing.hblank_start - s->timing.hblank_clear);
+        new_height = s->timing.vblank_start - s->timing.vblank_clear;
+        s->size_changed = 0;
+        if ((new_width != s->width || new_height != s->height) && new_width > 0 && new_height > 0) {
+            s->width = new_width;
+            s->height = new_height;
+            CG14_INFO("new resolution = %d x %d\n", new_width, new_height);
+            qemu_console_resize(s->ds, s->width, s->height);
+            s->dirty = 1;
+        }
+    }
+
+    if (!s->dirty || !s->width || !s->height) {
+        return;
+    }
+
+    if (ds_get_bits_per_pixel(s->ds) != 32) {
+        CG14_ERROR("cg14_update: FIXME: bpp (%d) != 32, linesize %d\n",
+            ds_get_bits_per_pixel(s->ds), ds_get_linesize(s->ds));
+        return;
+    }
+
+    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
+    case MCR_PIXMODE_32:
+        pixmode = 32;
+        break;
+    case MCR_PIXMODE_16:
+        pixmode = 16;
+        break;
+    case MCR_PIXMODE_8:
+    default:
+        pixmode = 8;
+        break;
+    }
+
+    pix = s->vram;
+    data = ds_get_data(s->ds);
+
+    for (h=0; h<s->height; h++) {
+        cg14_draw_line32(s, data, pix, pixmode, is_surface_bgr(s->ds->surface));
+        pix += s->width * (pixmode / 8);
+        data += ds_get_linesize(s->ds);
+    }
+    dpy_update(s->ds, 0, 0, s->width, s->height);
+    s->dirty = 0;
+}
+
+static void cg14_invalidate_display(void *opaque)
+{
+    CG14State *s = opaque;
+
+    s->dirty = 1;
+}
+
+static void ADV7152_write(struct ADV7152_state *s, unsigned int reg, unsigned int val)
+{
+    switch (reg) {
+    case 0: /* address register */
+        DPRINTF("ADV7152 Write address %02x\n", val);
+        s->address = val;
+        s->rgb_seq = 0;
+        break;
+    case 1: /* look up table */
+        DPRINTF("ADV7152 Write %02x to lookup table\n", val);
+        s->rgb_seq++;
+        break;
+    case 2: /* control registers */
+        DPRINTF("ADV7152 Write %02x to control reg %d\n", val, s->address);
+        switch (s->address) {
+        default:
+            break;
+        }
+        break;
+    case 3: /* mode register */
+        CG14_INFO("ADV7152 Write mode %02x (%d bit DAC, %d bit bus)\n",
+            val, (val & 2) ? 10 : 8, (val & 4) ? 10 : 8);
+        if (!val & 0x01) {
+            // reset the dac
+            s->rgb_seq = 0;
+        }
+        s->mode = val;
+        break;
+    }
+}
+
+static uint32_t cg14_reg_readb(void *opaque, target_phys_addr_t addr)
+{
+    CG14State *s = opaque;
+    unsigned int val;
+
+    switch (addr & 0xffff) {
+    case 0x0000:
+        val = s->ctrl.mcr;
+        break;
+    case 0x0001:
+        val = s->ctrl.ppr;
+        break;
+    case 0x0004: /* status ? */
+        /* monitor code in bits 1..3 */
+        val = CG14_MONID_DEFAULT << 1;
+        break;
+    case 0x0006: /* hw version */
+        //val = 0x00; /* old version */
+        val = 0x30;
+        break;
+    default:
+        val = 0;
+        break;
+    }
+    CG14_INFO("readb %02x from reg %x\n", val, (int)addr);
+
+    return val;
+}
+
+static void cg14_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    CG14State *s = opaque;
+    uint32_t i;
+
+    if ((addr & 0xfcff) == 0x2000) {
+        i = (addr & 0x300) >> 8;
+        ADV7152_write(&s->dac, i, val);
+        return;
+    }
+    if ((addr & 0xff00) == 0x3000) {
+        /* xlut */
+        i = addr & 0xff;
+        if (s->xlut[i] != val) {
+            s->dirty = 1;
+            s->xlut[i] = val;
+            if (val && val != 0x40)
+                CG14_ERROR("writeb xlut[%d] = %02x\n", i, val);
+        }
+        return;
+    }
+
+    s->dirty = 1;
+
+    switch (addr & 0xffff) {
+    case 0x0000:
+        s->ctrl.mcr = val;
+        break;
+    case 0x0001:
+        s->ctrl.ppr = val & 0xF0;
+        break;
+    case 0x0007:
+        /* clock control (ICS1562AM-001) */
+        DPRINTF("write %02x to clock control\n", val);
+        break;
+    default:
+        CG14_ERROR("writeb %02x to reg %x\n", val, (int)addr);
+        break;
+    }
+}
+
+static uint32_t cg14_reg_readw(void *opaque, target_phys_addr_t addr)
+{
+    CG14State *s = opaque;
+    unsigned int val;
+
+    switch (addr & 0xffff) {
+    case 0x0018:
+        val = s->timing.hblank_start;
+        break;
+    case 0x001a:
+        val = s->timing.hblank_clear;
+        break;
+    case 0x0022:
+        val = s->timing.vblank_start;
+        break;
+    case 0x0024:
+        val = s->timing.vblank_clear;
+        break;
+    default:
+        val = 0;
+        break;
+    }
+    CG14_INFO("readw 0x%08x from reg %x\n", val, (int)addr);
+
+    return val;
+}
+
+static void cg14_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    CG14State *s = opaque;
+
+    CG14_INFO("writew %04x to reg %x\n", val, (int)addr);
+
+    /* timing registers are 16bit */
+
+    switch (addr & 0xffff) {
+    case 0x0018:
+        s->timing.hblank_start = val;
+        break;
+    case 0x001a:
+        s->timing.hblank_clear = val;
+	s->size_changed = 1;
+        break;
+    case 0x0022:
+        s->timing.vblank_start = val;
+        break;
+    case 0x0024:
+        s->timing.vblank_clear = val;
+	s->size_changed = 1;
+        break;
+    case 0x001c: /* hsync_start */
+    case 0x001e: /* hsync_clear */
+    case 0x0020: /* csync_clear */
+    case 0x0026: /* vsync_start */
+    case 0x0028: /* vsync_clear */
+    default:
+        break;
+    }
+}
+
+static uint32_t cg14_reg_readl(void *opaque, target_phys_addr_t addr)
+{
+    CG14State *s = opaque;
+    uint32_t val;
+    uint32_t i;
+
+    i = (addr & 0x3ff) >> 2;
+    switch (addr & 0xfc00) {
+    case 0x4000:
+        val = s->clut1[i];
+        break;
+    case 0x5000:
+        val = s->clut2[i];
+        break;
+    default:
+        val = 0;
+        CG14_ERROR("readl %08x from reg %x\n", val, (int)addr);
+        break;
+    }
+
+    return val;
+}
+
+static void cg14_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    CG14State *s = opaque;
+    uint32_t i;
+
+    s->dirty = 1;
+
+    i = (addr & 0x3ff) >> 2;
+    switch (addr & 0xfc00) {
+    case 0x4000:
+        s->clut1[i] = val;
+        break;
+    case 0x5000:
+        s->clut2[i] = val;
+        break;
+    default:
+        CG14_ERROR("writel %08x to reg %x\n", val, (int)addr);
+        break;
+    }
+}
+
+static CPUReadMemoryFunc *cg14_reg_read[3] = {
+    cg14_reg_readb,
+    cg14_reg_readw,
+    cg14_reg_readl,
+};
+
+static CPUWriteMemoryFunc *cg14_reg_write[3] = {
+    cg14_reg_writeb,
+    cg14_reg_writew,
+    cg14_reg_writel,
+};
+
+static uint32_t cg14_vram_readb(void *opaque, target_phys_addr_t addr)
+{
+    CG14State *s = opaque;
+    uint32_t offset;
+    uint32_t val = 0;
+
+    switch (addr & 0x3000000) {
+    case 0x0000000:
+        offset = addr & s->vram_amask;
+        val =  ldub_p(s->vram+offset);
+        break;
+    case 0x1000000:
+        offset = addr & s->vram_amask;
+        val = 0; // FIXME
+        break;
+    case 0x2000000:
+        offset = ((addr << 1) & s->vram_amask) + ((addr >> 23) & 1);
+        val =  ldub_p(s->vram+offset);
+        break;
+    case 0x3000000:
+        offset = ((addr << 2) & s->vram_amask) + ((addr >> 22) & 3);
+        val =  ldub_p(s->vram+offset);
+        break;
+    }
+    CG14_INFO("readb %02x from vram %x\n", val, (int)addr);
+
+    return val;
+}
+
+static void cg14_vram_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    CG14State *s = opaque;
+    uint32_t offset;
+
+    switch (addr & 0x3000000) {
+    case 0x0000000:
+        offset = addr & s->vram_amask;
+        stb_p(s->vram+offset, val);
+        if (offset < 4 * s->width * s->height) {
+            s->dirty = 1;
+        }
+        break;
+    default:
+        CG14_ERROR("writeb %02x to vram %x\n", val, (int)addr);
+        break;
+    }
+}
+
+static uint32_t cg14_vram_readw(void *opaque, target_phys_addr_t addr)
+{
+    CG14State *s = opaque;
+    uint32_t offset;
+    uint32_t val;
+
+    switch (addr & 0x3000000) {
+    default:
+        offset = addr & s->vram_amask;
+        val = 0;
+        break;
+    }
+    CG14_ERROR("readw %04x from vram %x\n", val, (int)addr);
+
+    return val;
+}
+
+static void cg14_vram_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    CG14State *s = opaque;
+
+    CG14_ERROR("writew %04x to vram %x\n", val, (int)addr);
+
+    s->dirty = 1;
+
+    switch (addr & 0x3000000) {
+    default:
+        break;
+    }
+}
+
+static uint32_t cg14_vram_readl(void *opaque, target_phys_addr_t addr)
+{
+    CG14State *s = opaque;
+    uint32_t offset;
+    uint32_t val = 0;
+
+    switch (addr & 0x3000000) {
+    case 0x0000000:
+        offset = addr & s->vram_amask;
+        val = ldl_be_p(s->vram+offset);
+        break;
+    case 0x1000000:
+    case 0x2000000:
+    case 0x3000000:
+        CG14_ERROR("readl %08x from vram %x\n", val, (int)addr);
+        break;
+    }
+
+    return val;
+}
+
+static void cg14_vram_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    CG14State *s = opaque;
+    uint32_t offset;
+
+    switch (addr & 0x3000000) {
+    case 0x0000000:
+        offset = addr & s->vram_amask;
+        stl_be_p(s->vram+offset, val);
+        if (offset < 4 * s->width * s->height) {
+            s->dirty = 1;
+        }
+        break;
+    case 0x1000000:
+    case 0x2000000:
+    case 0x3000000:
+        CG14_ERROR("writel %08x to vram %x\n", val, (int)addr);
+        break;
+    }
+}
+
+static CPUReadMemoryFunc *cg14_vram_read[3] = {
+    cg14_vram_readb,
+    cg14_vram_readw,
+    cg14_vram_readl,
+};
+
+static CPUWriteMemoryFunc *cg14_vram_write[3] = {
+    cg14_vram_writeb,
+    cg14_vram_writew,
+    cg14_vram_writel,
+};
+
+
+/******** SX *********/
+
+static uint32_t sx_reg_readb(void *opaque, target_phys_addr_t addr)
+{
+    //CG14State *s = opaque;
+    int val;
+
+    printf("SX readb reg " TARGET_FMT_plx "\n", addr);
+
+    switch (addr & 0xffff) {
+    default:
+        val = 0;
+        break;
+    }
+    return val;
+}
+
+static void sx_reg_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    //CG14State *s = opaque;
+
+    printf("SX writeb %02x to reg " TARGET_FMT_plx "\n", val, addr);
+
+    switch (addr & 0xffff) {
+    default:
+        break;
+    }
+}
+
+static uint32_t sx_reg_readw(void *opaque, target_phys_addr_t addr)
+{
+    //CG14State *s = opaque;
+    int val;
+
+    printf("SX readw reg " TARGET_FMT_plx "\n", addr);
+
+    switch (addr & 0xffff) {
+    default:
+        val = 0;
+        break;
+    }
+    return val;
+}
+
+static void sx_reg_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    //CG14State *s = opaque;
+
+    printf("SX writew %04x to reg " TARGET_FMT_plx "\n", val, addr);
+
+    switch (addr & 0xffff) {
+    default:
+        break;
+    }
+}
+
+static uint32_t sx_reg_readl(void *opaque, target_phys_addr_t addr)
+{
+    //CG14State *s = opaque;
+    int val;
+
+    printf("SX readl reg " TARGET_FMT_plx "\n", addr);
+
+    switch (addr & 0xffff) {
+    default:
+        val = 0;
+        break;
+    }
+    return val;
+}
+
+static void sx_reg_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    //CG14State *s = opaque;
+
+    printf("SX writel %08x to reg " TARGET_FMT_plx "\n", val, addr);
+
+    switch (addr & 0xffff) {
+    default:
+        break;
+    }
+}
+
+static CPUReadMemoryFunc *sx_reg_read[3] = {
+    sx_reg_readb,
+    sx_reg_readw,
+    sx_reg_readl,
+};
+
+static CPUWriteMemoryFunc *sx_reg_write[3] = {
+    sx_reg_writeb,
+    sx_reg_writew,
+    sx_reg_writel,
+};
+
+/*********************/
+
+static uint32_t bad_mem_read(void *opaque, target_phys_addr_t addr)
+{
+    printf("Bad read from " TARGET_FMT_plx "\n", addr);
+    //cpu_abort(cpu_single_env, "bad ram read access at " TARGET_FMT_plx "\n", addr);
+    return 0;
+}
+static void bad_mem_write(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    printf("Bad write of 0x%02x to " TARGET_FMT_plx "\n", val, addr);
+    //cpu_abort(cpu_single_env, "bad ram write access at " TARGET_FMT_plx "\n", addr);
+}
+static CPUReadMemoryFunc *bad_memr[3] = { bad_mem_read, bad_mem_read, bad_mem_read };
+static CPUWriteMemoryFunc *bad_memw[3] = { bad_mem_write, bad_mem_write, bad_mem_write };
+
+void cg14_init(target_phys_addr_t ctrl_base, target_phys_addr_t vram_base,
+                uint32_t vram_size)
+{
+//    DeviceState *dev;
+//    SysBusDevice *s;
+
+//    dev = qdev_create(NULL, "SUNW,cg14");
+//    qdev_init(dev);
+//    s = sysbus_from_qdev(dev);
+//}
+
+//static void cg14_init1(SysBusDevice *dev)
+//{
+    CG14State *s;// = FROM_SYSBUS(CG14State, dev);
+    ram_addr_t vram_offset;
+    uint8_t *vram;
+    int ctrl_memory, vram_memory;
+    int sx_registers;
+    int bad_mem;
+
+    s = qemu_mallocz(sizeof(CG14State));
+
+    vram_offset = qemu_ram_alloc(vram_size);
+    vram = qemu_get_ram_ptr(vram_offset);
+
+    s->vram = vram;
+    s->vram_amask = vram_size - 1;
+
+    ctrl_memory = cpu_register_io_memory(cg14_reg_read, cg14_reg_write, s);
+    cpu_register_physical_memory_offset(ctrl_base, CG14_REG_SIZE, ctrl_memory, ctrl_base);
+
+    vram_memory = cpu_register_io_memory(cg14_vram_read, cg14_vram_write, s);
+    cpu_register_physical_memory_offset(vram_base, CG14_VMEM_SLOTSIZE, vram_memory, vram_base);
+
+    s->ds = graphic_console_init(cg14_update_display,
+                                 cg14_invalidate_display,
+                                 cg14_screen_dump, NULL, s);
+
+    s->width = 640;
+    s->height = 480;
+    qemu_console_resize(s->ds, s->width, s->height);
+
+    /* SX or SPAM (Sun Pixel Arithmetic Memory) */
+    sx_registers = cpu_register_io_memory(sx_reg_read, sx_reg_write, s);
+    cpu_register_physical_memory(0xf80000000ULL, 0x2000, sx_registers);
+
+    bad_mem = cpu_register_io_memory(bad_memr, bad_memw, s);
+    /* missing vsimms */
+    cpu_register_physical_memory_offset(0x90000000, 0x2000, bad_mem, 0x90000000);
+    cpu_register_physical_memory_offset(0x94000000, 0x2000, bad_mem, 0x94000000);
+    cpu_register_physical_memory_offset(0x98000000, 0x2000, bad_mem, 0x98000000);
+    /* DBRI (audio) */
+    cpu_register_physical_memory_offset(0xEE0001000ULL, 0x10000, bad_mem, 0xE0001000);
+}
+
+/* save to file */
+static void cg14_screen_dump(void *opaque, const char *filename)
+{
+    CG14State *s = opaque;
+    FILE *f;
+    int y, pixmode, linesize;
+    void *buf;
+    uint8_t *pix;
+
+    f = fopen(filename, "wb");
+    if (!f) {
+        return;
+    }
+    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
+
+    linesize = s->width * 3;
+    buf = qemu_mallocz(linesize);
+    pix = s->vram;
+
+    switch (s->ctrl.mcr & MCR_PIXMODE_MASK) {
+    case MCR_PIXMODE_32:
+        pixmode = 32;
+        break;
+    case MCR_PIXMODE_16:
+        pixmode = 16;
+        break;
+    case MCR_PIXMODE_8:
+    default:
+        pixmode = 8;
+        break;
+    }
+
+    for (y=0; y<s->height; y++) {
+        // cg14_draw_line24_bgr(s, buf, pix, pixmode);
+        fwrite(buf, 1, linesize, f);
+        pix += s->width * (pixmode / 8);
+    }
+
+    qemu_free(buf);
+    fclose(f);
+}