diff --git a/hw/g364fb.c b/hw/g364fb.c new file mode 100644 index 0000000000..0dd4ee766c --- /dev/null +++ b/hw/g364fb.c @@ -0,0 +1,396 @@ +/* + * QEMU G364 framebuffer Emulator. + * + * Copyright (c) 2007-2008 Hervé Poussineau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "hw.h" +#include "console.h" +#include "pixel_ops.h" + +//#define DEBUG_G364 + +typedef struct G364State { + target_phys_addr_t vram_base; + unsigned int vram_size; + uint8_t *vram_buffer; + uint32_t ctla; + uint8_t palette[256][3]; + /* display refresh support */ + DisplayState *ds; + int graphic_mode; + uint32_t scr_width, scr_height; /* in pixels */ + uint32_t last_scr_width, last_scr_height; /* in pixels */ +} G364State; + +/* + * graphic modes + */ +#define BPP 8 +#define PIXEL_WIDTH 8 +#include "g364fb_template.h" +#undef BPP +#undef PIXEL_WIDTH + +#define BPP 15 +#define PIXEL_WIDTH 16 +#include "g364fb_template.h" +#undef BPP +#undef PIXEL_WIDTH + +#define BPP 16 +#define PIXEL_WIDTH 16 +#include "g364fb_template.h" +#undef BPP +#undef PIXEL_WIDTH + +#define BPP 32 +#define PIXEL_WIDTH 32 +#include "g364fb_template.h" +#undef BPP +#undef PIXEL_WIDTH + +#define REG_DISPLAYX 0x0918 +#define REG_DISPLAYY 0x0940 + +#define CTLA_FORCE_BLANK 0x400 + +static void g364fb_draw_graphic(G364State *s, int full_update) +{ + if (s->scr_width == 0 || s->scr_height == 0) + return; + if (s->scr_width != s->last_scr_width + || s->scr_height != s->last_scr_height) { + s->last_scr_width = s->scr_width; + s->last_scr_height = s->scr_height; + dpy_resize(s->ds, s->last_scr_width, s->last_scr_height); + full_update = 1; + } + + switch (s->ds->depth) { + case 8: + g364fb_draw_graphic8(s, full_update); + break; + case 15: + g364fb_draw_graphic15(s, full_update); + break; + case 16: + g364fb_draw_graphic16(s, full_update); + break; + case 32: + g364fb_draw_graphic32(s, full_update); + break; + default: + printf("g364fb: unknown depth %d\n", s->ds->depth); + return; + } + + dpy_update(s->ds, 0, 0, s->last_scr_width, s->last_scr_height); +} + +static void g364fb_draw_blank(G364State *s, int full_update) +{ + int i, w; + uint8_t *d; + + if (!full_update) + return; + if (s->last_scr_width <= 0 || s->last_scr_height <= 0) + return; + + w = s->last_scr_width * ((s->ds->depth + 7) >> 3); + d = s->ds->data; + for(i = 0; i < s->last_scr_height; i++) { + memset(d, 0, w); + d += s->ds->linesize; + } + dpy_update(s->ds, 0, 0, + s->last_scr_width, s->last_scr_height); +} + +#define GMODE_GRAPH 0 +#define GMODE_BLANK 1 + +static void g364fb_update_display(void *opaque) +{ + G364State *s = opaque; + int full_update, graphic_mode; + + if (s->ctla & CTLA_FORCE_BLANK) + graphic_mode = GMODE_BLANK; + else + graphic_mode = GMODE_GRAPH; + full_update = 0; + if (graphic_mode != s->graphic_mode) { + s->graphic_mode = graphic_mode; + full_update = 1; + } + switch(graphic_mode) { + case GMODE_GRAPH: + g364fb_draw_graphic(s, full_update); + break; + case GMODE_BLANK: + default: + g364fb_draw_blank(s, full_update); + break; + } +} + +/* force a full display refresh */ +static void g364fb_invalidate_display(void *opaque) +{ + G364State *s = opaque; + s->graphic_mode = -1; /* force full update */ +} + +static void g364fb_reset(void *opaque) +{ + G364State *s = opaque; + + memset(s->palette, 0, sizeof(s->palette)); + s->scr_width = s->scr_height = 0; + s->last_scr_width = s->last_scr_height = 0; + memset(s->vram_buffer, 0, s->vram_size); + s->graphic_mode = -1; /* force full update */ +} + +static void g364fb_screen_dump(void *opaque, const char *filename) +{ + G364State *s = opaque; + int y, x; + uint8_t index; + uint8_t *data_buffer; + FILE *f; + + f = fopen(filename, "wb"); + if (!f) + return; + + data_buffer = s->vram_buffer; + fprintf(f, "P6\n%d %d\n%d\n", + s->scr_width, s->scr_height, 255); + for(y = 0; y < s->scr_height; y++) + for(x = 0; x < s->scr_width; x++, data_buffer++) { + index = *data_buffer; + fputc(s->palette[index][0], f); + fputc(s->palette[index][1], f); + fputc(s->palette[index][2], f); + } + fclose(f); +} + +/* called for accesses to io ports */ +static uint32_t g364fb_ctrl_readb(void *opaque, target_phys_addr_t addr) +{ + //G364State *s = opaque; + uint32_t val; + + addr &= 0xffff; + + switch (addr) { + default: +#ifdef DEBUG_G364 + printf("g364fb/ctrl: invalid read at [" TARGET_FMT_lx "]\n", addr); +#endif + val = 0; + break; + } + +#ifdef DEBUG_G364 + printf("g364fb/ctrl: read 0x%02x at [" TARGET_FMT_lx "]\n", val, addr); +#endif + + return val; +} + +static uint32_t g364fb_ctrl_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; + v = g364fb_ctrl_readb(opaque, addr); + v |= g364fb_ctrl_readb(opaque, addr + 1) << 8; + return v; +} + +static uint32_t g364fb_ctrl_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; + v = g364fb_ctrl_readb(opaque, addr); + v |= g364fb_ctrl_readb(opaque, addr + 1) << 8; + v |= g364fb_ctrl_readb(opaque, addr + 2) << 16; + v |= g364fb_ctrl_readb(opaque, addr + 3) << 24; + return v; +} + +static void g364fb_ctrl_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + G364State *s = opaque; + + addr &= 0xffff; + +#ifdef DEBUG_G364 + printf("g364fb/ctrl: write 0x%02x at [" TARGET_FMT_lx "]\n", val, addr); +#endif + + if (addr < 0x0800) { + /* color palette */ + int idx = addr >> 3; + int c = addr & 7; + if (c < 3) + s->palette[idx][c] = (uint8_t)val; + } else { + switch (addr) { + case REG_DISPLAYX: + s->scr_width = (s->scr_width & 0xfffffc03) | (val << 2); + break; + case REG_DISPLAYX + 1: + s->scr_width = (s->scr_width & 0xfffc03ff) | (val << 10); + break; + case REG_DISPLAYY: + s->scr_height = (s->scr_height & 0xffffff80) | (val >> 1); + break; + case REG_DISPLAYY + 1: + s->scr_height = (s->scr_height & 0xffff801f) | (val << 7); + break; + default: +#ifdef DEBUG_G364 + printf("g364fb/ctrl: invalid write of 0x%02x at [" TARGET_FMT_lx "]\n", val, addr); +#endif + break; + } + } +} + +static void g364fb_ctrl_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + g364fb_ctrl_writeb(opaque, addr, val & 0xff); + g364fb_ctrl_writeb(opaque, addr + 1, (val >> 8) & 0xff); +} + +static void g364fb_ctrl_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + g364fb_ctrl_writeb(opaque, addr, val & 0xff); + g364fb_ctrl_writeb(opaque, addr + 1, (val >> 8) & 0xff); + g364fb_ctrl_writeb(opaque, addr + 2, (val >> 16) & 0xff); + g364fb_ctrl_writeb(opaque, addr + 3, (val >> 24) & 0xff); +} + +static CPUReadMemoryFunc *g364fb_ctrl_read[3] = { + g364fb_ctrl_readb, + g364fb_ctrl_readw, + g364fb_ctrl_readl, +}; + +static CPUWriteMemoryFunc *g364fb_ctrl_write[3] = { + g364fb_ctrl_writeb, + g364fb_ctrl_writew, + g364fb_ctrl_writel, +}; + +/* called for accesses to video ram */ +static uint32_t g364fb_mem_readb(void *opaque, target_phys_addr_t addr) +{ + G364State *s = opaque; + target_phys_addr_t relative_addr = addr - s->vram_base; + + return s->vram_buffer[relative_addr]; +} + +static uint32_t g364fb_mem_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; + v = g364fb_mem_readb(opaque, addr); + v |= g364fb_mem_readb(opaque, addr + 1) << 8; + return v; +} + +static uint32_t g364fb_mem_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; + v = g364fb_mem_readb(opaque, addr); + v |= g364fb_mem_readb(opaque, addr + 1) << 8; + v |= g364fb_mem_readb(opaque, addr + 2) << 16; + v |= g364fb_mem_readb(opaque, addr + 3) << 24; + return v; +} + +static void g364fb_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + G364State *s = opaque; + target_phys_addr_t relative_addr = addr - s->vram_base; + + s->vram_buffer[relative_addr] = val; +} + +static void g364fb_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + g364fb_mem_writeb(opaque, addr, val & 0xff); + g364fb_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); +} + +static void g364fb_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + g364fb_mem_writeb(opaque, addr, val & 0xff); + g364fb_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); + g364fb_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff); + g364fb_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff); +} + +static CPUReadMemoryFunc *g364fb_mem_read[3] = { + g364fb_mem_readb, + g364fb_mem_readw, + g364fb_mem_readl, +}; + +static CPUWriteMemoryFunc *g364fb_mem_write[3] = { + g364fb_mem_writeb, + g364fb_mem_writew, + g364fb_mem_writel, +}; + +int g364fb_mm_init(DisplayState *ds, + int vram_size, int it_shift, + target_phys_addr_t vram_base, target_phys_addr_t ctrl_base) +{ + G364State *s; + int io_vram, io_ctrl; + + s = qemu_mallocz(sizeof(G364State)); + if (!s) + return -1; + + s->vram_size = vram_size; + s->vram_buffer = qemu_mallocz(s->vram_size); + + qemu_register_reset(g364fb_reset, s); + g364fb_reset(s); + + s->ds = ds; + s->vram_base = vram_base; + + graphic_console_init(ds, g364fb_update_display, + g364fb_invalidate_display, g364fb_screen_dump, + NULL, s); + + io_vram = cpu_register_io_memory(0, g364fb_mem_read, g364fb_mem_write, s); + cpu_register_physical_memory(s->vram_base, vram_size, io_vram); + + io_ctrl = cpu_register_io_memory(0, g364fb_ctrl_read, g364fb_ctrl_write, s); + cpu_register_physical_memory(ctrl_base, 0x10000, io_ctrl); + + return 0; +} diff --git a/hw/g364fb_template.h b/hw/g364fb_template.h new file mode 100644 index 0000000000..f16f5e609f --- /dev/null +++ b/hw/g364fb_template.h @@ -0,0 +1,43 @@ +/* + * QEMU G364 framebuffer Emulator. + * + * Copyright (c) 2007 Hervé Poussineau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +static void glue(g364fb_draw_graphic, BPP)(G364State *s, int full_update) +{ + int i, j; + int w_display; + uint8_t *data_buffer; + uint8_t *data_display, *dd; + + data_buffer = s->vram_buffer; + w_display = s->last_scr_width * PIXEL_WIDTH / 8; + data_display = s->ds->data; + for(i = 0; i < s->last_scr_height; i++) { + dd = data_display; + for (j = 0; j < s->last_scr_width; j++, dd += PIXEL_WIDTH / 8, data_buffer++) { + uint8_t index = *data_buffer; + *((glue(glue(uint, PIXEL_WIDTH), _t) *)dd) = glue(rgb_to_pixel, BPP)( + s->palette[index][0], + s->palette[index][1], + s->palette[index][2]); + } + data_display += s->ds->linesize; + } +}