From patchwork Fri May 28 07:54:59 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bob Breuer X-Patchwork-Id: 53869 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 31AEBB7D6C for ; Fri, 28 May 2010 17:52:39 +1000 (EST) Received: from localhost ([127.0.0.1]:57683 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OHuMo-00008y-Bu for incoming@patchwork.ozlabs.org; Fri, 28 May 2010 03:52:30 -0400 Received: from [140.186.70.92] (port=50971 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1OHuM3-00008t-7Z for qemu-devel@nongnu.org; Fri, 28 May 2010 03:51:45 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.69) (envelope-from ) id 1OHuM1-0007xM-1u for qemu-devel@nongnu.org; Fri, 28 May 2010 03:51:42 -0400 Received: from mail.mc.net ([209.172.128.24]:45613) by eggs.gnu.org with smtp (Exim 4.69) (envelope-from ) id 1OHuM0-0007xC-M8 for qemu-devel@nongnu.org; Fri, 28 May 2010 03:51:41 -0400 Received: (qmail 21748 invoked by uid 420); 28 May 2010 07:51:40 -0000 Received: from unknown (HELO ?127.0.0.1?) (breuerr@209.172.177.18) by mail.mc.net with SMTP; 28 May 2010 07:51:40 -0000 Message-ID: <4BFF76D3.80606@mc.net> Date: Fri, 28 May 2010 02:54:59 -0500 From: Bob Breuer User-Agent: Thunderbird 2.0.0.24 (Windows/20100228) MIME-Version: 1.0 To: Artyom Tarasenko Subject: Re: [Qemu-devel] cg14 References: In-Reply-To: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.4-2.6 Cc: Blue Swirl , qemu-devel@nongnu.org X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Artyom Tarasenko wrote: > 2010/5/27 Bob Breuer : > >> 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 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; iwidth; 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; hheight; 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; yheight; y++) { + // cg14_draw_line24_bgr(s, buf, pix, pixmode); + fwrite(buf, 1, linesize, f); + pix += s->width * (pixmode / 8); + } + + qemu_free(buf); + fclose(f); +}