From: Toni Wilen Date: Sun, 7 Jul 2013 15:10:28 +0000 (+0300) Subject: Added cirrus logic emulation. X-Git-Tag: 2700~19 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=ee4cfb19369c5edc4acd95c9210cc1ff3b221047;p=francis%2Fwinuae.git Added cirrus logic emulation. --- diff --git a/qemuvga/cirrus_vga.cpp b/qemuvga/cirrus_vga.cpp new file mode 100644 index 00000000..273293c4 --- /dev/null +++ b/qemuvga/cirrus_vga.cpp @@ -0,0 +1,3003 @@ +/* + * QEMU Cirrus CLGD 54xx VGA Emulator. + * + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2004 Makoto Suzuki (suzu) + * + * 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. + */ +/* + * Reference: Finn Thogersons' VGADOC4b + * available at http://home.worldonline.dk/~finth/ + */ +#if 0 +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "ui/console.h" +#include "vga_int.h" +#include "hw/loader.h" +#else +#include "qemuuaeglue.h" +#endif + +/* + * TODO: + * - destination write mask support not complete (bits 5..7) + * - optimize linear mappings + * - optimize bitblt functions + */ + +//#define DEBUG_CIRRUS +//#define DEBUG_BITBLT + +/*************************************** + * + * definitions + * + ***************************************/ + +// sequencer 0x07 +#define CIRRUS_SR7_BPP_VGA 0x00 +#define CIRRUS_SR7_BPP_SVGA 0x01 +#define CIRRUS_SR7_BPP_MASK 0x0e +#define CIRRUS_SR7_BPP_8 0x00 +#define CIRRUS_SR7_BPP_16_DOUBLEVCLK 0x02 +#define CIRRUS_SR7_BPP_24 0x04 +#define CIRRUS_SR7_BPP_16 0x06 +#define CIRRUS_SR7_BPP_32 0x08 +#define CIRRUS_SR7_ISAADDR_MASK 0xe0 + +// sequencer 0x0f +#define CIRRUS_MEMSIZE_512k 0x08 +#define CIRRUS_MEMSIZE_1M 0x10 +#define CIRRUS_MEMSIZE_2M 0x18 +#define CIRRUS_MEMFLAGS_BANKSWITCH 0x80 // bank switching is enabled. + +// sequencer 0x12 +#define CIRRUS_CURSOR_SHOW 0x01 +#define CIRRUS_CURSOR_HIDDENPEL 0x02 +#define CIRRUS_CURSOR_LARGE 0x04 // 64x64 if set, 32x32 if clear + +// sequencer 0x17 +#define CIRRUS_BUSTYPE_VLBFAST 0x10 +#define CIRRUS_BUSTYPE_PCI 0x20 +#define CIRRUS_BUSTYPE_VLBSLOW 0x30 +#define CIRRUS_BUSTYPE_ISA 0x38 +#define CIRRUS_MMIO_ENABLE 0x04 +#define CIRRUS_MMIO_USE_PCIADDR 0x40 // 0xb8000 if cleared. +#define CIRRUS_MEMSIZEEXT_DOUBLE 0x80 + +// control 0x0b +#define CIRRUS_BANKING_DUAL 0x01 +#define CIRRUS_BANKING_GRANULARITY_16K 0x20 // set:16k, clear:4k + +// control 0x30 +#define CIRRUS_BLTMODE_BACKWARDS 0x01 +#define CIRRUS_BLTMODE_MEMSYSDEST 0x02 +#define CIRRUS_BLTMODE_MEMSYSSRC 0x04 +#define CIRRUS_BLTMODE_TRANSPARENTCOMP 0x08 +#define CIRRUS_BLTMODE_PATTERNCOPY 0x40 +#define CIRRUS_BLTMODE_COLOREXPAND 0x80 +#define CIRRUS_BLTMODE_PIXELWIDTHMASK 0x30 +#define CIRRUS_BLTMODE_PIXELWIDTH8 0x00 +#define CIRRUS_BLTMODE_PIXELWIDTH16 0x10 +#define CIRRUS_BLTMODE_PIXELWIDTH24 0x20 +#define CIRRUS_BLTMODE_PIXELWIDTH32 0x30 + +// control 0x31 +#define CIRRUS_BLT_BUSY 0x01 +#define CIRRUS_BLT_START 0x02 +#define CIRRUS_BLT_RESET 0x04 +#define CIRRUS_BLT_STATUS 0x08 +#define CIRRUS_BLT_FIFOUSED 0x10 +#define CIRRUS_BLT_AUTOSTART 0x80 + +// control 0x32 +#define CIRRUS_ROP_0 0x00 +#define CIRRUS_ROP_SRC_AND_DST 0x05 +#define CIRRUS_ROP_NOP 0x06 +#define CIRRUS_ROP_SRC_AND_NOTDST 0x09 +#define CIRRUS_ROP_NOTDST 0x0b +#define CIRRUS_ROP_SRC 0x0d +#define CIRRUS_ROP_1 0x0e +#define CIRRUS_ROP_NOTSRC_AND_DST 0x50 +#define CIRRUS_ROP_SRC_XOR_DST 0x59 +#define CIRRUS_ROP_SRC_OR_DST 0x6d +#define CIRRUS_ROP_NOTSRC_OR_NOTDST 0x90 +#define CIRRUS_ROP_SRC_NOTXOR_DST 0x95 +#define CIRRUS_ROP_SRC_OR_NOTDST 0xad +#define CIRRUS_ROP_NOTSRC 0xd0 +#define CIRRUS_ROP_NOTSRC_OR_DST 0xd6 +#define CIRRUS_ROP_NOTSRC_AND_NOTDST 0xda + +#define CIRRUS_ROP_NOP_INDEX 2 +#define CIRRUS_ROP_SRC_INDEX 5 + +// control 0x33 +#define CIRRUS_BLTMODEEXT_SOLIDFILL 0x04 +#define CIRRUS_BLTMODEEXT_COLOREXPINV 0x02 +#define CIRRUS_BLTMODEEXT_DWORDGRANULARITY 0x01 + +// memory-mapped IO +#define CIRRUS_MMIO_BLTBGCOLOR 0x00 // dword +#define CIRRUS_MMIO_BLTFGCOLOR 0x04 // dword +#define CIRRUS_MMIO_BLTWIDTH 0x08 // word +#define CIRRUS_MMIO_BLTHEIGHT 0x0a // word +#define CIRRUS_MMIO_BLTDESTPITCH 0x0c // word +#define CIRRUS_MMIO_BLTSRCPITCH 0x0e // word +#define CIRRUS_MMIO_BLTDESTADDR 0x10 // dword +#define CIRRUS_MMIO_BLTSRCADDR 0x14 // dword +#define CIRRUS_MMIO_BLTWRITEMASK 0x17 // byte +#define CIRRUS_MMIO_BLTMODE 0x18 // byte +#define CIRRUS_MMIO_BLTROP 0x1a // byte +#define CIRRUS_MMIO_BLTMODEEXT 0x1b // byte +#define CIRRUS_MMIO_BLTTRANSPARENTCOLOR 0x1c // word? +#define CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK 0x20 // word? +#define CIRRUS_MMIO_LINEARDRAW_START_X 0x24 // word +#define CIRRUS_MMIO_LINEARDRAW_START_Y 0x26 // word +#define CIRRUS_MMIO_LINEARDRAW_END_X 0x28 // word +#define CIRRUS_MMIO_LINEARDRAW_END_Y 0x2a // word +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_INC 0x2c // byte +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ROLLOVER 0x2d // byte +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_MASK 0x2e // byte +#define CIRRUS_MMIO_LINEARDRAW_LINESTYLE_ACCUM 0x2f // byte +#define CIRRUS_MMIO_BRESENHAM_K1 0x30 // word +#define CIRRUS_MMIO_BRESENHAM_K3 0x32 // word +#define CIRRUS_MMIO_BRESENHAM_ERROR 0x34 // word +#define CIRRUS_MMIO_BRESENHAM_DELTA_MAJOR 0x36 // word +#define CIRRUS_MMIO_BRESENHAM_DIRECTION 0x38 // byte +#define CIRRUS_MMIO_LINEDRAW_MODE 0x39 // byte +#define CIRRUS_MMIO_BLTSTATUS 0x40 // byte + +#define CIRRUS_PNPMMIO_SIZE 0x1000 + +#define BLTUNSAFE(s) \ + ( \ + ( /* check dst is within bounds */ \ + (s)->cirrus_blt_height * ABS((s)->cirrus_blt_dstpitch) \ + + ((s)->cirrus_blt_dstaddr & (s)->cirrus_addr_mask) > \ + (s)->vga.vram_size \ + ) || \ + ( /* check src is within bounds */ \ + (s)->cirrus_blt_height * ABS((s)->cirrus_blt_srcpitch) \ + + ((s)->cirrus_blt_srcaddr & (s)->cirrus_addr_mask) > \ + (s)->vga.vram_size \ + ) \ + ) + +#if 0 +typedef struct PCICirrusVGAState { + PCIDevice dev; + CirrusVGAState cirrus_vga; +} PCICirrusVGAState; + +#define TYPE_ISA_CIRRUS_VGA "isa-cirrus-vga" +#define ISA_CIRRUS_VGA(obj) \ + OBJECT_CHECK(ISACirrusVGAState, (obj), TYPE_ISA_CIRRUS_VGA) + +typedef struct ISACirrusVGAState { + ISADevice parent_obj; + + CirrusVGAState cirrus_vga; +} ISACirrusVGAState; +#endif + +static uint8_t rop_to_index[256]; + +/*************************************** + * + * prototypes. + * + ***************************************/ + + +static void cirrus_bitblt_reset(CirrusVGAState *s); +static void cirrus_update_memory_access(CirrusVGAState *s); + +/*************************************** + * + * raster operations + * + ***************************************/ + +static void cirrus_bitblt_rop_nop(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ +} + +static void cirrus_bitblt_fill_nop(CirrusVGAState *s, + uint8_t *dst, + int dstpitch, int bltwidth,int bltheight) +{ +} + + +#define ROP_NAME 0 +#define ROP_FN(d, s) 0 +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_and_dst +#define ROP_FN(d, s) (s) & (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_and_notdst +#define ROP_FN(d, s) (s) & (~(d)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notdst +#define ROP_FN(d, s) ~(d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src +#define ROP_FN(d, s) s +#include "cirrus_vga_rop.h" + +#define ROP_NAME 1 +#define ROP_FN(d, s) ~0 +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc_and_dst +#define ROP_FN(d, s) (~(s)) & (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_xor_dst +#define ROP_FN(d, s) (s) ^ (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_or_dst +#define ROP_FN(d, s) (s) | (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc_or_notdst +#define ROP_FN(d, s) (~(s)) | (~(d)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_notxor_dst +#define ROP_FN(d, s) ~((s) ^ (d)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME src_or_notdst +#define ROP_FN(d, s) (s) | (~(d)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc +#define ROP_FN(d, s) (~(s)) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc_or_dst +#define ROP_FN(d, s) (~(s)) | (d) +#include "cirrus_vga_rop.h" + +#define ROP_NAME notsrc_and_notdst +#define ROP_FN(d, s) (~(s)) & (~(d)) +#include "cirrus_vga_rop.h" + +static const cirrus_bitblt_rop_t cirrus_fwd_rop[16] = { + cirrus_bitblt_rop_fwd_0, + cirrus_bitblt_rop_fwd_src_and_dst, + cirrus_bitblt_rop_nop, + cirrus_bitblt_rop_fwd_src_and_notdst, + cirrus_bitblt_rop_fwd_notdst, + cirrus_bitblt_rop_fwd_src, + cirrus_bitblt_rop_fwd_1, + cirrus_bitblt_rop_fwd_notsrc_and_dst, + cirrus_bitblt_rop_fwd_src_xor_dst, + cirrus_bitblt_rop_fwd_src_or_dst, + cirrus_bitblt_rop_fwd_notsrc_or_notdst, + cirrus_bitblt_rop_fwd_src_notxor_dst, + cirrus_bitblt_rop_fwd_src_or_notdst, + cirrus_bitblt_rop_fwd_notsrc, + cirrus_bitblt_rop_fwd_notsrc_or_dst, + cirrus_bitblt_rop_fwd_notsrc_and_notdst, +}; + +static const cirrus_bitblt_rop_t cirrus_bkwd_rop[16] = { + cirrus_bitblt_rop_bkwd_0, + cirrus_bitblt_rop_bkwd_src_and_dst, + cirrus_bitblt_rop_nop, + cirrus_bitblt_rop_bkwd_src_and_notdst, + cirrus_bitblt_rop_bkwd_notdst, + cirrus_bitblt_rop_bkwd_src, + cirrus_bitblt_rop_bkwd_1, + cirrus_bitblt_rop_bkwd_notsrc_and_dst, + cirrus_bitblt_rop_bkwd_src_xor_dst, + cirrus_bitblt_rop_bkwd_src_or_dst, + cirrus_bitblt_rop_bkwd_notsrc_or_notdst, + cirrus_bitblt_rop_bkwd_src_notxor_dst, + cirrus_bitblt_rop_bkwd_src_or_notdst, + cirrus_bitblt_rop_bkwd_notsrc, + cirrus_bitblt_rop_bkwd_notsrc_or_dst, + cirrus_bitblt_rop_bkwd_notsrc_and_notdst, +}; + +#define TRANSP_ROP(name) {\ + name ## _8,\ + name ## _16,\ + } +#define TRANSP_NOP(func) {\ + func,\ + func,\ + } + +static const cirrus_bitblt_rop_t cirrus_fwd_transp_rop[16][2] = { + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_0), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_dst), + TRANSP_NOP(cirrus_bitblt_rop_nop), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_and_notdst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notdst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_1), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_xor_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_notdst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_notxor_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_src_or_notdst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_or_dst), + TRANSP_ROP(cirrus_bitblt_rop_fwd_transp_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_bkwd_transp_rop[16][2] = { + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_0), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_dst), + TRANSP_NOP(cirrus_bitblt_rop_nop), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_and_notdst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notdst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_1), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_xor_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_notdst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_notxor_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_src_or_notdst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_or_dst), + TRANSP_ROP(cirrus_bitblt_rop_bkwd_transp_notsrc_and_notdst), +}; + +#define ROP2(name) {\ + name ## _8,\ + name ## _16,\ + name ## _24,\ + name ## _32,\ + } + +#define ROP_NOP2(func) {\ + func,\ + func,\ + func,\ + func,\ + } + +static const cirrus_bitblt_rop_t cirrus_patternfill[16][4] = { + ROP2(cirrus_patternfill_0), + ROP2(cirrus_patternfill_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_patternfill_src_and_notdst), + ROP2(cirrus_patternfill_notdst), + ROP2(cirrus_patternfill_src), + ROP2(cirrus_patternfill_1), + ROP2(cirrus_patternfill_notsrc_and_dst), + ROP2(cirrus_patternfill_src_xor_dst), + ROP2(cirrus_patternfill_src_or_dst), + ROP2(cirrus_patternfill_notsrc_or_notdst), + ROP2(cirrus_patternfill_src_notxor_dst), + ROP2(cirrus_patternfill_src_or_notdst), + ROP2(cirrus_patternfill_notsrc), + ROP2(cirrus_patternfill_notsrc_or_dst), + ROP2(cirrus_patternfill_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_colorexpand_transp[16][4] = { + ROP2(cirrus_colorexpand_transp_0), + ROP2(cirrus_colorexpand_transp_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_transp_src_and_notdst), + ROP2(cirrus_colorexpand_transp_notdst), + ROP2(cirrus_colorexpand_transp_src), + ROP2(cirrus_colorexpand_transp_1), + ROP2(cirrus_colorexpand_transp_notsrc_and_dst), + ROP2(cirrus_colorexpand_transp_src_xor_dst), + ROP2(cirrus_colorexpand_transp_src_or_dst), + ROP2(cirrus_colorexpand_transp_notsrc_or_notdst), + ROP2(cirrus_colorexpand_transp_src_notxor_dst), + ROP2(cirrus_colorexpand_transp_src_or_notdst), + ROP2(cirrus_colorexpand_transp_notsrc), + ROP2(cirrus_colorexpand_transp_notsrc_or_dst), + ROP2(cirrus_colorexpand_transp_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_colorexpand[16][4] = { + ROP2(cirrus_colorexpand_0), + ROP2(cirrus_colorexpand_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_src_and_notdst), + ROP2(cirrus_colorexpand_notdst), + ROP2(cirrus_colorexpand_src), + ROP2(cirrus_colorexpand_1), + ROP2(cirrus_colorexpand_notsrc_and_dst), + ROP2(cirrus_colorexpand_src_xor_dst), + ROP2(cirrus_colorexpand_src_or_dst), + ROP2(cirrus_colorexpand_notsrc_or_notdst), + ROP2(cirrus_colorexpand_src_notxor_dst), + ROP2(cirrus_colorexpand_src_or_notdst), + ROP2(cirrus_colorexpand_notsrc), + ROP2(cirrus_colorexpand_notsrc_or_dst), + ROP2(cirrus_colorexpand_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern_transp[16][4] = { + ROP2(cirrus_colorexpand_pattern_transp_0), + ROP2(cirrus_colorexpand_pattern_transp_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_pattern_transp_src_and_notdst), + ROP2(cirrus_colorexpand_pattern_transp_notdst), + ROP2(cirrus_colorexpand_pattern_transp_src), + ROP2(cirrus_colorexpand_pattern_transp_1), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_dst), + ROP2(cirrus_colorexpand_pattern_transp_src_xor_dst), + ROP2(cirrus_colorexpand_pattern_transp_src_or_dst), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_notdst), + ROP2(cirrus_colorexpand_pattern_transp_src_notxor_dst), + ROP2(cirrus_colorexpand_pattern_transp_src_or_notdst), + ROP2(cirrus_colorexpand_pattern_transp_notsrc), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_or_dst), + ROP2(cirrus_colorexpand_pattern_transp_notsrc_and_notdst), +}; + +static const cirrus_bitblt_rop_t cirrus_colorexpand_pattern[16][4] = { + ROP2(cirrus_colorexpand_pattern_0), + ROP2(cirrus_colorexpand_pattern_src_and_dst), + ROP_NOP2(cirrus_bitblt_rop_nop), + ROP2(cirrus_colorexpand_pattern_src_and_notdst), + ROP2(cirrus_colorexpand_pattern_notdst), + ROP2(cirrus_colorexpand_pattern_src), + ROP2(cirrus_colorexpand_pattern_1), + ROP2(cirrus_colorexpand_pattern_notsrc_and_dst), + ROP2(cirrus_colorexpand_pattern_src_xor_dst), + ROP2(cirrus_colorexpand_pattern_src_or_dst), + ROP2(cirrus_colorexpand_pattern_notsrc_or_notdst), + ROP2(cirrus_colorexpand_pattern_src_notxor_dst), + ROP2(cirrus_colorexpand_pattern_src_or_notdst), + ROP2(cirrus_colorexpand_pattern_notsrc), + ROP2(cirrus_colorexpand_pattern_notsrc_or_dst), + ROP2(cirrus_colorexpand_pattern_notsrc_and_notdst), +}; + +static const cirrus_fill_t cirrus_fill[16][4] = { + ROP2(cirrus_fill_0), + ROP2(cirrus_fill_src_and_dst), + ROP_NOP2(cirrus_bitblt_fill_nop), + ROP2(cirrus_fill_src_and_notdst), + ROP2(cirrus_fill_notdst), + ROP2(cirrus_fill_src), + ROP2(cirrus_fill_1), + ROP2(cirrus_fill_notsrc_and_dst), + ROP2(cirrus_fill_src_xor_dst), + ROP2(cirrus_fill_src_or_dst), + ROP2(cirrus_fill_notsrc_or_notdst), + ROP2(cirrus_fill_src_notxor_dst), + ROP2(cirrus_fill_src_or_notdst), + ROP2(cirrus_fill_notsrc), + ROP2(cirrus_fill_notsrc_or_dst), + ROP2(cirrus_fill_notsrc_and_notdst), +}; + +STATIC_INLINE void cirrus_bitblt_fgcol(CirrusVGAState *s) +{ + unsigned int color; + switch (s->cirrus_blt_pixelwidth) { + case 1: + s->cirrus_blt_fgcol = s->cirrus_shadow_gr1; + break; + case 2: + color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8); + s->cirrus_blt_fgcol = le16_to_cpu(color); + break; + case 3: + s->cirrus_blt_fgcol = s->cirrus_shadow_gr1 | + (s->vga.gr[0x11] << 8) | (s->vga.gr[0x13] << 16); + break; + default: + case 4: + color = s->cirrus_shadow_gr1 | (s->vga.gr[0x11] << 8) | + (s->vga.gr[0x13] << 16) | (s->vga.gr[0x15] << 24); + s->cirrus_blt_fgcol = le32_to_cpu(color); + break; + } +} + +STATIC_INLINE void cirrus_bitblt_bgcol(CirrusVGAState *s) +{ + unsigned int color; + switch (s->cirrus_blt_pixelwidth) { + case 1: + s->cirrus_blt_bgcol = s->cirrus_shadow_gr0; + break; + case 2: + color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8); + s->cirrus_blt_bgcol = le16_to_cpu(color); + break; + case 3: + s->cirrus_blt_bgcol = s->cirrus_shadow_gr0 | + (s->vga.gr[0x10] << 8) | (s->vga.gr[0x12] << 16); + break; + default: + case 4: + color = s->cirrus_shadow_gr0 | (s->vga.gr[0x10] << 8) | + (s->vga.gr[0x12] << 16) | (s->vga.gr[0x14] << 24); + s->cirrus_blt_bgcol = le32_to_cpu(color); + break; + } +} + +static void cirrus_invalidate_region(CirrusVGAState * s, int off_begin, + int off_pitch, int bytesperline, + int lines) +{ + int y; + int off_cur; + int off_cur_end; + + for (y = 0; y < lines; y++) { + off_cur = off_begin; + off_cur_end = (off_cur + bytesperline) & s->cirrus_addr_mask; + memory_region_set_dirty(&s->vga.vram, off_cur, off_cur_end - off_cur); + off_begin += off_pitch; + } +} + +static int cirrus_bitblt_common_patterncopy(CirrusVGAState * s, + const uint8_t * src) +{ + uint8_t *dst; + + dst = s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask); + +// if (BLTUNSAFE(s)) +// return 0; + + (*s->cirrus_rop) (s, dst, src, + s->cirrus_blt_dstpitch, 0, + s->cirrus_blt_width, s->cirrus_blt_height); + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); + return 1; +} + +/* fill */ + +static int cirrus_bitblt_solidfill(CirrusVGAState *s, int blt_rop) +{ + cirrus_fill_t rop_func; + +// if (BLTUNSAFE(s)) +// return 0; + rop_func = cirrus_fill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + rop_func(s, s->vga.vram_ptr + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask), + s->cirrus_blt_dstpitch, + s->cirrus_blt_width, s->cirrus_blt_height); + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); + cirrus_bitblt_reset(s); + return 1; +} + +/*************************************** + * + * bitblt (video-to-video) + * + ***************************************/ + +static int cirrus_bitblt_videotovideo_patterncopy(CirrusVGAState * s) +{ + return cirrus_bitblt_common_patterncopy(s, + s->vga.vram_ptr + ((s->cirrus_blt_srcaddr & ~7) & + s->cirrus_addr_mask)); +} + +static void cirrus_do_copy(CirrusVGAState *s, int dst, int src, int w, int h) +{ + int sx = 0, sy = 0; + int dx = 0, dy = 0; + int depth = 0; + int notify = 0; + + /* make sure to only copy if it's a plain copy ROP */ + if (*s->cirrus_rop == cirrus_bitblt_rop_fwd_src || + *s->cirrus_rop == cirrus_bitblt_rop_bkwd_src) { + + int width, height; + + depth = s->vga.get_bpp(&s->vga) / 8; + s->vga.get_resolution(&s->vga, &width, &height); + + /* TW FIXME! */ + /* extra x, y */ + sx = s->cirrus_blt_srcpitch ? (src % ABS(s->cirrus_blt_srcpitch)) / depth : 0; + sy = s->cirrus_blt_srcpitch ? (src / ABS(s->cirrus_blt_srcpitch)) : 0; + dx = s->cirrus_blt_dstpitch ? (dst % ABS(s->cirrus_blt_dstpitch)) / depth : 0; + dy = s->cirrus_blt_dstpitch ? (dst / ABS(s->cirrus_blt_dstpitch)) : 0; + + /* normalize width */ + w /= depth; + + /* if we're doing a backward copy, we have to adjust + our x/y to be the upper left corner (instead of the lower + right corner) */ + if (s->cirrus_blt_dstpitch < 0) { + sx -= (s->cirrus_blt_width / depth) - 1; + dx -= (s->cirrus_blt_width / depth) - 1; + sy -= s->cirrus_blt_height - 1; + dy -= s->cirrus_blt_height - 1; + } + + /* are we in the visible portion of memory? */ + if (sx >= 0 && sy >= 0 && dx >= 0 && dy >= 0 && + (sx + w) <= width && (sy + h) <= height && + (dx + w) <= width && (dy + h) <= height) { + notify = 1; + } + } + + /* we have to flush all pending changes so that the copy + is generated at the appropriate moment in time */ + if (notify) + graphic_hw_update(s->vga.con); + + (*s->cirrus_rop) (s, s->vga.vram_ptr + + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask), + s->vga.vram_ptr + + (s->cirrus_blt_srcaddr & s->cirrus_addr_mask), + s->cirrus_blt_dstpitch, s->cirrus_blt_srcpitch, + s->cirrus_blt_width, s->cirrus_blt_height); + + if (notify) { + qemu_console_copy(s->vga.con, + sx, sy, dx, dy, + s->cirrus_blt_width / depth, + s->cirrus_blt_height); + } + + /* we don't have to notify the display that this portion has + changed since qemu_console_copy implies this */ + + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, + s->cirrus_blt_dstpitch, s->cirrus_blt_width, + s->cirrus_blt_height); +} + +static int cirrus_bitblt_videotovideo_copy(CirrusVGAState * s) +{ +// if (BLTUNSAFE(s)) +// return 0; + + cirrus_do_copy(s, s->cirrus_blt_dstaddr - s->vga.start_addr, + s->cirrus_blt_srcaddr - s->vga.start_addr, + s->cirrus_blt_width, s->cirrus_blt_height); + + return 1; +} + +/*************************************** + * + * bitblt (cpu-to-video) + * + ***************************************/ + +static void cirrus_bitblt_cputovideo_next(CirrusVGAState * s) +{ + int copy_count; + uint8_t *end_ptr; + + if (s->cirrus_srccounter > 0) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { + cirrus_bitblt_common_patterncopy(s, s->cirrus_bltbuf); + the_end: + s->cirrus_srccounter = 0; + cirrus_bitblt_reset(s); + } else { + /* at least one scan line */ + do { + (*s->cirrus_rop)(s, s->vga.vram_ptr + + (s->cirrus_blt_dstaddr & s->cirrus_addr_mask), + s->cirrus_bltbuf, 0, 0, s->cirrus_blt_width, 1); + cirrus_invalidate_region(s, s->cirrus_blt_dstaddr, 0, + s->cirrus_blt_width, 1); + s->cirrus_blt_dstaddr += s->cirrus_blt_dstpitch; + s->cirrus_srccounter -= s->cirrus_blt_srcpitch; + if (s->cirrus_srccounter <= 0) + goto the_end; + /* more bytes than needed can be transferred because of + word alignment, so we keep them for the next line */ + /* XXX: keep alignment to speed up transfer */ + end_ptr = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; + copy_count = s->cirrus_srcptr_end - end_ptr; + memmove(s->cirrus_bltbuf, end_ptr, copy_count); + s->cirrus_srcptr = s->cirrus_bltbuf + copy_count; + s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; + } while (s->cirrus_srcptr >= s->cirrus_srcptr_end); + } + } +} + +/*************************************** + * + * bitblt wrapper + * + ***************************************/ + +static void cirrus_bitblt_reset(CirrusVGAState * s) +{ + int need_update; + + s->vga.gr[0x31] &= + ~(CIRRUS_BLT_START | CIRRUS_BLT_BUSY | CIRRUS_BLT_FIFOUSED | CIRRUS_BLT_STATUS); + need_update = s->cirrus_srcptr != &s->cirrus_bltbuf[0] + || s->cirrus_srcptr_end != &s->cirrus_bltbuf[0]; + s->cirrus_srcptr = &s->cirrus_bltbuf[0]; + s->cirrus_srcptr_end = &s->cirrus_bltbuf[0]; + s->cirrus_srccounter = 0; + if (!need_update) + return; + cirrus_update_memory_access(s); +} + +static int cirrus_bitblt_cputovideo(CirrusVGAState * s) +{ + int w; + + s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_MEMSYSSRC; + s->cirrus_srcptr = &s->cirrus_bltbuf[0]; + s->cirrus_srcptr_end = &s->cirrus_bltbuf[0]; + + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { + s->cirrus_blt_srcpitch = 8; + } else { + /* XXX: check for 24 bpp */ + s->cirrus_blt_srcpitch = 8 * 8 * s->cirrus_blt_pixelwidth; + } + s->cirrus_srccounter = s->cirrus_blt_srcpitch; + } else { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { + w = s->cirrus_blt_width / s->cirrus_blt_pixelwidth; + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_DWORDGRANULARITY) + s->cirrus_blt_srcpitch = ((w + 31) >> 5); + else + s->cirrus_blt_srcpitch = ((w + 7) >> 3); + } else { + /* always align input size to 32 bits */ + s->cirrus_blt_srcpitch = (s->cirrus_blt_width + 3) & ~3; + } + s->cirrus_srccounter = s->cirrus_blt_srcpitch * s->cirrus_blt_height; + } + s->cirrus_srcptr = s->cirrus_bltbuf; + s->cirrus_srcptr_end = s->cirrus_bltbuf + s->cirrus_blt_srcpitch; + cirrus_update_memory_access(s); + return 1; +} + +static int cirrus_bitblt_videotocpu(CirrusVGAState * s) +{ + /* XXX */ +#ifdef DEBUG_BITBLT + write_log("cirrus: bitblt (video to cpu) is not implemented yet\n"); +#endif + return 0; +} + +static int cirrus_bitblt_videotovideo(CirrusVGAState * s) +{ + int ret; + + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { + ret = cirrus_bitblt_videotovideo_patterncopy(s); + } else { + ret = cirrus_bitblt_videotovideo_copy(s); + } + if (ret) + cirrus_bitblt_reset(s); + return ret; +} + +static void cirrus_bitblt_start(CirrusVGAState * s) +{ + uint8_t blt_rop; + + s->vga.gr[0x31] |= CIRRUS_BLT_BUSY | CIRRUS_BLT_STATUS; + + s->cirrus_blt_width = (s->vga.gr[0x20] | (s->vga.gr[0x21] << 8)) + 1; + s->cirrus_blt_height = (s->vga.gr[0x22] | (s->vga.gr[0x23] << 8)) + 1; + s->cirrus_blt_dstpitch = (s->vga.gr[0x24] | (s->vga.gr[0x25] << 8)); + s->cirrus_blt_srcpitch = (s->vga.gr[0x26] | (s->vga.gr[0x27] << 8)); + s->cirrus_blt_dstaddr = + (s->vga.gr[0x28] | (s->vga.gr[0x29] << 8) | (s->vga.gr[0x2a] << 16)); + s->cirrus_blt_srcaddr = + (s->vga.gr[0x2c] | (s->vga.gr[0x2d] << 8) | (s->vga.gr[0x2e] << 16)); + s->cirrus_blt_mode = s->vga.gr[0x30]; + s->cirrus_blt_modeext = s->vga.gr[0x33]; + blt_rop = s->vga.gr[0x32]; + +#ifdef DEBUG_BITBLT + write_log("rop=0x%02x mode=0x%02x modeext=0x%02x w=%d h=%d dpitch=%d spitch=%d daddr=0x%08x saddr=0x%08x writemask=0x%02x\n", + blt_rop, + s->cirrus_blt_mode, + s->cirrus_blt_modeext, + s->cirrus_blt_width, + s->cirrus_blt_height, + s->cirrus_blt_dstpitch, + s->cirrus_blt_srcpitch, + s->cirrus_blt_dstaddr, + s->cirrus_blt_srcaddr, + s->vga.gr[0x2f]); +#endif + + switch (s->cirrus_blt_mode & CIRRUS_BLTMODE_PIXELWIDTHMASK) { + case CIRRUS_BLTMODE_PIXELWIDTH8: + s->cirrus_blt_pixelwidth = 1; + break; + case CIRRUS_BLTMODE_PIXELWIDTH16: + s->cirrus_blt_pixelwidth = 2; + break; + case CIRRUS_BLTMODE_PIXELWIDTH24: + s->cirrus_blt_pixelwidth = 3; + break; + case CIRRUS_BLTMODE_PIXELWIDTH32: + s->cirrus_blt_pixelwidth = 4; + break; + default: +#ifdef DEBUG_BITBLT + write_log("cirrus: bitblt - pixel width is unknown\n"); +#endif + goto bitblt_ignore; + } + s->cirrus_blt_mode &= ~CIRRUS_BLTMODE_PIXELWIDTHMASK; + + if ((s-> + cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSSRC | + CIRRUS_BLTMODE_MEMSYSDEST)) + == (CIRRUS_BLTMODE_MEMSYSSRC | CIRRUS_BLTMODE_MEMSYSDEST)) { +#ifdef DEBUG_BITBLT + write_log("cirrus: bitblt - memory-to-memory copy is requested\n"); +#endif + goto bitblt_ignore; + } + + if ((s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_SOLIDFILL) && + (s->cirrus_blt_mode & (CIRRUS_BLTMODE_MEMSYSDEST | + CIRRUS_BLTMODE_TRANSPARENTCOMP | + CIRRUS_BLTMODE_PATTERNCOPY | + CIRRUS_BLTMODE_COLOREXPAND)) == + (CIRRUS_BLTMODE_PATTERNCOPY | CIRRUS_BLTMODE_COLOREXPAND)) { + cirrus_bitblt_fgcol(s); + cirrus_bitblt_solidfill(s, blt_rop); + } else { + if ((s->cirrus_blt_mode & (CIRRUS_BLTMODE_COLOREXPAND | + CIRRUS_BLTMODE_PATTERNCOPY)) == + CIRRUS_BLTMODE_COLOREXPAND) { + + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) + cirrus_bitblt_bgcol(s); + else + cirrus_bitblt_fgcol(s); + s->cirrus_rop = cirrus_colorexpand_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } else { + cirrus_bitblt_fgcol(s); + cirrus_bitblt_bgcol(s); + s->cirrus_rop = cirrus_colorexpand[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } + } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_PATTERNCOPY) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_COLOREXPAND) { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) + cirrus_bitblt_bgcol(s); + else + cirrus_bitblt_fgcol(s); + s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } else { + cirrus_bitblt_fgcol(s); + cirrus_bitblt_bgcol(s); + s->cirrus_rop = cirrus_colorexpand_pattern[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } + } else { + s->cirrus_rop = cirrus_patternfill[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } + } else { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_TRANSPARENTCOMP) { + if (s->cirrus_blt_pixelwidth > 2) { + //write_log("src transparent without colorexpand must be 8bpp or 16bpp\n"); + goto bitblt_ignore; + } + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { + s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; + s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; + s->cirrus_rop = cirrus_bkwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } else { + s->cirrus_rop = cirrus_fwd_transp_rop[rop_to_index[blt_rop]][s->cirrus_blt_pixelwidth - 1]; + } + } else { + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_BACKWARDS) { + s->cirrus_blt_dstpitch = -s->cirrus_blt_dstpitch; + s->cirrus_blt_srcpitch = -s->cirrus_blt_srcpitch; + s->cirrus_rop = cirrus_bkwd_rop[rop_to_index[blt_rop]]; + } else { + s->cirrus_rop = cirrus_fwd_rop[rop_to_index[blt_rop]]; + } + } + } + // setup bitblt engine. + if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSSRC) { + if (!cirrus_bitblt_cputovideo(s)) + goto bitblt_ignore; + } else if (s->cirrus_blt_mode & CIRRUS_BLTMODE_MEMSYSDEST) { + if (!cirrus_bitblt_videotocpu(s)) + goto bitblt_ignore; + } else { + if (!cirrus_bitblt_videotovideo(s)) + goto bitblt_ignore; + } + } + return; + bitblt_ignore:; + cirrus_bitblt_reset(s); +} + +static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value) +{ + unsigned old_value; + + old_value = s->vga.gr[0x31]; + s->vga.gr[0x31] = reg_value; + + if (((old_value & CIRRUS_BLT_RESET) != 0) && + ((reg_value & CIRRUS_BLT_RESET) == 0)) { + cirrus_bitblt_reset(s); + } else if (((old_value & CIRRUS_BLT_START) == 0) && + ((reg_value & CIRRUS_BLT_START) != 0)) { + cirrus_bitblt_start(s); + } +} + + +/*************************************** + * + * basic parameters + * + ***************************************/ + +static void cirrus_get_offsets(VGACommonState *s1, + uint32_t *pline_offset, + uint32_t *pstart_addr, + uint32_t *pline_compare) +{ + CirrusVGAState * s = container_of(s1, CirrusVGAState, vga); + uint32_t start_addr, line_offset, line_compare; + + line_offset = s->vga.cr[0x13] + | ((s->vga.cr[0x1b] & 0x10) << 4); + line_offset <<= 3; + /* TW 5434/36 "32bit/Pixel Data at Pixel Rate". Offset doubled. */ + if (((s->vga.sr[0x07] >> 1) & 7) == 4) + line_offset <<= 1; + *pline_offset = line_offset; + + start_addr = (s->vga.cr[0x0c] << 8) + | s->vga.cr[0x0d] + | ((s->vga.cr[0x1b] & 0x01) << 16) + | ((s->vga.cr[0x1b] & 0x0c) << 15) + | ((s->vga.cr[0x1d] & 0x80) << 12); + *pstart_addr = start_addr; + + line_compare = s->vga.cr[0x18] | + ((s->vga.cr[0x07] & 0x10) << 4) | + ((s->vga.cr[0x09] & 0x40) << 3); + *pline_compare = line_compare; +} + +static uint32_t cirrus_get_bpp16_depth(CirrusVGAState * s) +{ + uint32_t ret = 16; + + /* TW */ + if ((s->cirrus_hidden_dac_data & 0x80) == 0) + return 8; + switch (s->cirrus_hidden_dac_data & 0xf) { + case 0: + ret = 15; + break; /* Sierra HiColor */ + case 1: + ret = 16; + break; /* XGA HiColor */ + default: +#ifdef DEBUG_CIRRUS + write_log("cirrus: invalid DAC value %x in 16bpp\n", + (s->cirrus_hidden_dac_data & 0xf)); +#endif + ret = 15; /* XXX */ + break; + } + return ret; +} + +static int cirrus_get_bpp(VGACommonState *s1) +{ + CirrusVGAState * s = container_of(s1, CirrusVGAState, vga); + uint32_t ret = 8; + + if ((s->vga.sr[0x07] & 0x01) != 0) { + /* Cirrus SVGA */ + switch (s->vga.sr[0x07] & CIRRUS_SR7_BPP_MASK) { + case CIRRUS_SR7_BPP_8: + ret = 8; + break; + case CIRRUS_SR7_BPP_16_DOUBLEVCLK: + ret = cirrus_get_bpp16_depth(s); + break; + case CIRRUS_SR7_BPP_24: + ret = 24; + break; + case CIRRUS_SR7_BPP_16: + ret = cirrus_get_bpp16_depth(s); + break; + case CIRRUS_SR7_BPP_32: + ret = 32; + break; + default: +#ifdef DEBUG_CIRRUS + write_log("cirrus: unknown bpp - sr7=%x\n", s->vga.sr[0x7]); +#endif + ret = 8; + break; + } + } else { + /* VGA */ + ret = 0; + } + + return ret; +} + +static void cirrus_get_resolution(VGACommonState *s, int *pwidth, int *pheight) +{ + CirrusVGAState * cs = container_of(s, CirrusVGAState, vga); + int width, height; + + width = (s->cr[0x01] + 1) * 8; + /* TW: if 16 bit mdoe but SR7 bit 7 == 0: 2x width and palette mode */ + if ((cs->cirrus_hidden_dac_data & 0x80) == 0) { + switch (s->sr[0x07] & CIRRUS_SR7_BPP_MASK) { + case CIRRUS_SR7_BPP_16_DOUBLEVCLK: + case CIRRUS_SR7_BPP_16: + width *= 2; + break; + } + } + + height = s->cr[0x12] | + ((s->cr[0x07] & 0x02) << 7) | + ((s->cr[0x07] & 0x40) << 3); + height = (height + 1); + /* interlace support */ + if (s->cr[0x1a] & 0x01) + height *= 2; + /* TW multiply vertical registers by two */ + if (s->cr[0x17] & 0x04) + height *= 2; + + *pwidth = width; + *pheight = height; +} + +/*************************************** + * + * bank memory + * + ***************************************/ + +static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index) +{ + unsigned offset; + unsigned limit; + + if ((s->vga.gr[0x0b] & 0x01) != 0) /* dual bank */ + offset = s->vga.gr[0x09 + bank_index]; + else /* single bank */ + offset = s->vga.gr[0x09]; + + if ((s->vga.gr[0x0b] & 0x20) != 0) + offset <<= 14; + else + offset <<= 12; + + if (s->real_vram_size <= offset) + limit = 0; + else + limit = s->real_vram_size - offset; + + if (((s->vga.gr[0x0b] & 0x01) == 0) && (bank_index != 0)) { + if (limit > 0x8000) { + offset += 0x8000; + limit -= 0x8000; + } else { + limit = 0; + } + } + + if (limit > 0) { + s->cirrus_bank_base[bank_index] = offset; + s->cirrus_bank_limit[bank_index] = limit; + } else { + s->cirrus_bank_base[bank_index] = 0; + s->cirrus_bank_limit[bank_index] = 0; + } +} + +/*************************************** + * + * I/O access between 0x3c4-0x3c5 + * + ***************************************/ + +static int cirrus_vga_read_sr(CirrusVGAState * s) +{ + switch (s->vga.sr_index) { + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + return s->vga.sr[s->vga.sr_index]; + case 0x06: // Unlock Cirrus extensions + return s->vga.sr[s->vga.sr_index]; + case 0x10: + case 0x30: + case 0x50: + case 0x70: // Graphics Cursor X + case 0x90: + case 0xb0: + case 0xd0: + case 0xf0: // Graphics Cursor X + return s->vga.sr[0x10]; + case 0x11: + case 0x31: + case 0x51: + case 0x71: // Graphics Cursor Y + case 0x91: + case 0xb1: + case 0xd1: + case 0xf1: // Graphics Cursor Y + return s->vga.sr[0x11]; + case 0x05: // ??? + case 0x07: // Extended Sequencer Mode + case 0x08: // EEPROM Control + case 0x09: // Scratch Register 0 + case 0x0a: // Scratch Register 1 + case 0x0b: // VCLK 0 + case 0x0c: // VCLK 1 + case 0x0d: // VCLK 2 + case 0x0e: // VCLK 3 + case 0x0f: // DRAM Control + case 0x12: // Graphics Cursor Attribute + case 0x13: // Graphics Cursor Pattern Address + case 0x14: // Scratch Register 2 + case 0x15: // Scratch Register 3 + case 0x16: // Performance Tuning Register + case 0x17: // Configuration Readback and Extended Control + case 0x18: // Signature Generator Control + case 0x19: // Signal Generator Result + case 0x1a: // Signal Generator Result + case 0x1b: // VCLK 0 Denominator & Post + case 0x1c: // VCLK 1 Denominator & Post + case 0x1d: // VCLK 2 Denominator & Post + case 0x1e: // VCLK 3 Denominator & Post + case 0x1f: // BIOS Write Enable and MCLK select +#ifdef DEBUG_CIRRUS + write_log("cirrus: handled inport sr_index %02x\n", s->vga.sr_index); +#endif + return s->vga.sr[s->vga.sr_index]; + default: +#ifdef DEBUG_CIRRUS + write_log("cirrus: inport sr_index %02x\n", s->vga.sr_index); +#endif + return 0xff; + break; + } +} + +static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) +{ + switch (s->vga.sr_index) { + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + s->vga.sr[s->vga.sr_index] = val & sr_mask[s->vga.sr_index]; + if (s->vga.sr_index == 1) + s->vga.update_retrace_info(&s->vga); + break; + case 0x06: // Unlock Cirrus extensions + val &= 0x17; + if (val == 0x12) { + s->vga.sr[s->vga.sr_index] = 0x12; + } else { + s->vga.sr[s->vga.sr_index] = 0x0f; + } + break; + case 0x10: + case 0x30: + case 0x50: + case 0x70: // Graphics Cursor X + case 0x90: + case 0xb0: + case 0xd0: + case 0xf0: // Graphics Cursor X + s->vga.sr[0x10] = val; + s->hw_cursor_x = (val << 3) | (s->vga.sr_index >> 5); + break; + case 0x11: + case 0x31: + case 0x51: + case 0x71: // Graphics Cursor Y + case 0x91: + case 0xb1: + case 0xd1: + case 0xf1: // Graphics Cursor Y + s->vga.sr[0x11] = val; + s->hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5); + break; + case 0x07: // Extended Sequencer Mode + cirrus_update_memory_access(s); + case 0x08: // EEPROM Control + case 0x09: // Scratch Register 0 + case 0x0a: // Scratch Register 1 + case 0x0b: // VCLK 0 + case 0x0c: // VCLK 1 + case 0x0d: // VCLK 2 + case 0x0e: // VCLK 3 + case 0x0f: // DRAM Control + case 0x12: // Graphics Cursor Attribute + case 0x13: // Graphics Cursor Pattern Address + case 0x14: // Scratch Register 2 + case 0x15: // Scratch Register 3 + case 0x16: // Performance Tuning Register + case 0x18: // Signature Generator Control + case 0x19: // Signature Generator Result + case 0x1a: // Signature Generator Result + case 0x1b: // VCLK 0 Denominator & Post + case 0x1c: // VCLK 1 Denominator & Post + case 0x1d: // VCLK 2 Denominator & Post + case 0x1e: // VCLK 3 Denominator & Post + case 0x1f: // BIOS Write Enable and MCLK select + s->vga.sr[s->vga.sr_index] = val; +#ifdef DEBUG_CIRRUS + write_log("cirrus: handled outport sr_index %02x, sr_value %02x\n", + s->vga.sr_index, val); +#endif + break; + case 0x17: // Configuration Readback and Extended Control + s->vga.sr[s->vga.sr_index] = (s->vga.sr[s->vga.sr_index] & 0x38) + | (val & 0xc7); + cirrus_update_memory_access(s); + break; + default: +#ifdef DEBUG_CIRRUS + write_log("cirrus: outport sr_index %02x, sr_value %02x\n", + s->vga.sr_index, val); +#endif + break; + } +} + +/*************************************** + * + * I/O access at 0x3c6 + * + ***************************************/ + +static int cirrus_read_hidden_dac(CirrusVGAState * s) +{ + if (++s->cirrus_hidden_dac_lockindex == 5) { + s->cirrus_hidden_dac_lockindex = 0; + return s->cirrus_hidden_dac_data; + } + return 0xff; +} + +static void cirrus_write_hidden_dac(CirrusVGAState * s, int reg_value) +{ + if (s->cirrus_hidden_dac_lockindex == 4) { + s->cirrus_hidden_dac_data = reg_value; + + s->vga.gr[5] |= 0x40; + + +#if defined(DEBUG_CIRRUS) + write_log("cirrus: outport hidden DAC, value %02x\n", reg_value); +#endif + } + s->cirrus_hidden_dac_lockindex = 0; +} + +/*************************************** + * + * I/O access at 0x3c9 + * + ***************************************/ + +static int cirrus_vga_read_palette(CirrusVGAState * s) +{ + int val; + + if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) { + val = s->cirrus_hidden_palette[(s->vga.dac_read_index & 0x0f) * 3 + + s->vga.dac_sub_index]; + } else { + val = s->vga.palette[s->vga.dac_read_index * 3 + s->vga.dac_sub_index]; + } + if (++s->vga.dac_sub_index == 3) { + s->vga.dac_sub_index = 0; + s->vga.dac_read_index++; + } + return val; +} + +static void cirrus_vga_write_palette(CirrusVGAState * s, int reg_value) +{ +// if (s->vga.dac_write_index < 8) +// write_log ("PAL: %d %d: %02X\n", s->vga.dac_write_index, s->vga.dac_sub_index, reg_value); + s->vga.dac_cache[s->vga.dac_sub_index] = reg_value; + if (++s->vga.dac_sub_index == 3) { + if ((s->vga.sr[0x12] & CIRRUS_CURSOR_HIDDENPEL)) { + memcpy(&s->cirrus_hidden_palette[(s->vga.dac_write_index & 0x0f) * 3], + s->vga.dac_cache, 3); + } else { + memcpy(&s->vga.palette[s->vga.dac_write_index * 3], s->vga.dac_cache, 3); + } + /* XXX update cursor */ + s->vga.dac_sub_index = 0; + s->vga.dac_write_index++; + } +} + +/*************************************** + * + * I/O access between 0x3ce-0x3cf + * + ***************************************/ + +static int cirrus_vga_read_gr(CirrusVGAState * s, unsigned reg_index) +{ + switch (reg_index) { + case 0x00: // Standard VGA, BGCOLOR 0x000000ff + return s->cirrus_shadow_gr0; + case 0x01: // Standard VGA, FGCOLOR 0x000000ff + return s->cirrus_shadow_gr1; + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + return s->vga.gr[s->vga.gr_index]; + case 0x05: // Standard VGA, Cirrus extended mode + default: + break; + } + + if (reg_index < 0x3a) { + return s->vga.gr[reg_index]; + } else { +#ifdef DEBUG_CIRRUS + write_log("cirrus: inport gr_index %02x\n", reg_index); +#endif + return 0xff; + } +} + +static void +cirrus_vga_write_gr(CirrusVGAState * s, unsigned reg_index, int reg_value) +{ +#if defined(DEBUG_BITBLT) && 0 + write_log("gr%02x: %02x\n", reg_index, reg_value); +#endif + switch (reg_index) { + case 0x00: // Standard VGA, BGCOLOR 0x000000ff + s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; + s->cirrus_shadow_gr0 = reg_value; + break; + case 0x01: // Standard VGA, FGCOLOR 0x000000ff + s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; + s->cirrus_shadow_gr1 = reg_value; + break; + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + s->vga.gr[reg_index] = reg_value & gr_mask[reg_index]; + break; + case 0x05: // Standard VGA, Cirrus extended mode + s->vga.gr[reg_index] = reg_value & 0x7f; + cirrus_update_memory_access(s); + break; + case 0x09: // bank offset #0 + case 0x0A: // bank offset #1 + s->vga.gr[reg_index] = reg_value; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); + cirrus_update_memory_access(s); + break; + case 0x0B: + s->vga.gr[reg_index] = reg_value; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); + cirrus_update_memory_access(s); + break; + case 0x10: // BGCOLOR 0x0000ff00 + case 0x11: // FGCOLOR 0x0000ff00 + case 0x12: // BGCOLOR 0x00ff0000 + case 0x13: // FGCOLOR 0x00ff0000 + case 0x14: // BGCOLOR 0xff000000 + case 0x15: // FGCOLOR 0xff000000 + case 0x20: // BLT WIDTH 0x0000ff + case 0x22: // BLT HEIGHT 0x0000ff + case 0x24: // BLT DEST PITCH 0x0000ff + case 0x26: // BLT SRC PITCH 0x0000ff + case 0x28: // BLT DEST ADDR 0x0000ff + case 0x29: // BLT DEST ADDR 0x00ff00 + case 0x2c: // BLT SRC ADDR 0x0000ff + case 0x2d: // BLT SRC ADDR 0x00ff00 + case 0x2f: // BLT WRITEMASK + case 0x30: // BLT MODE + case 0x32: // RASTER OP + case 0x33: // BLT MODEEXT + case 0x34: // BLT TRANSPARENT COLOR 0x00ff + case 0x35: // BLT TRANSPARENT COLOR 0xff00 + case 0x38: // BLT TRANSPARENT COLOR MASK 0x00ff + case 0x39: // BLT TRANSPARENT COLOR MASK 0xff00 + s->vga.gr[reg_index] = reg_value; + break; + case 0x21: // BLT WIDTH 0x001f00 + case 0x23: // BLT HEIGHT 0x001f00 + case 0x25: // BLT DEST PITCH 0x001f00 + case 0x27: // BLT SRC PITCH 0x001f00 + s->vga.gr[reg_index] = reg_value & 0x1f; + break; + case 0x2a: // BLT DEST ADDR 0x3f0000 + s->vga.gr[reg_index] = reg_value & 0x3f; + /* if auto start mode, starts bit blt now */ + if (s->vga.gr[0x31] & CIRRUS_BLT_AUTOSTART) { + cirrus_bitblt_start(s); + } + break; + case 0x2e: // BLT SRC ADDR 0x3f0000 + s->vga.gr[reg_index] = reg_value & 0x3f; + break; + case 0x31: // BLT STATUS/START + cirrus_write_bitblt(s, reg_value); + break; + default: +#ifdef DEBUG_CIRRUS + write_log("cirrus: outport gr_index %02x, gr_value %02x\n", reg_index, + reg_value); +#endif + break; + } +} + +/*************************************** + * + * I/O access between 0x3d4-0x3d5 + * + ***************************************/ + +static int cirrus_vga_read_cr(CirrusVGAState * s, unsigned reg_index) +{ + switch (reg_index) { + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x05: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + case 0x09: // Standard VGA + case 0x0a: // Standard VGA + case 0x0b: // Standard VGA + case 0x0c: // Standard VGA + case 0x0d: // Standard VGA + case 0x0e: // Standard VGA + case 0x0f: // Standard VGA + case 0x10: // Standard VGA + case 0x11: // Standard VGA + case 0x12: // Standard VGA + case 0x13: // Standard VGA + case 0x14: // Standard VGA + case 0x15: // Standard VGA + case 0x16: // Standard VGA + case 0x17: // Standard VGA + case 0x18: // Standard VGA + return s->vga.cr[s->vga.cr_index]; + case 0x24: // Attribute Controller Toggle Readback (R) + return (s->vga.ar_flip_flop << 7); + case 0x19: // Interlace End + case 0x1a: // Miscellaneous Control + case 0x1b: // Extended Display Control + case 0x1c: // Sync Adjust and Genlock + case 0x1d: // Overlay Extended Control + case 0x22: // Graphics Data Latches Readback (R) + case 0x25: // Part Status + case 0x27: // Part ID (R) + return s->vga.cr[s->vga.cr_index]; + case 0x26: // Attribute Controller Index Readback (R) + return s->vga.ar_index & 0x3f; + break; + default: +#ifdef DEBUG_CIRRUS + write_log("cirrus: inport cr_index %02x\n", reg_index); +#endif + return 0xff; + } +} + +static void cirrus_vga_write_cr(CirrusVGAState * s, int reg_value) +{ +#if 0 + if (s->vga.cr_index == 11) + write_log ("CR11 = %02X\n", reg_value & 0xff); +#endif + switch (s->vga.cr_index) { + case 0x00: // Standard VGA + case 0x01: // Standard VGA + case 0x02: // Standard VGA + case 0x03: // Standard VGA + case 0x04: // Standard VGA + case 0x05: // Standard VGA + case 0x06: // Standard VGA + case 0x07: // Standard VGA + case 0x08: // Standard VGA + case 0x09: // Standard VGA + case 0x0a: // Standard VGA + case 0x0b: // Standard VGA + case 0x0c: // Standard VGA + case 0x0d: // Standard VGA + case 0x0e: // Standard VGA + case 0x0f: // Standard VGA + case 0x10: // Standard VGA + case 0x11: // Standard VGA + case 0x12: // Standard VGA + case 0x13: // Standard VGA + case 0x14: // Standard VGA + case 0x15: // Standard VGA + case 0x16: // Standard VGA + case 0x17: // Standard VGA + case 0x18: // Standard VGA + /* handle CR0-7 protection */ + if ((s->vga.cr[0x11] & 0x80) && s->vga.cr_index <= 7) { + /* can always write bit 4 of CR7 */ + if (s->vga.cr_index == 7) + s->vga.cr[7] = (s->vga.cr[7] & ~0x10) | (reg_value & 0x10); + return; + } + s->vga.cr[s->vga.cr_index] = reg_value; + switch(s->vga.cr_index) { + case 0x00: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x11: + case 0x17: + s->vga.update_retrace_info(&s->vga); + break; + } + break; + case 0x19: // Interlace End + case 0x1a: // Miscellaneous Control + case 0x1b: // Extended Display Control + case 0x1c: // Sync Adjust and Genlock + case 0x1d: // Overlay Extended Control + s->vga.cr[s->vga.cr_index] = reg_value; +#ifdef DEBUG_CIRRUS + write_log("cirrus: handled outport cr_index %02x, cr_value %02x\n", + s->vga.cr_index, reg_value); +#endif + break; + case 0x22: // Graphics Data Latches Readback (R) + case 0x24: // Attribute Controller Toggle Readback (R) + case 0x26: // Attribute Controller Index Readback (R) + case 0x27: // Part ID (R) + break; + case 0x25: // Part Status + default: +#ifdef DEBUG_CIRRUS + write_log("cirrus: outport cr_index %02x, cr_value %02x\n", + s->vga.cr_index, reg_value); +#endif + break; + } +} + +/*************************************** + * + * memory-mapped I/O (bitblt) + * + ***************************************/ + +static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address) +{ + int value = 0xff; + + switch (address) { + case (CIRRUS_MMIO_BLTBGCOLOR + 0): + value = cirrus_vga_read_gr(s, 0x00); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 1): + value = cirrus_vga_read_gr(s, 0x10); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 2): + value = cirrus_vga_read_gr(s, 0x12); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 3): + value = cirrus_vga_read_gr(s, 0x14); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 0): + value = cirrus_vga_read_gr(s, 0x01); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 1): + value = cirrus_vga_read_gr(s, 0x11); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 2): + value = cirrus_vga_read_gr(s, 0x13); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 3): + value = cirrus_vga_read_gr(s, 0x15); + break; + case (CIRRUS_MMIO_BLTWIDTH + 0): + value = cirrus_vga_read_gr(s, 0x20); + break; + case (CIRRUS_MMIO_BLTWIDTH + 1): + value = cirrus_vga_read_gr(s, 0x21); + break; + case (CIRRUS_MMIO_BLTHEIGHT + 0): + value = cirrus_vga_read_gr(s, 0x22); + break; + case (CIRRUS_MMIO_BLTHEIGHT + 1): + value = cirrus_vga_read_gr(s, 0x23); + break; + case (CIRRUS_MMIO_BLTDESTPITCH + 0): + value = cirrus_vga_read_gr(s, 0x24); + break; + case (CIRRUS_MMIO_BLTDESTPITCH + 1): + value = cirrus_vga_read_gr(s, 0x25); + break; + case (CIRRUS_MMIO_BLTSRCPITCH + 0): + value = cirrus_vga_read_gr(s, 0x26); + break; + case (CIRRUS_MMIO_BLTSRCPITCH + 1): + value = cirrus_vga_read_gr(s, 0x27); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 0): + value = cirrus_vga_read_gr(s, 0x28); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 1): + value = cirrus_vga_read_gr(s, 0x29); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 2): + value = cirrus_vga_read_gr(s, 0x2a); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 0): + value = cirrus_vga_read_gr(s, 0x2c); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 1): + value = cirrus_vga_read_gr(s, 0x2d); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 2): + value = cirrus_vga_read_gr(s, 0x2e); + break; + case CIRRUS_MMIO_BLTWRITEMASK: + value = cirrus_vga_read_gr(s, 0x2f); + break; + case CIRRUS_MMIO_BLTMODE: + value = cirrus_vga_read_gr(s, 0x30); + break; + case CIRRUS_MMIO_BLTROP: + value = cirrus_vga_read_gr(s, 0x32); + break; + case CIRRUS_MMIO_BLTMODEEXT: + value = cirrus_vga_read_gr(s, 0x33); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0): + value = cirrus_vga_read_gr(s, 0x34); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1): + value = cirrus_vga_read_gr(s, 0x35); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0): + value = cirrus_vga_read_gr(s, 0x38); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1): + value = cirrus_vga_read_gr(s, 0x39); + break; + case CIRRUS_MMIO_BLTSTATUS: + value = cirrus_vga_read_gr(s, 0x31); + break; + default: +#ifdef DEBUG_CIRRUS + write_log("cirrus: mmio read - address 0x%04x\n", address); +#endif + break; + } + + return (uint8_t) value; +} + +static void cirrus_mmio_blt_write(CirrusVGAState * s, unsigned address, + uint8_t value) +{ + switch (address) { + case (CIRRUS_MMIO_BLTBGCOLOR + 0): + cirrus_vga_write_gr(s, 0x00, value); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 1): + cirrus_vga_write_gr(s, 0x10, value); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 2): + cirrus_vga_write_gr(s, 0x12, value); + break; + case (CIRRUS_MMIO_BLTBGCOLOR + 3): + cirrus_vga_write_gr(s, 0x14, value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 0): + cirrus_vga_write_gr(s, 0x01, value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 1): + cirrus_vga_write_gr(s, 0x11, value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 2): + cirrus_vga_write_gr(s, 0x13, value); + break; + case (CIRRUS_MMIO_BLTFGCOLOR + 3): + cirrus_vga_write_gr(s, 0x15, value); + break; + case (CIRRUS_MMIO_BLTWIDTH + 0): + cirrus_vga_write_gr(s, 0x20, value); + break; + case (CIRRUS_MMIO_BLTWIDTH + 1): + cirrus_vga_write_gr(s, 0x21, value); + break; + case (CIRRUS_MMIO_BLTHEIGHT + 0): + cirrus_vga_write_gr(s, 0x22, value); + break; + case (CIRRUS_MMIO_BLTHEIGHT + 1): + cirrus_vga_write_gr(s, 0x23, value); + break; + case (CIRRUS_MMIO_BLTDESTPITCH + 0): + cirrus_vga_write_gr(s, 0x24, value); + break; + case (CIRRUS_MMIO_BLTDESTPITCH + 1): + cirrus_vga_write_gr(s, 0x25, value); + break; + case (CIRRUS_MMIO_BLTSRCPITCH + 0): + cirrus_vga_write_gr(s, 0x26, value); + break; + case (CIRRUS_MMIO_BLTSRCPITCH + 1): + cirrus_vga_write_gr(s, 0x27, value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 0): + cirrus_vga_write_gr(s, 0x28, value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 1): + cirrus_vga_write_gr(s, 0x29, value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 2): + cirrus_vga_write_gr(s, 0x2a, value); + break; + case (CIRRUS_MMIO_BLTDESTADDR + 3): + /* ignored */ + break; + case (CIRRUS_MMIO_BLTSRCADDR + 0): + cirrus_vga_write_gr(s, 0x2c, value); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 1): + cirrus_vga_write_gr(s, 0x2d, value); + break; + case (CIRRUS_MMIO_BLTSRCADDR + 2): + cirrus_vga_write_gr(s, 0x2e, value); + break; + case CIRRUS_MMIO_BLTWRITEMASK: + cirrus_vga_write_gr(s, 0x2f, value); + break; + case CIRRUS_MMIO_BLTMODE: + cirrus_vga_write_gr(s, 0x30, value); + break; + case CIRRUS_MMIO_BLTROP: + cirrus_vga_write_gr(s, 0x32, value); + break; + case CIRRUS_MMIO_BLTMODEEXT: + cirrus_vga_write_gr(s, 0x33, value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0): + cirrus_vga_write_gr(s, 0x34, value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1): + cirrus_vga_write_gr(s, 0x35, value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0): + cirrus_vga_write_gr(s, 0x38, value); + break; + case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1): + cirrus_vga_write_gr(s, 0x39, value); + break; + case CIRRUS_MMIO_BLTSTATUS: + cirrus_vga_write_gr(s, 0x31, value); + break; + default: +#ifdef DEBUG_CIRRUS + write_log("cirrus: mmio write - addr 0x%04x val 0x%02x (ignored)\n", + address, value); +#endif + break; + } +} + +/*************************************** + * + * write mode 4/5 + * + ***************************************/ + +static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s, + unsigned mode, + unsigned offset, + uint32_t mem_value) +{ + int x; + unsigned val = mem_value; + uint8_t *dst; + + dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask); + for (x = 0; x < 8; x++) { + if (val & 0x80) { + *dst = s->cirrus_shadow_gr1; + } else if (mode == 5) { + *dst = s->cirrus_shadow_gr0; + } + val <<= 1; + dst++; + } + memory_region_set_dirty(&s->vga.vram, offset, 8); +} + +static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s, + unsigned mode, + unsigned offset, + uint32_t mem_value) +{ + int x; + unsigned val = mem_value; + uint8_t *dst; + + dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask); + for (x = 0; x < 8; x++) { + if (val & 0x80) { + *dst = s->cirrus_shadow_gr1; + *(dst + 1) = s->vga.gr[0x11]; + } else if (mode == 5) { + *dst = s->cirrus_shadow_gr0; + *(dst + 1) = s->vga.gr[0x10]; + } + val <<= 1; + dst += 2; + } + memory_region_set_dirty(&s->vga.vram, offset, 16); +} + +/*************************************** + * + * memory access between 0xa0000-0xbffff + * + ***************************************/ + +static uint64_t cirrus_vga_mem_read(void *opaque, + hwaddr addr, + uint32_t size) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + unsigned bank_index; + unsigned bank_offset; + uint32_t val; + + if ((s->vga.sr[0x07] & 0x01) == 0) { + return vga_mem_readb(&s->vga, addr); + } + + if (addr < 0x10000) { + /* XXX handle bitblt */ + /* video memory */ + bank_index = addr >> 15; + bank_offset = addr & 0x7fff; + if (bank_offset < s->cirrus_bank_limit[bank_index]) { + bank_offset += s->cirrus_bank_base[bank_index]; + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + bank_offset <<= 4; + } else if (s->vga.gr[0x0B] & 0x02) { + bank_offset <<= 3; + } + bank_offset &= s->cirrus_addr_mask; + val = *(s->vga.vram_ptr + bank_offset); + } else + val = 0xff; + } else if (addr >= 0x18000 && addr < 0x18100) { + /* memory-mapped I/O */ + val = 0xff; + if ((s->vga.sr[0x17] & 0x44) == 0x04) { + val = cirrus_mmio_blt_read(s, addr & 0xff); + } + } else { + val = 0xff; +#ifdef DEBUG_CIRRUS + write_log("cirrus: mem_readb " TARGET_FMT_plx "\n", addr); +#endif + } + return val; +} + +static void cirrus_vga_mem_write(void *opaque, + hwaddr addr, + uint64_t mem_value, + uint32_t size) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + unsigned bank_index; + unsigned bank_offset; + unsigned mode; + + if ((s->vga.sr[0x07] & 0x01) == 0) { + vga_mem_writeb(&s->vga, addr, mem_value); + return; + } + + if (addr < 0x10000) { + if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + /* bitblt */ + *s->cirrus_srcptr++ = (uint8_t) mem_value; + if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { + cirrus_bitblt_cputovideo_next(s); + } + } else { + /* video memory */ + bank_index = addr >> 15; + bank_offset = addr & 0x7fff; + if (bank_offset < s->cirrus_bank_limit[bank_index]) { + bank_offset += s->cirrus_bank_base[bank_index]; + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + bank_offset <<= 4; + } else if (s->vga.gr[0x0B] & 0x02) { + bank_offset <<= 3; + } + bank_offset &= s->cirrus_addr_mask; + mode = s->vga.gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { + *(s->vga.vram_ptr + bank_offset) = mem_value; + memory_region_set_dirty(&s->vga.vram, bank_offset, + sizeof(mem_value)); + } else { + if ((s->vga.gr[0x0B] & 0x14) != 0x14) { + cirrus_mem_writeb_mode4and5_8bpp(s, mode, + bank_offset, + mem_value); + } else { + cirrus_mem_writeb_mode4and5_16bpp(s, mode, + bank_offset, + mem_value); + } + } + } + } + } else if (addr >= 0x18000 && addr < 0x18100) { + /* memory-mapped I/O */ + if ((s->vga.sr[0x17] & 0x44) == 0x04) { + cirrus_mmio_blt_write(s, addr & 0xff, mem_value); + } + } else { +#ifdef DEBUG_CIRRUS + write_log("cirrus: mem_writeb " TARGET_FMT_plx " value %02x\n", addr, + mem_value); +#endif + } +} + +static const MemoryRegionOps cirrus_vga_mem_ops = { + cirrus_vga_mem_read, + cirrus_vga_mem_write, + DEVICE_LITTLE_ENDIAN, + 1, + 1, +}; + +/*************************************** + * + * hardware cursor + * + ***************************************/ + +STATIC_INLINE void invalidate_cursor1(CirrusVGAState *s) +{ + if (s->last_hw_cursor_size) { + vga_invalidate_scanlines(&s->vga, + s->last_hw_cursor_y + s->last_hw_cursor_y_start, + s->last_hw_cursor_y + s->last_hw_cursor_y_end); + } +} + +STATIC_INLINE void cirrus_cursor_compute_yrange(CirrusVGAState *s) +{ + const uint8_t *src; + uint32_t content; + int y, y_min, y_max; + + src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024; + if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { + src += (s->vga.sr[0x13] & 0x3c) * 256; + y_min = 64; + y_max = -1; + for(y = 0; y < 64; y++) { + content = ((uint32_t *)src)[0] | + ((uint32_t *)src)[1] | + ((uint32_t *)src)[2] | + ((uint32_t *)src)[3]; + if (content) { + if (y < y_min) + y_min = y; + if (y > y_max) + y_max = y; + } + src += 16; + } + } else { + src += (s->vga.sr[0x13] & 0x3f) * 256; + y_min = 32; + y_max = -1; + for(y = 0; y < 32; y++) { + content = ((uint32_t *)src)[0] | + ((uint32_t *)(src + 128))[0]; + if (content) { + if (y < y_min) + y_min = y; + if (y > y_max) + y_max = y; + } + src += 4; + } + } + if (y_min > y_max) { + s->last_hw_cursor_y_start = 0; + s->last_hw_cursor_y_end = 0; + } else { + s->last_hw_cursor_y_start = y_min; + s->last_hw_cursor_y_end = y_max + 1; + } +} + +/* NOTE: we do not currently handle the cursor bitmap change, so we + update the cursor only if it moves. */ +static void cirrus_cursor_invalidate(VGACommonState *s1) +{ + CirrusVGAState *s = container_of(s1, CirrusVGAState, vga); + int size; + + if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW)) { + size = 0; + } else { + if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) + size = 64; + else + size = 32; + } + /* invalidate last cursor and new cursor if any change */ + if (s->last_hw_cursor_size != size || + s->last_hw_cursor_x != s->hw_cursor_x || + s->last_hw_cursor_y != s->hw_cursor_y) { + + invalidate_cursor1(s); + + s->last_hw_cursor_size = size; + s->last_hw_cursor_x = s->hw_cursor_x; + s->last_hw_cursor_y = s->hw_cursor_y; + /* compute the real cursor min and max y */ + cirrus_cursor_compute_yrange(s); + invalidate_cursor1(s); + } +} + +#define DEPTH 8 +#include "cirrus_vga_template.h" + +#define DEPTH 16 +#include "cirrus_vga_template.h" + +#define DEPTH 32 +#include "cirrus_vga_template.h" + +static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y) +{ + CirrusVGAState *s = container_of(s1, CirrusVGAState, vga); + DisplaySurface *surface = qemu_console_surface(s->vga.con); + int w, h, bpp, x1, x2, poffset; + unsigned int color0, color1; + const uint8_t *palette, *src; + uint32_t content; + + if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW)) + return; + /* fast test to see if the cursor intersects with the scan line */ + if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { + h = 64; + } else { + h = 32; + } + if (scr_y < s->hw_cursor_y || + scr_y >= (s->hw_cursor_y + h)) + return; + + src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024; + if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) { + src += (s->vga.sr[0x13] & 0x3c) * 256; + src += (scr_y - s->hw_cursor_y) * 16; + poffset = 8; + content = ((uint32_t *)src)[0] | + ((uint32_t *)src)[1] | + ((uint32_t *)src)[2] | + ((uint32_t *)src)[3]; + } else { + src += (s->vga.sr[0x13] & 0x3f) * 256; + src += (scr_y - s->hw_cursor_y) * 4; + poffset = 128; + content = ((uint32_t *)src)[0] | + ((uint32_t *)(src + 128))[0]; + } + /* if nothing to draw, no need to continue */ + if (!content) + return; + w = h; + + x1 = s->hw_cursor_x; + if (x1 >= s->vga.last_scr_width) + return; + x2 = s->hw_cursor_x + w; + if (x2 > s->vga.last_scr_width) + x2 = s->vga.last_scr_width; + w = x2 - x1; + palette = s->cirrus_hidden_palette; + color0 = s->vga.rgb_to_pixel(c6_to_8(palette[0x0 * 3]), + c6_to_8(palette[0x0 * 3 + 1]), + c6_to_8(palette[0x0 * 3 + 2])); + color1 = s->vga.rgb_to_pixel(c6_to_8(palette[0xf * 3]), + c6_to_8(palette[0xf * 3 + 1]), + c6_to_8(palette[0xf * 3 + 2])); + bpp = surface_bytes_per_pixel(surface); + d1 += x1 * bpp; + switch (surface_bits_per_pixel(surface)) { + default: + break; + case 8: + vga_draw_cursor_line_8(d1, src, poffset, w, color0, color1, 0xff); + break; + case 15: + vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0x7fff); + break; + case 16: + vga_draw_cursor_line_16(d1, src, poffset, w, color0, color1, 0xffff); + break; + case 32: + vga_draw_cursor_line_32(d1, src, poffset, w, color0, color1, 0xffffff); + break; + } +} + +/*************************************** + * + * LFB memory access + * + ***************************************/ + +static uint64_t cirrus_linear_read(void *opaque, hwaddr addr, + unsigned size) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + uint32_t ret; + + addr &= s->cirrus_addr_mask; + + if (((s->vga.sr[0x17] & 0x44) == 0x44) && + ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { + /* memory-mapped I/O */ + ret = cirrus_mmio_blt_read(s, addr & 0xff); + } else if (0) { + /* XXX handle bitblt */ + ret = 0xff; + } else { + /* video memory */ + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + addr <<= 4; + } else if (s->vga.gr[0x0B] & 0x02) { + addr <<= 3; + } + addr &= s->cirrus_addr_mask; + ret = *(s->vga.vram_ptr + addr); + } + + return ret; +} + +static void cirrus_linear_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + unsigned mode; + + addr &= s->cirrus_addr_mask; + + if (((s->vga.sr[0x17] & 0x44) == 0x44) && + ((addr & s->linear_mmio_mask) == s->linear_mmio_mask)) { + /* memory-mapped I/O */ + cirrus_mmio_blt_write(s, addr & 0xff, val); + } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + /* bitblt */ + *s->cirrus_srcptr++ = (uint8_t) val; + if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { + cirrus_bitblt_cputovideo_next(s); + } + } else { + /* video memory */ + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + addr <<= 4; + } else if (s->vga.gr[0x0B] & 0x02) { + addr <<= 3; + } + addr &= s->cirrus_addr_mask; + + mode = s->vga.gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { + *(s->vga.vram_ptr + addr) = (uint8_t) val; + memory_region_set_dirty(&s->vga.vram, addr, 1); + } else { + if ((s->vga.gr[0x0B] & 0x14) != 0x14) { + cirrus_mem_writeb_mode4and5_8bpp(s, mode, addr, val); + } else { + cirrus_mem_writeb_mode4and5_16bpp(s, mode, addr, val); + } + } + } +} + +/*************************************** + * + * system to screen memory access + * + ***************************************/ + + +static uint64_t cirrus_linear_bitblt_read(void *opaque, + hwaddr addr, + unsigned size) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + uint32_t ret; + + /* XXX handle bitblt */ + (void)s; + ret = 0xff; + return ret; +} + +static void cirrus_linear_bitblt_write(void *opaque, + hwaddr addr, + uint64_t val, + unsigned size) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + + if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + /* bitblt */ + *s->cirrus_srcptr++ = (uint8_t) val; + if (s->cirrus_srcptr >= s->cirrus_srcptr_end) { + cirrus_bitblt_cputovideo_next(s); + } + } +} + +static const MemoryRegionOps cirrus_linear_bitblt_io_ops = { + cirrus_linear_bitblt_read, + cirrus_linear_bitblt_write, + DEVICE_LITTLE_ENDIAN, + 1, + 1, +}; + +static void map_linear_vram_bank(CirrusVGAState *s, unsigned bank) +{ + MemoryRegion *mr = &s->cirrus_bank[bank]; + bool enabled = !(s->cirrus_srcptr != s->cirrus_srcptr_end) + && !((s->vga.sr[0x07] & 0x01) == 0) + && !((s->vga.gr[0x0B] & 0x14) == 0x14) + && !(s->vga.gr[0x0B] & 0x02); + + memory_region_set_enabled(mr, enabled); + memory_region_set_alias_offset(mr, s->cirrus_bank_base[bank]); +} + +static void map_linear_vram(CirrusVGAState *s) +{ + if (s->bustype == CIRRUS_BUSTYPE_PCI && !s->linear_vram) { + s->linear_vram = true; + memory_region_add_subregion_overlap(&s->pci_bar, 0, &s->vga.vram, 1); + } + map_linear_vram_bank(s, 0); + map_linear_vram_bank(s, 1); +} + +static void unmap_linear_vram(CirrusVGAState *s) +{ + if (s->bustype == CIRRUS_BUSTYPE_PCI && s->linear_vram) { + s->linear_vram = false; + memory_region_del_subregion(&s->pci_bar, &s->vga.vram); + } + memory_region_set_enabled(&s->cirrus_bank[0], false); + memory_region_set_enabled(&s->cirrus_bank[1], false); +} + +/* Compute the memory access functions */ +static void cirrus_update_memory_access(CirrusVGAState *s) +{ + unsigned mode; + + memory_region_transaction_begin(); + if ((s->vga.sr[0x17] & 0x44) == 0x44) { + goto generic_io; + } else if (s->cirrus_srcptr != s->cirrus_srcptr_end) { + goto generic_io; + } else { + if ((s->vga.gr[0x0B] & 0x14) == 0x14) { + goto generic_io; + } else if (s->vga.gr[0x0B] & 0x02) { + goto generic_io; + } + + mode = s->vga.gr[0x05] & 0x7; + if (mode < 4 || mode > 5 || ((s->vga.gr[0x0B] & 0x4) == 0)) { + map_linear_vram(s); + } else { + generic_io: + unmap_linear_vram(s); + } + } + memory_region_transaction_commit(); +} + + +/* I/O ports */ + +static uint64_t cirrus_vga_ioport_read(void *opaque, hwaddr addr, + unsigned size) +{ + CirrusVGAState *c = (CirrusVGAState*)opaque; + VGACommonState *s = &c->vga; + int val, index; + + qemu_flush_coalesced_mmio_buffer(); + addr += 0x3b0; + + if (vga_ioport_invalid(s, addr)) { + val = 0xff; + } else { + switch (addr) { + case 0x3c0: + if (s->ar_flip_flop == 0) { + val = s->ar_index; + } else { + val = 0; + } + break; + case 0x3c1: + index = s->ar_index & 0x1f; + if (index < 21) + val = s->ar[index]; + else + val = 0; + break; + case 0x3c2: + val = s->st00; + break; + case 0x3c4: + val = s->sr_index; + break; + case 0x3c5: + val = cirrus_vga_read_sr(c); + break; +#ifdef DEBUG_VGA_REG + write_log("vga: read SR%x = 0x%02x\n", s->sr_index, val); +#endif + break; + case 0x3c6: + val = cirrus_read_hidden_dac(c); + break; + case 0x3c7: + val = s->dac_state; + break; + case 0x3c8: + val = s->dac_write_index; + c->cirrus_hidden_dac_lockindex = 0; + break; + case 0x3c9: + val = cirrus_vga_read_palette(c); + break; + case 0x3ca: + val = s->fcr; + break; + case 0x3cc: + val = s->msr; + break; + case 0x3ce: + val = s->gr_index; + break; + case 0x3cf: + val = cirrus_vga_read_gr(c, s->gr_index); +#ifdef DEBUG_VGA_REG + write_log("vga: read GR%x = 0x%02x\n", s->gr_index, val); +#endif + break; + case 0x3b4: + case 0x3d4: + val = s->cr_index; + break; + case 0x3b5: + case 0x3d5: + val = cirrus_vga_read_cr(c, s->cr_index); +#ifdef DEBUG_VGA_REG + write_log("vga: read CR%x = 0x%02x\n", s->cr_index, val); +#endif + break; + case 0x3ba: + case 0x3da: + /* just toggle to fool polling */ + val = s->st01 = s->retrace(s); + s->ar_flip_flop = 0; + break; + default: + val = 0x00; + break; + } + } +#if defined(DEBUG_VGA) + write_log("VGA: read addr=0x%04x data=0x%02x\n", addr, val); +#endif + return val; +} + +static void cirrus_vga_ioport_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + CirrusVGAState *c = (CirrusVGAState*)opaque; + VGACommonState *s = &c->vga; + int index; + + qemu_flush_coalesced_mmio_buffer(); + addr += 0x3b0; + + /* check port range access depending on color/monochrome mode */ + if (vga_ioport_invalid(s, addr)) { + return; + } +#ifdef DEBUG_VGA + write_log("VGA: write addr=0x%04x data=0x%02x\n", addr, val); +#endif + + switch (addr) { + case 0x3c0: + if (s->ar_flip_flop == 0) { + val &= 0x3f; + s->ar_index = val; + } else { + index = s->ar_index & 0x1f; + switch (index) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + s->ar[index] = val & 0x3f; + break; + case 0x10: + s->ar[index] = val & ~0x10; + break; + case 0x11: + s->ar[index] = val; + break; + case 0x12: + s->ar[index] = val & ~0xc0; + break; + case 0x13: + s->ar[index] = val & ~0xf0; + break; + case 0x14: + s->ar[index] = val & ~0xf0; + break; + default: + break; + } + } + s->ar_flip_flop ^= 1; + break; + case 0x3c2: + s->msr = val & ~0x10; + s->update_retrace_info(s); + break; + case 0x3c4: + s->sr_index = val; + break; + case 0x3c5: +#ifdef DEBUG_VGA_REG + write_log("vga: write SR%x = 0x%02x\n", s->sr_index, val); +#endif + cirrus_vga_write_sr(c, val); + break; + break; + case 0x3c6: + cirrus_write_hidden_dac(c, val); + break; + case 0x3c7: + s->dac_read_index = val; + s->dac_sub_index = 0; + s->dac_state = 3; + break; + case 0x3c8: + s->dac_write_index = val; + s->dac_sub_index = 0; + s->dac_state = 0; + break; + case 0x3c9: + cirrus_vga_write_palette(c, val); + break; + case 0x3ce: + s->gr_index = val; + break; + case 0x3cf: +#ifdef DEBUG_VGA_REG + write_log("vga: write GR%x = 0x%02x\n", s->gr_index, val); +#endif + cirrus_vga_write_gr(c, s->gr_index, val); + break; + case 0x3b4: + case 0x3d4: + s->cr_index = val; + break; + case 0x3b5: + case 0x3d5: +#ifdef DEBUG_VGA_REG + write_log("vga: write CR%x = 0x%02x\n", s->cr_index, val); +#endif + cirrus_vga_write_cr(c, val); + break; + case 0x3ba: + case 0x3da: + s->fcr = val & 0x10; + break; + } +} + +/*************************************** + * + * memory-mapped I/O access + * + ***************************************/ + +static uint64_t cirrus_mmio_read(void *opaque, hwaddr addr, + unsigned size) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + + if (addr >= 0x100) { + return cirrus_mmio_blt_read(s, addr - 0x100); + } else { + return cirrus_vga_ioport_read(s, addr + 0x10, size); + } +} + +static void cirrus_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + + if (addr >= 0x100) { + cirrus_mmio_blt_write(s, addr - 0x100, val); + } else { + cirrus_vga_ioport_write(s, addr + 0x10, val, size); + } +} + +static MemoryRegionOps cirrus_mmio_io_ops = { + cirrus_mmio_read, + cirrus_mmio_write, + DEVICE_LITTLE_ENDIAN, + 1, + 1, +}; + +/* load/save state */ + +static int cirrus_post_load(void *opaque, int version_id) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + + s->vga.gr[0x00] = s->cirrus_shadow_gr0 & 0x0f; + s->vga.gr[0x01] = s->cirrus_shadow_gr1 & 0x0f; + + cirrus_update_memory_access(s); + /* force refresh */ + s->vga.graphic_mode = -1; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); + return 0; +} + +#if 0 +static const VMStateDescription vmstate_cirrus_vga = { + .name = "cirrus_vga", + .version_id = 2, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .post_load = cirrus_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT32(vga.latch, CirrusVGAState), + VMSTATE_UINT8(vga.sr_index, CirrusVGAState), + VMSTATE_BUFFER(vga.sr, CirrusVGAState), + VMSTATE_UINT8(vga.gr_index, CirrusVGAState), + VMSTATE_UINT8(cirrus_shadow_gr0, CirrusVGAState), + VMSTATE_UINT8(cirrus_shadow_gr1, CirrusVGAState), + VMSTATE_BUFFER_START_MIDDLE(vga.gr, CirrusVGAState, 2), + VMSTATE_UINT8(vga.ar_index, CirrusVGAState), + VMSTATE_BUFFER(vga.ar, CirrusVGAState), + VMSTATE_INT32(vga.ar_flip_flop, CirrusVGAState), + VMSTATE_UINT8(vga.cr_index, CirrusVGAState), + VMSTATE_BUFFER(vga.cr, CirrusVGAState), + VMSTATE_UINT8(vga.msr, CirrusVGAState), + VMSTATE_UINT8(vga.fcr, CirrusVGAState), + VMSTATE_UINT8(vga.st00, CirrusVGAState), + VMSTATE_UINT8(vga.st01, CirrusVGAState), + VMSTATE_UINT8(vga.dac_state, CirrusVGAState), + VMSTATE_UINT8(vga.dac_sub_index, CirrusVGAState), + VMSTATE_UINT8(vga.dac_read_index, CirrusVGAState), + VMSTATE_UINT8(vga.dac_write_index, CirrusVGAState), + VMSTATE_BUFFER(vga.dac_cache, CirrusVGAState), + VMSTATE_BUFFER(vga.palette, CirrusVGAState), + VMSTATE_INT32(vga.bank_offset, CirrusVGAState), + VMSTATE_UINT8(cirrus_hidden_dac_lockindex, CirrusVGAState), + VMSTATE_UINT8(cirrus_hidden_dac_data, CirrusVGAState), + VMSTATE_UINT32(hw_cursor_x, CirrusVGAState), + VMSTATE_UINT32(hw_cursor_y, CirrusVGAState), + /* XXX: we do not save the bitblt state - we assume we do not save + the state when the blitter is active */ + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_pci_cirrus_vga = { + .name = "cirrus_vga", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .fields = (VMStateField []) { + VMSTATE_PCI_DEVICE(dev, PCICirrusVGAState), + VMSTATE_STRUCT(cirrus_vga, PCICirrusVGAState, 0, + vmstate_cirrus_vga, CirrusVGAState), + VMSTATE_END_OF_LIST() + } +}; +#endif + +/*************************************** + * + * initialize + * + ***************************************/ + +static void cirrus_reset(void *opaque) +{ + CirrusVGAState *s = (CirrusVGAState*)opaque; + + vga_common_reset(&s->vga); + unmap_linear_vram(s); + s->vga.sr[0x06] = 0x0f; + if (s->device_id >= CIRRUS_ID_CLGD5434) { // TW + /* 4MB 64 bit memory config, always PCI */ + s->vga.sr[0x1F] = 0x2d; // MemClock + s->vga.gr[0x18] = 0x0f; // fastest memory configuration + s->vga.sr[0x0f] = 0x98; + s->vga.sr[0x17] = 0x20; + s->vga.sr[0x15] = 0x04; /* memory size, 3=2MB, 4=4MB */ + } else { + s->vga.sr[0x1F] = 0x22; // MemClock + s->vga.sr[0x0F] = CIRRUS_MEMSIZE_2M; + s->vga.sr[0x17] = s->bustype; + s->vga.sr[0x15] = 0x03; /* memory size, 3=2MB, 4=4MB */ + } + s->vga.cr[0x27] = s->device_id; + + s->cirrus_hidden_dac_lockindex = 5; + s->cirrus_hidden_dac_data = 0; +} + +static const MemoryRegionOps cirrus_linear_io_ops = { + cirrus_linear_read, + cirrus_linear_write, + DEVICE_LITTLE_ENDIAN, + 1, + 1, +}; + +static const MemoryRegionOps cirrus_vga_io_ops = { + cirrus_vga_ioport_read, + cirrus_vga_ioport_write, + DEVICE_LITTLE_ENDIAN, + 1, + 1, +}; + +void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci, + MemoryRegion *system_memory, + MemoryRegion *system_io) +{ + int i; + static int inited; + + if (!inited) { + inited = 1; + for(i = 0;i < 256; i++) + rop_to_index[i] = CIRRUS_ROP_NOP_INDEX; /* nop rop */ + rop_to_index[CIRRUS_ROP_0] = 0; + rop_to_index[CIRRUS_ROP_SRC_AND_DST] = 1; + rop_to_index[CIRRUS_ROP_NOP] = 2; + rop_to_index[CIRRUS_ROP_SRC_AND_NOTDST] = 3; + rop_to_index[CIRRUS_ROP_NOTDST] = 4; + rop_to_index[CIRRUS_ROP_SRC] = 5; + rop_to_index[CIRRUS_ROP_1] = 6; + rop_to_index[CIRRUS_ROP_NOTSRC_AND_DST] = 7; + rop_to_index[CIRRUS_ROP_SRC_XOR_DST] = 8; + rop_to_index[CIRRUS_ROP_SRC_OR_DST] = 9; + rop_to_index[CIRRUS_ROP_NOTSRC_OR_NOTDST] = 10; + rop_to_index[CIRRUS_ROP_SRC_NOTXOR_DST] = 11; + rop_to_index[CIRRUS_ROP_SRC_OR_NOTDST] = 12; + rop_to_index[CIRRUS_ROP_NOTSRC] = 13; + rop_to_index[CIRRUS_ROP_NOTSRC_OR_DST] = 14; + rop_to_index[CIRRUS_ROP_NOTSRC_AND_NOTDST] = 15; + s->device_id = device_id; + if (is_pci) + s->bustype = CIRRUS_BUSTYPE_PCI; + else + s->bustype = CIRRUS_BUSTYPE_ISA; + } + + /* Register ioport 0x3b0 - 0x3df */ + memory_region_init_io(&s->cirrus_vga_io, &cirrus_vga_io_ops, s, + "cirrus-io", 0x30); + memory_region_add_subregion(system_io, 0x3b0, &s->cirrus_vga_io); + + memory_region_init(&s->low_mem_container, + "cirrus-lowmem-container", + 0x20000); + + memory_region_init_io(&s->low_mem, &cirrus_vga_mem_ops, s, + "cirrus-low-memory", 0x20000); + memory_region_add_subregion(&s->low_mem_container, 0, &s->low_mem); + for (i = 0; i < 2; ++i) { + static const char *names[] = { "vga.bank0", "vga.bank1" }; + MemoryRegion *bank = &s->cirrus_bank[i]; + memory_region_init_alias(bank, names[i], &s->vga.vram, 0, 0x8000); + memory_region_set_enabled(bank, false); + memory_region_add_subregion_overlap(&s->low_mem_container, i * 0x8000, + bank, 1); + } + memory_region_add_subregion_overlap(system_memory, + isa_mem_base + 0x000a0000, + &s->low_mem_container, + 1); + memory_region_set_coalescing(&s->low_mem); + + /* I/O handler for LFB */ + memory_region_init_io(&s->cirrus_linear_io, &cirrus_linear_io_ops, s, + "cirrus-linear-io", s->vga.vram_size_mb + * 1024 * 1024); + memory_region_set_flush_coalesced(&s->cirrus_linear_io); + + /* I/O handler for LFB */ + memory_region_init_io(&s->cirrus_linear_bitblt_io, + &cirrus_linear_bitblt_io_ops, + s, + "cirrus-bitblt-mmio", + 0x400000); + memory_region_set_flush_coalesced(&s->cirrus_linear_bitblt_io); + + /* I/O handler for memory-mapped I/O */ + memory_region_init_io(&s->cirrus_mmio_io, &cirrus_mmio_io_ops, s, + "cirrus-mmio", CIRRUS_PNPMMIO_SIZE); + memory_region_set_flush_coalesced(&s->cirrus_mmio_io); + + // TW: CIRRUS_ID_CLGD5434 and newer has 4M support + s->real_vram_size = + (s->device_id == CIRRUS_ID_CLGD5434) ? 4096 * 1024 : 2048 * 1024; + + /* XXX: s->vga.vram_size must be a power of two */ + s->cirrus_addr_mask = s->real_vram_size - 1; + s->linear_mmio_mask = s->real_vram_size - 256; + + s->vga.get_bpp = cirrus_get_bpp; + s->vga.get_offsets = cirrus_get_offsets; + s->vga.get_resolution = cirrus_get_resolution; + s->vga.cursor_invalidate = cirrus_cursor_invalidate; + s->vga.cursor_draw_line = cirrus_cursor_draw_line; + qemu_register_reset(cirrus_reset, s); +} + +#if 0 +/*************************************** + * + * ISA bus support + * + ***************************************/ + +static int vga_initfn(ISADevice *dev) +{ + ISACirrusVGAState *d = ISA_CIRRUS_VGA(dev); + VGACommonState *s = &d->cirrus_vga.vga; + + vga_common_init(s); + cirrus_init_common(&d->cirrus_vga, CIRRUS_ID_CLGD5430, 0, + isa_address_space(dev), isa_address_space_io(dev)); + s->con = graphic_console_init(DEVICE(dev), s->hw_ops, s); + rom_add_vga(VGABIOS_CIRRUS_FILENAME); + /* XXX ISA-LFB support */ + /* FIXME not qdev yet */ + return 0; +} + +static Property isa_cirrus_vga_properties[] = { + DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, + cirrus_vga.vga.vram_size_mb, 8), + DEFINE_PROP_END_OF_LIST(), +}; + +static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) +{ + ISADeviceClass *k = ISA_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_cirrus_vga; + k->init = vga_initfn; + dc->props = isa_cirrus_vga_properties; +} + +static const TypeInfo isa_cirrus_vga_info = { + .name = TYPE_ISA_CIRRUS_VGA, + .parent = TYPE_ISA_DEVICE,mun + .instance_size = sizeof(ISACirrusVGAState), + .class_init = isa_cirrus_vga_class_init, +}; + +/*************************************** + * + * PCI bus support + * + ***************************************/ + +static int pci_cirrus_vga_initfn(PCIDevice *dev) +{ + PCICirrusVGAState *d = DO_UPCAST(PCICirrusVGAState, dev, dev); + CirrusVGAState *s = &d->cirrus_vga; + PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + int16_t device_id = pc->device_id; + + /* setup VGA */ + vga_common_init(&s->vga); + cirrus_init_common(s, device_id, 1, pci_address_space(dev), + pci_address_space_io(dev)); + s->vga.con = graphic_console_init(DEVICE(dev), s->vga.hw_ops, &s->vga); + + /* setup PCI */ + + memory_region_init(&s->pci_bar, "cirrus-pci-bar0", 0x2000000); + + /* XXX: add byte swapping apertures */ + memory_region_add_subregion(&s->pci_bar, 0, &s->cirrus_linear_io); + memory_region_add_subregion(&s->pci_bar, 0x1000000, + &s->cirrus_linear_bitblt_io); + + /* setup memory space */ + /* memory #0 LFB */ + /* memory #1 memory-mapped I/O */ + /* XXX: s->vga.vram_size must be a power of two */ + pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->pci_bar); + if (device_id == CIRRUS_ID_CLGD5446) { + pci_register_bar(&d->dev, 1, 0, &s->cirrus_mmio_io); + } + return 0; +} + +static Property pci_vga_cirrus_properties[] = { + DEFINE_PROP_UINT32("vgamem_mb", struct PCICirrusVGAState, + cirrus_vga.vga.vram_size_mb, 8), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cirrus_vga_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->no_hotplug = 1; + k->init = pci_cirrus_vga_initfn; + k->romfile = VGABIOS_CIRRUS_FILENAME; + k->vendor_id = PCI_VENDOR_ID_CIRRUS; + k->device_id = CIRRUS_ID_CLGD5446; + k->class_id = PCI_CLASS_DISPLAY_VGA; + dc->desc = "Cirrus CLGD 54xx VGA"; + dc->vmsd = &vmstate_pci_cirrus_vga; + dc->props = pci_vga_cirrus_properties; +} + +static const TypeInfo cirrus_vga_info = { + .name = "cirrus-vga", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCICirrusVGAState), + .class_init = cirrus_vga_class_init, +}; + +static void cirrus_vga_register_types(void) +{ + type_register_static(&isa_cirrus_vga_info); + type_register_static(&cirrus_vga_info); +} + +type_init(cirrus_vga_register_types) + +#endif \ No newline at end of file diff --git a/qemuvga/cirrus_vga_rop.h b/qemuvga/cirrus_vga_rop.h new file mode 100644 index 00000000..24cf95ac --- /dev/null +++ b/qemuvga/cirrus_vga_rop.h @@ -0,0 +1,211 @@ +/* + * QEMU Cirrus CLGD 54xx VGA Emulator. + * + * Copyright (c) 2004 Fabrice Bellard + * + * 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. + */ + +STATIC_INLINE void glue(rop_8_,ROP_NAME)(uint8_t *dst, uint8_t src) +{ + *dst = ROP_FN(*dst, src); +} + +STATIC_INLINE void glue(rop_16_,ROP_NAME)(uint16_t *dst, uint16_t src) +{ + *dst = ROP_FN(*dst, src); +} + +STATIC_INLINE void glue(rop_32_,ROP_NAME)(uint32_t *dst, uint32_t src) +{ + *dst = ROP_FN(*dst, src); +} + +#define ROP_OP(d, s) glue(rop_8_,ROP_NAME)(d, s) +#define ROP_OP_16(d, s) glue(rop_16_,ROP_NAME)(d, s) +#define ROP_OP_32(d, s) glue(rop_32_,ROP_NAME)(d, s) +#undef ROP_FN + +static void +glue(cirrus_bitblt_rop_fwd_, ROP_NAME)(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ + int x,y; + dstpitch -= bltwidth; + srcpitch -= bltwidth; + +#if 0 + /* Invalid test! TW */ + if (dstpitch < 0 || srcpitch < 0) { + /* is 0 valid? srcpitch == 0 could be useful */ + return; + } +#endif + + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x++) { + ROP_OP(dst, *src); + dst++; + src++; + } + dst += dstpitch; + src += srcpitch; + } +} + +static void +glue(cirrus_bitblt_rop_bkwd_, ROP_NAME)(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ + int x,y; + dstpitch += bltwidth; + srcpitch += bltwidth; + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x++) { + ROP_OP(dst, *src); + dst--; + src--; + } + dst += dstpitch; + src += srcpitch; + } +} + +static void +glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_8)(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ + int x,y; + uint8_t p; + dstpitch -= bltwidth; + srcpitch -= bltwidth; + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x++) { + p = *dst; + ROP_OP(&p, *src); + if (p != s->vga.gr[0x34]) *dst = p; + dst++; + src++; + } + dst += dstpitch; + src += srcpitch; + } +} + +static void +glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_8)(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ + int x,y; + uint8_t p; + dstpitch += bltwidth; + srcpitch += bltwidth; + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x++) { + p = *dst; + ROP_OP(&p, *src); + if (p != s->vga.gr[0x34]) *dst = p; + dst--; + src--; + } + dst += dstpitch; + src += srcpitch; + } +} + +static void +glue(glue(cirrus_bitblt_rop_fwd_transp_, ROP_NAME),_16)(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ + int x,y; + uint8_t p1, p2; + dstpitch -= bltwidth; + srcpitch -= bltwidth; + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x+=2) { + p1 = *dst; + p2 = *(dst+1); + ROP_OP(&p1, *src); + ROP_OP(&p2, *(src + 1)); + if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) { + *dst = p1; + *(dst+1) = p2; + } + dst+=2; + src+=2; + } + dst += dstpitch; + src += srcpitch; + } +} + +static void +glue(glue(cirrus_bitblt_rop_bkwd_transp_, ROP_NAME),_16)(CirrusVGAState *s, + uint8_t *dst,const uint8_t *src, + int dstpitch,int srcpitch, + int bltwidth,int bltheight) +{ + int x,y; + uint8_t p1, p2; + dstpitch += bltwidth; + srcpitch += bltwidth; + for (y = 0; y < bltheight; y++) { + for (x = 0; x < bltwidth; x+=2) { + p1 = *(dst-1); + p2 = *dst; + ROP_OP(&p1, *(src - 1)); + ROP_OP(&p2, *src); + if ((p1 != s->vga.gr[0x34]) || (p2 != s->vga.gr[0x35])) { + *(dst-1) = p1; + *dst = p2; + } + dst-=2; + src-=2; + } + dst += dstpitch; + src += srcpitch; + } +} + +#define DEPTH 8 +#include "cirrus_vga_rop2.h" + +#define DEPTH 16 +#include "cirrus_vga_rop2.h" + +#define DEPTH 24 +#include "cirrus_vga_rop2.h" + +#define DEPTH 32 +#include "cirrus_vga_rop2.h" + +#undef ROP_NAME +#undef ROP_OP +#undef ROP_OP_16 +#undef ROP_OP_32 diff --git a/qemuvga/cirrus_vga_rop2.h b/qemuvga/cirrus_vga_rop2.h new file mode 100644 index 00000000..d28bcc6f --- /dev/null +++ b/qemuvga/cirrus_vga_rop2.h @@ -0,0 +1,281 @@ +/* + * QEMU Cirrus CLGD 54xx VGA Emulator. + * + * Copyright (c) 2004 Fabrice Bellard + * + * 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. + */ + +#if DEPTH == 8 +#define PUTPIXEL() ROP_OP(&d[0], col) +#elif DEPTH == 16 +#define PUTPIXEL() ROP_OP_16((uint16_t *)&d[0], col) +#elif DEPTH == 24 +#define PUTPIXEL() ROP_OP(&d[0], col); \ + ROP_OP(&d[1], (col >> 8)); \ + ROP_OP(&d[2], (col >> 16)) +#elif DEPTH == 32 +#define PUTPIXEL() ROP_OP_32(((uint32_t *)&d[0]), col) +#else +#error unsupported DEPTH +#endif + +static void +glue(glue(glue(cirrus_patternfill_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint8_t *d; + int x, y, pattern_y, pattern_pitch, pattern_x; + unsigned int col; + const uint8_t *src1; +#if DEPTH == 24 + int skipleft = s->vga.gr[0x2f] & 0x1f; +#else + int skipleft = (s->vga.gr[0x2f] & 0x07) * (DEPTH / 8); +#endif + +#if DEPTH == 8 + pattern_pitch = 8; +#elif DEPTH == 16 + pattern_pitch = 16; +#else + pattern_pitch = 32; +#endif + pattern_y = s->cirrus_blt_srcaddr & 7; + for(y = 0; y < bltheight; y++) { + pattern_x = skipleft; + d = dst + skipleft; + src1 = src + pattern_y * pattern_pitch; + for (x = skipleft; x < bltwidth; x += (DEPTH / 8)) { +#if DEPTH == 8 + col = src1[pattern_x]; + pattern_x = (pattern_x + 1) & 7; +#elif DEPTH == 16 + col = ((uint16_t *)(src1 + pattern_x))[0]; + pattern_x = (pattern_x + 2) & 15; +#elif DEPTH == 24 + { + const uint8_t *src2 = src1 + pattern_x * 3; + col = src2[0] | (src2[1] << 8) | (src2[2] << 16); + pattern_x = (pattern_x + 1) & 7; + } +#else + col = ((uint32_t *)(src1 + pattern_x))[0]; + pattern_x = (pattern_x + 4) & 31; +#endif + PUTPIXEL(); + d += (DEPTH / 8); + } + pattern_y = (pattern_y + 1) & 7; + dst += dstpitch; + } +} + +/* NOTE: srcpitch is ignored */ +static void +glue(glue(glue(cirrus_colorexpand_transp_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint8_t *d; + int x, y; + unsigned bits, bits_xor; + unsigned int col; + unsigned bitmask; + unsigned index; +#if DEPTH == 24 + int dstskipleft = s->vga.gr[0x2f] & 0x1f; + int srcskipleft = dstskipleft / 3; +#else + int srcskipleft = s->vga.gr[0x2f] & 0x07; + int dstskipleft = srcskipleft * (DEPTH / 8); +#endif + + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) { + bits_xor = 0xff; + col = s->cirrus_blt_bgcol; + } else { + bits_xor = 0x00; + col = s->cirrus_blt_fgcol; + } + + for(y = 0; y < bltheight; y++) { + bitmask = 0x80 >> srcskipleft; + bits = *src++ ^ bits_xor; + d = dst + dstskipleft; + for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { + if ((bitmask & 0xff) == 0) { + bitmask = 0x80; + bits = *src++ ^ bits_xor; + } + index = (bits & bitmask); + if (index) { + PUTPIXEL(); + } + d += (DEPTH / 8); + bitmask >>= 1; + } + dst += dstpitch; + } +} + +static void +glue(glue(glue(cirrus_colorexpand_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint32_t colors[2]; + uint8_t *d; + int x, y; + unsigned bits; + unsigned int col; + unsigned bitmask; + int srcskipleft = s->vga.gr[0x2f] & 0x07; + int dstskipleft = srcskipleft * (DEPTH / 8); + + colors[0] = s->cirrus_blt_bgcol; + colors[1] = s->cirrus_blt_fgcol; + for(y = 0; y < bltheight; y++) { + bitmask = 0x80 >> srcskipleft; + bits = *src++; + d = dst + dstskipleft; + for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { + if ((bitmask & 0xff) == 0) { + bitmask = 0x80; + bits = *src++; + } + col = colors[!!(bits & bitmask)]; + PUTPIXEL(); + d += (DEPTH / 8); + bitmask >>= 1; + } + dst += dstpitch; + } +} + +static void +glue(glue(glue(cirrus_colorexpand_pattern_transp_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint8_t *d; + int x, y, bitpos, pattern_y; + unsigned int bits, bits_xor; + unsigned int col; +#if DEPTH == 24 + int dstskipleft = s->vga.gr[0x2f] & 0x1f; + int srcskipleft = dstskipleft / 3; +#else + int srcskipleft = s->vga.gr[0x2f] & 0x07; + int dstskipleft = srcskipleft * (DEPTH / 8); +#endif + + if (s->cirrus_blt_modeext & CIRRUS_BLTMODEEXT_COLOREXPINV) { + bits_xor = 0xff; + col = s->cirrus_blt_bgcol; + } else { + bits_xor = 0x00; + col = s->cirrus_blt_fgcol; + } + pattern_y = s->cirrus_blt_srcaddr & 7; + + for(y = 0; y < bltheight; y++) { + bits = src[pattern_y] ^ bits_xor; + bitpos = 7 - srcskipleft; + d = dst + dstskipleft; + for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { + if ((bits >> bitpos) & 1) { + PUTPIXEL(); + } + d += (DEPTH / 8); + bitpos = (bitpos - 1) & 7; + } + pattern_y = (pattern_y + 1) & 7; + dst += dstpitch; + } +} + +static void +glue(glue(glue(cirrus_colorexpand_pattern_, ROP_NAME), _),DEPTH) + (CirrusVGAState * s, uint8_t * dst, + const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight) +{ + uint32_t colors[2]; + uint8_t *d; + int x, y, bitpos, pattern_y; + unsigned int bits; + unsigned int col; + int srcskipleft = s->vga.gr[0x2f] & 0x07; + int dstskipleft = srcskipleft * (DEPTH / 8); + + colors[0] = s->cirrus_blt_bgcol; + colors[1] = s->cirrus_blt_fgcol; + pattern_y = s->cirrus_blt_srcaddr & 7; + + for(y = 0; y < bltheight; y++) { + bits = src[pattern_y]; + bitpos = 7 - srcskipleft; + d = dst + dstskipleft; + for (x = dstskipleft; x < bltwidth; x += (DEPTH / 8)) { + col = colors[(bits >> bitpos) & 1]; + PUTPIXEL(); + d += (DEPTH / 8); + bitpos = (bitpos - 1) & 7; + } + pattern_y = (pattern_y + 1) & 7; + dst += dstpitch; + } +} + +static void +glue(glue(glue(cirrus_fill_, ROP_NAME), _),DEPTH) + (CirrusVGAState *s, + uint8_t *dst, int dst_pitch, + int width, int height) +{ + uint8_t *d, *d1; + uint32_t col; + int x, y; + + col = s->cirrus_blt_fgcol; + + d1 = dst; + for(y = 0; y < height; y++) { + d = d1; + for(x = 0; x < width; x += (DEPTH / 8)) { + PUTPIXEL(); + d += (DEPTH / 8); + } + d1 += dst_pitch; + } +} + +#undef DEPTH +#undef PUTPIXEL diff --git a/qemuvga/cirrus_vga_template.h b/qemuvga/cirrus_vga_template.h new file mode 100644 index 00000000..3b282805 --- /dev/null +++ b/qemuvga/cirrus_vga_template.h @@ -0,0 +1,102 @@ +/* + * QEMU Cirrus VGA Emulator templates + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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. + */ + +#if DEPTH == 8 +#define BPP 1 +#elif DEPTH == 15 || DEPTH == 16 +#define BPP 2 +#elif DEPTH == 32 +#define BPP 4 +#else +#error unsupported depth +#endif + +static void glue(vga_draw_cursor_line_, DEPTH)(uint8_t *d1, + const uint8_t *src1, + int poffset, int w, + unsigned int color0, + unsigned int color1, + unsigned int color_xor) +{ + const uint8_t *plane0, *plane1; + int x, b0, b1; + uint8_t *d; + + d = d1; + plane0 = src1; + plane1 = src1 + poffset; + for (x = 0; x < w; x++) { + b0 = (plane0[x >> 3] >> (7 - (x & 7))) & 1; + b1 = (plane1[x >> 3] >> (7 - (x & 7))) & 1; +#if DEPTH == 8 + switch (b0 | (b1 << 1)) { + case 0: + break; + case 1: + d[0] ^= color_xor; + break; + case 2: + d[0] = color0; + break; + case 3: + d[0] = color1; + break; + } +#elif DEPTH == 16 + switch (b0 | (b1 << 1)) { + case 0: + break; + case 1: + ((uint16_t *)d)[0] ^= color_xor; + break; + case 2: + ((uint16_t *)d)[0] = color0; + break; + case 3: + ((uint16_t *)d)[0] = color1; + break; + } +#elif DEPTH == 32 + switch (b0 | (b1 << 1)) { + case 0: + break; + case 1: + ((uint32_t *)d)[0] ^= color_xor; + break; + case 2: + ((uint32_t *)d)[0] = color0; + break; + case 3: + ((uint32_t *)d)[0] = color1; + break; + } +#else +#error unsupported depth +#endif + d += BPP; + } +} + +#undef DEPTH +#undef BPP diff --git a/qemuvga/pixel_ops.h b/qemuvga/pixel_ops.h new file mode 100644 index 00000000..d390adfd --- /dev/null +++ b/qemuvga/pixel_ops.h @@ -0,0 +1,53 @@ +static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, + unsigned int b) +{ + return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6); +} + +static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, + unsigned int b) +{ + return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3); +} + +static inline unsigned int rgb_to_pixel15bgr(unsigned int r, unsigned int g, + unsigned int b) +{ + return ((b >> 3) << 10) | ((g >> 3) << 5) | (r >> 3); +} + +static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, + unsigned int b) +{ + return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); +} + +static inline unsigned int rgb_to_pixel16bgr(unsigned int r, unsigned int g, + unsigned int b) +{ + return ((b >> 3) << 11) | ((g >> 2) << 5) | (r >> 3); +} + +static inline unsigned int rgb_to_pixel24(unsigned int r, unsigned int g, + unsigned int b) +{ + return (r << 16) | (g << 8) | b; +} + +static inline unsigned int rgb_to_pixel24bgr(unsigned int r, unsigned int g, + unsigned int b) +{ + return (b << 16) | (g << 8) | r; +} + +static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, + unsigned int b) +{ + return (r << 16) | (g << 8) | b; +} + +static inline unsigned int rgb_to_pixel32bgr(unsigned int r, unsigned int g, + unsigned int b) +{ + return (b << 16) | (g << 8) | r; +} diff --git a/qemuvga/qemumemory.h b/qemuvga/qemumemory.h new file mode 100644 index 00000000..1ddfb673 --- /dev/null +++ b/qemuvga/qemumemory.h @@ -0,0 +1,902 @@ +/* + * Physical memory management API + * + * Copyright 2011 Red Hat, Inc. and/or its affiliates + * + * Authors: + * Avi Kivity + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef MEMORY_H +#define MEMORY_H + +#ifndef CONFIG_USER_ONLY + +#include + +typedef struct MemoryRegionOps MemoryRegionOps; +typedef struct MemoryRegionPortio MemoryRegionPortio; +typedef struct MemoryRegionMmio MemoryRegionMmio; + +/* Must match *_DIRTY_FLAGS in cpu-all.h. To be replaced with dynamic + * registration. + */ +#define DIRTY_MEMORY_VGA 0 +#define DIRTY_MEMORY_CODE 1 +#define DIRTY_MEMORY_MIGRATION 3 + +struct MemoryRegionMmio { + CPUReadMemoryFunc *read[3]; + CPUWriteMemoryFunc *write[3]; +}; + +/* Internal use; thunks between old-style IORange and MemoryRegions. */ +typedef struct MemoryRegionIORange MemoryRegionIORange; +struct MemoryRegionIORange { + IORange iorange; + //MemoryRegion *mr; + hwaddr offset; +}; + +/* + * Memory region callbacks + */ +struct MemoryRegionOps { + /* Read from the memory region. @addr is relative to @mr; @size is + * in bytes. */ + uint64_t (*read)(void *opaque, + hwaddr addr, + unsigned size); + /* Write to the memory region. @addr is relative to @mr; @size is + * in bytes. */ + void (*write)(void *opaque, + hwaddr addr, + uint64_t data, + unsigned size); + + enum device_endian endianness; + /* Guest-visible constraints: */ + struct { + /* If nonzero, specify bounds on access sizes beyond which a machine + * check is thrown. + */ + unsigned min_access_size; + unsigned max_access_size; + /* If true, unaligned accesses are supported. Otherwise unaligned + * accesses throw machine checks. + */ + bool unaligned; + /* + * If present, and returns #false, the transaction is not accepted + * by the device (and results in machine dependent behaviour such + * as a machine check exception). + */ + bool (*accepts)(void *opaque, hwaddr addr, + unsigned size, bool is_write); + } valid; + /* Internal implementation constraints: */ + struct { + /* If nonzero, specifies the minimum size implemented. Smaller sizes + * will be rounded upwards and a partial result will be returned. + */ + unsigned min_access_size; + /* If nonzero, specifies the maximum size implemented. Larger sizes + * will be done as a series of accesses with smaller sizes. + */ + unsigned max_access_size; + /* If true, unaligned accesses are supported. Otherwise all accesses + * are converted to (possibly multiple) naturally aligned accesses. + */ + bool unaligned; + } impl; +#if 0 + /* If .read and .write are not present, old_portio may be used for + * backwards compatibility with old portio registration + */ + const MemoryRegionPortio *old_portio; + /* If .read and .write are not present, old_mmio may be used for + * backwards compatibility with old mmio registration + */ + const MemoryRegionMmio old_mmio; +#endif +}; + +typedef struct CoalescedMemoryRange CoalescedMemoryRange; +typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; + +typedef struct MemoryRegion { + /* All fields are private - violators will be prosecuted */ + void *opaque; +#if 0 + const MemoryRegionOps *ops; + MemoryRegion *parent; + Int128 size; + hwaddr addr; + void (*destructor)(MemoryRegion *mr); + ram_addr_t ram_addr; + bool subpage; + bool terminates; + bool readable; + bool ram; + bool readonly; /* For RAM regions */ + bool enabled; + bool rom_device; + bool warning_printed; /* For reservations */ + bool flush_coalesced_mmio; + MemoryRegion *alias; + hwaddr alias_offset; + unsigned priority; + bool may_overlap; + QTAILQ_HEAD(subregions, MemoryRegion) subregions; + QTAILQ_ENTRY(MemoryRegion) subregions_link; + QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) coalesced; + const char *name; + uint8_t dirty_log_mask; + unsigned ioeventfd_nb; + MemoryRegionIoeventfd *ioeventfds; +#endif +} MemoryRegion; + +struct MemoryRegionPortio { + uint32_t offset; + uint32_t len; + unsigned size; + IOPortReadFunc *read; + IOPortWriteFunc *write; +}; + +#define PORTIO_END_OF_LIST() { } + +/** + * AddressSpace: describes a mapping of addresses to #MemoryRegion objects + */ +typedef struct AddressSpace AddressSpace; +struct AddressSpace { + /* All fields are private. */ + const char *name; + MemoryRegion *root; + struct FlatView *current_map; + int ioeventfd_nb; + struct MemoryRegionIoeventfd *ioeventfds; + struct AddressSpaceDispatch *dispatch; +#if 0 + QTAILQ_ENTRY(AddressSpace) address_spaces_link; +#endif +}; + +/** + * MemoryRegionSection: describes a fragment of a #MemoryRegion + * + * @mr: the region, or %NULL if empty + * @address_space: the address space the region is mapped in + * @offset_within_region: the beginning of the section, relative to @mr's start + * @size: the size of the section; will not exceed @mr's boundaries + * @offset_within_address_space: the address of the first byte of the section + * relative to the region's address space + * @readonly: writes to this section are ignored + */ +typedef struct MemoryRegionSection MemoryRegionSection; +struct MemoryRegionSection { + MemoryRegion *mr; + AddressSpace *address_space; + hwaddr offset_within_region; + uint64_t size; + hwaddr offset_within_address_space; + bool readonly; +}; + +typedef struct MemoryListener MemoryListener; + +/** + * MemoryListener: callbacks structure for updates to the physical memory map + * + * Allows a component to adjust to changes in the guest-visible memory map. + * Use with memory_listener_register() and memory_listener_unregister(). + */ +struct MemoryListener { + void (*begin)(MemoryListener *listener); + void (*commit)(MemoryListener *listener); + void (*region_add)(MemoryListener *listener, MemoryRegionSection *section); + void (*region_del)(MemoryListener *listener, MemoryRegionSection *section); + void (*region_nop)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_start)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_stop)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_sync)(MemoryListener *listener, MemoryRegionSection *section); + void (*log_global_start)(MemoryListener *listener); + void (*log_global_stop)(MemoryListener *listener); +#if 0 + void (*eventfd_add)(MemoryListener *listener, MemoryRegionSection *section, + bool match_data, uint64_t data, EventNotifier *e); + void (*eventfd_del)(MemoryListener *listener, MemoryRegionSection *section, + bool match_data, uint64_t data, EventNotifier *e); + void (*coalesced_mmio_add)(MemoryListener *listener, MemoryRegionSection *section, + hwaddr addr, hwaddr len); + void (*coalesced_mmio_del)(MemoryListener *listener, MemoryRegionSection *section, + hwaddr addr, hwaddr len); +#endif + /* Lower = earlier (during add), later (during del) */ + unsigned priority; + AddressSpace *address_space_filter; +#if 0 + QTAILQ_ENTRY(MemoryListener) link; +#endif +}; + +/** + * memory_region_init: Initialize a memory region + * + * The region typically acts as a container for other memory regions. Use + * memory_region_add_subregion() to add subregions. + * + * @mr: the #MemoryRegion to be initialized + * @name: used for debugging; not visible to the user or ABI + * @size: size of the region; any subregions beyond this size will be clipped + */ +void memory_region_init(MemoryRegion *mr, + const char *name, + uint64_t size); +/** + * memory_region_init_io: Initialize an I/O memory region. + * + * Accesses into the region will cause the callbacks in @ops to be called. + * if @size is nonzero, subregions will be clipped to @size. + * + * @mr: the #MemoryRegion to be initialized. + * @ops: a structure containing read and write callbacks to be used when + * I/O is performed on the region. + * @opaque: passed to to the read and write callbacks of the @ops structure. + * @name: used for debugging; not visible to the user or ABI + * @size: size of the region. + */ +void memory_region_init_io(MemoryRegion *mr, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size); + +/** + * memory_region_init_ram: Initialize RAM memory region. Accesses into the + * region will modify memory directly. + * + * @mr: the #MemoryRegion to be initialized. + * @name: the name of the region. + * @size: size of the region. + */ +void memory_region_init_ram(MemoryRegion *mr, + const char *name, + uint64_t size); + +/** + * memory_region_init_ram_ptr: Initialize RAM memory region from a + * user-provided pointer. Accesses into the + * region will modify memory directly. + * + * @mr: the #MemoryRegion to be initialized. + * @name: the name of the region. + * @size: size of the region. + * @ptr: memory to be mapped; must contain at least @size bytes. + */ +void memory_region_init_ram_ptr(MemoryRegion *mr, + const char *name, + uint64_t size, + void *ptr); + +/** + * memory_region_init_alias: Initialize a memory region that aliases all or a + * part of another memory region. + * + * @mr: the #MemoryRegion to be initialized. + * @name: used for debugging; not visible to the user or ABI + * @orig: the region to be referenced; @mr will be equivalent to + * @orig between @offset and @offset + @size - 1. + * @offset: start of the section in @orig to be referenced. + * @size: size of the region. + */ +void memory_region_init_alias(MemoryRegion *mr, + const char *name, + MemoryRegion *orig, + hwaddr offset, + uint64_t size); + +/** + * memory_region_init_rom_device: Initialize a ROM memory region. Writes are + * handled via callbacks. + * + * @mr: the #MemoryRegion to be initialized. + * @ops: callbacks for write access handling. + * @name: the name of the region. + * @size: size of the region. + */ +void memory_region_init_rom_device(MemoryRegion *mr, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size); + +/** + * memory_region_init_reservation: Initialize a memory region that reserves + * I/O space. + * + * A reservation region primariy serves debugging purposes. It claims I/O + * space that is not supposed to be handled by QEMU itself. Any access via + * the memory API will cause an abort(). + * + * @mr: the #MemoryRegion to be initialized + * @name: used for debugging; not visible to the user or ABI + * @size: size of the region. + */ +void memory_region_init_reservation(MemoryRegion *mr, + const char *name, + uint64_t size); +/** + * memory_region_destroy: Destroy a memory region and reclaim all resources. + * + * @mr: the region to be destroyed. May not currently be a subregion + * (see memory_region_add_subregion()) or referenced in an alias + * (see memory_region_init_alias()). + */ +void memory_region_destroy(MemoryRegion *mr); + +/** + * memory_region_size: get a memory region's size. + * + * @mr: the memory region being queried. + */ +uint64_t memory_region_size(MemoryRegion *mr); + +/** + * memory_region_is_ram: check whether a memory region is random access + * + * Returns %true is a memory region is random access. + * + * @mr: the memory region being queried + */ +bool memory_region_is_ram(MemoryRegion *mr); + +/** + * memory_region_is_romd: check whether a memory region is ROMD + * + * Returns %true is a memory region is ROMD and currently set to allow + * direct reads. + * + * @mr: the memory region being queried + */ +#if 0 +static inline bool memory_region_is_romd(MemoryRegion *mr) +{ + return mr->rom_device && mr->readable; +} +#endif + +/** + * memory_region_name: get a memory region's name + * + * Returns the string that was used to initialize the memory region. + * + * @mr: the memory region being queried + */ +const char *memory_region_name(MemoryRegion *mr); + +/** + * memory_region_is_logging: return whether a memory region is logging writes + * + * Returns %true if the memory region is logging writes + * + * @mr: the memory region being queried + */ +bool memory_region_is_logging(MemoryRegion *mr); + +/** + * memory_region_is_rom: check whether a memory region is ROM + * + * Returns %true is a memory region is read-only memory. + * + * @mr: the memory region being queried + */ +bool memory_region_is_rom(MemoryRegion *mr); + +/** + * memory_region_get_ram_ptr: Get a pointer into a RAM memory region. + * + * Returns a host pointer to a RAM memory region (created with + * memory_region_init_ram() or memory_region_init_ram_ptr()). Use with + * care. + * + * @mr: the memory region being queried. + */ +void *memory_region_get_ram_ptr(MemoryRegion *mr); + +/** + * memory_region_set_log: Turn dirty logging on or off for a region. + * + * Turns dirty logging on or off for a specified client (display, migration). + * Only meaningful for RAM regions. + * + * @mr: the memory region being updated. + * @log: whether dirty logging is to be enabled or disabled. + * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or + * %DIRTY_MEMORY_VGA. + */ +void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client); + +/** + * memory_region_get_dirty: Check whether a range of bytes is dirty + * for a specified client. + * + * Checks whether a range of bytes has been written to since the last + * call to memory_region_reset_dirty() with the same @client. Dirty logging + * must be enabled. + * + * @mr: the memory region being queried. + * @addr: the address (relative to the start of the region) being queried. + * @size: the size of the range being queried. + * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or + * %DIRTY_MEMORY_VGA. + */ +bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr, + hwaddr size, unsigned client); + +/** + * memory_region_set_dirty: Mark a range of bytes as dirty in a memory region. + * + * Marks a range of bytes as dirty, after it has been dirtied outside + * guest code. + * + * @mr: the memory region being dirtied. + * @addr: the address (relative to the start of the region) being dirtied. + * @size: size of the range being dirtied. + */ +void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, + hwaddr size); + +/** + * memory_region_test_and_clear_dirty: Check whether a range of bytes is dirty + * for a specified client. It clears them. + * + * Checks whether a range of bytes has been written to since the last + * call to memory_region_reset_dirty() with the same @client. Dirty logging + * must be enabled. + * + * @mr: the memory region being queried. + * @addr: the address (relative to the start of the region) being queried. + * @size: the size of the range being queried. + * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or + * %DIRTY_MEMORY_VGA. + */ +bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, + hwaddr size, unsigned client); +/** + * memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with + * any external TLBs (e.g. kvm) + * + * Flushes dirty information from accelerators such as kvm and vhost-net + * and makes it available to users of the memory API. + * + * @mr: the region being flushed. + */ +void memory_region_sync_dirty_bitmap(MemoryRegion *mr); + +/** + * memory_region_reset_dirty: Mark a range of pages as clean, for a specified + * client. + * + * Marks a range of pages as no longer dirty. + * + * @mr: the region being updated. + * @addr: the start of the subrange being cleaned. + * @size: the size of the subrange being cleaned. + * @client: the user of the logging information; %DIRTY_MEMORY_MIGRATION or + * %DIRTY_MEMORY_VGA. + */ +void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, + hwaddr size, unsigned client); + +/** + * memory_region_set_readonly: Turn a memory region read-only (or read-write) + * + * Allows a memory region to be marked as read-only (turning it into a ROM). + * only useful on RAM regions. + * + * @mr: the region being updated. + * @readonly: whether rhe region is to be ROM or RAM. + */ +void memory_region_set_readonly(MemoryRegion *mr, bool readonly); + +/** + * memory_region_rom_device_set_readable: enable/disable ROM readability + * + * Allows a ROM device (initialized with memory_region_init_rom_device() to + * to be marked as readable (default) or not readable. When it is readable, + * the device is mapped to guest memory. When not readable, reads are + * forwarded to the #MemoryRegion.read function. + * + * @mr: the memory region to be updated + * @readable: whether reads are satisified directly (%true) or via callbacks + * (%false) + */ +void memory_region_rom_device_set_readable(MemoryRegion *mr, bool readable); + +/** + * memory_region_set_coalescing: Enable memory coalescing for the region. + * + * Enabled writes to a region to be queued for later processing. MMIO ->write + * callbacks may be delayed until a non-coalesced MMIO is issued. + * Only useful for IO regions. Roughly similar to write-combining hardware. + * + * @mr: the memory region to be write coalesced + */ +void memory_region_set_coalescing(MemoryRegion *mr); + +/** + * memory_region_add_coalescing: Enable memory coalescing for a sub-range of + * a region. + * + * Like memory_region_set_coalescing(), but works on a sub-range of a region. + * Multiple calls can be issued coalesced disjoint ranges. + * + * @mr: the memory region to be updated. + * @offset: the start of the range within the region to be coalesced. + * @size: the size of the subrange to be coalesced. + */ +void memory_region_add_coalescing(MemoryRegion *mr, + hwaddr offset, + uint64_t size); + +/** + * memory_region_clear_coalescing: Disable MMIO coalescing for the region. + * + * Disables any coalescing caused by memory_region_set_coalescing() or + * memory_region_add_coalescing(). Roughly equivalent to uncacheble memory + * hardware. + * + * @mr: the memory region to be updated. + */ +void memory_region_clear_coalescing(MemoryRegion *mr); + +/** + * memory_region_set_flush_coalesced: Enforce memory coalescing flush before + * accesses. + * + * Ensure that pending coalesced MMIO request are flushed before the memory + * region is accessed. This property is automatically enabled for all regions + * passed to memory_region_set_coalescing() and memory_region_add_coalescing(). + * + * @mr: the memory region to be updated. + */ +void memory_region_set_flush_coalesced(MemoryRegion *mr); + +/** + * memory_region_clear_flush_coalesced: Disable memory coalescing flush before + * accesses. + * + * Clear the automatic coalesced MMIO flushing enabled via + * memory_region_set_flush_coalesced. Note that this service has no effect on + * memory regions that have MMIO coalescing enabled for themselves. For them, + * automatic flushing will stop once coalescing is disabled. + * + * @mr: the memory region to be updated. + */ +void memory_region_clear_flush_coalesced(MemoryRegion *mr); + +#if 0 +/** + * memory_region_add_eventfd: Request an eventfd to be triggered when a word + * is written to a location. + * + * Marks a word in an IO region (initialized with memory_region_init_io()) + * as a trigger for an eventfd event. The I/O callback will not be called. + * The caller must be prepared to handle failure (that is, take the required + * action if the callback _is_ called). + * + * @mr: the memory region being updated. + * @addr: the address within @mr that is to be monitored + * @size: the size of the access to trigger the eventfd + * @match_data: whether to match against @data, instead of just @addr + * @data: the data to match against the guest write + * @fd: the eventfd to be triggered when @addr, @size, and @data all match. + **/ +void memory_region_add_eventfd(MemoryRegion *mr, + hwaddr addr, + unsigned size, + bool match_data, + uint64_t data, + EventNotifier *e); + +/** + * memory_region_del_eventfd: Cancel an eventfd. + * + * Cancels an eventfd trigger requested by a previous + * memory_region_add_eventfd() call. + * + * @mr: the memory region being updated. + * @addr: the address within @mr that is to be monitored + * @size: the size of the access to trigger the eventfd + * @match_data: whether to match against @data, instead of just @addr + * @data: the data to match against the guest write + * @fd: the eventfd to be triggered when @addr, @size, and @data all match. + */ +void memory_region_del_eventfd(MemoryRegion *mr, + hwaddr addr, + unsigned size, + bool match_data, + uint64_t data, + EventNotifier *e); +#endif +/** + * memory_region_add_subregion: Add a subregion to a container. + * + * Adds a subregion at @offset. The subregion may not overlap with other + * subregions (except for those explicitly marked as overlapping). A region + * may only be added once as a subregion (unless removed with + * memory_region_del_subregion()); use memory_region_init_alias() if you + * want a region to be a subregion in multiple locations. + * + * @mr: the region to contain the new subregion; must be a container + * initialized with memory_region_init(). + * @offset: the offset relative to @mr where @subregion is added. + * @subregion: the subregion to be added. + */ +void memory_region_add_subregion(MemoryRegion *mr, + hwaddr offset, + MemoryRegion *subregion); +/** + * memory_region_add_subregion_overlap: Add a subregion to a container + * with overlap. + * + * Adds a subregion at @offset. The subregion may overlap with other + * subregions. Conflicts are resolved by having a higher @priority hide a + * lower @priority. Subregions without priority are taken as @priority 0. + * A region may only be added once as a subregion (unless removed with + * memory_region_del_subregion()); use memory_region_init_alias() if you + * want a region to be a subregion in multiple locations. + * + * @mr: the region to contain the new subregion; must be a container + * initialized with memory_region_init(). + * @offset: the offset relative to @mr where @subregion is added. + * @subregion: the subregion to be added. + * @priority: used for resolving overlaps; highest priority wins. + */ +void memory_region_add_subregion_overlap(MemoryRegion *mr, + hwaddr offset, + MemoryRegion *subregion, + unsigned priority); + +#if 0 +/** + * memory_region_get_ram_addr: Get the ram address associated with a memory + * region + * + * DO NOT USE THIS FUNCTION. This is a temporary workaround while the Xen + * code is being reworked. + */ +ram_addr_t memory_region_get_ram_addr(MemoryRegion *mr); +#endif + +/** + * memory_region_del_subregion: Remove a subregion. + * + * Removes a subregion from its container. + * + * @mr: the container to be updated. + * @subregion: the region being removed; must be a current subregion of @mr. + */ +void memory_region_del_subregion(MemoryRegion *mr, + MemoryRegion *subregion); +/* + * memory_region_set_enabled: dynamically enable or disable a region + * + * Enables or disables a memory region. A disabled memory region + * ignores all accesses to itself and its subregions. It does not + * obscure sibling subregions with lower priority - it simply behaves as + * if it was removed from the hierarchy. + * + * Regions default to being enabled. + * + * @mr: the region to be updated + * @enabled: whether to enable or disable the region + */ +void memory_region_set_enabled(MemoryRegion *mr, bool enabled); + +/* + * memory_region_set_address: dynamically update the address of a region + * + * Dynamically updates the address of a region, relative to its parent. + * May be used on regions are currently part of a memory hierarchy. + * + * @mr: the region to be updated + * @addr: new address, relative to parent region + */ +void memory_region_set_address(MemoryRegion *mr, hwaddr addr); + +/* + * memory_region_set_alias_offset: dynamically update a memory alias's offset + * + * Dynamically updates the offset into the target region that an alias points + * to, as if the fourth argument to memory_region_init_alias() has changed. + * + * @mr: the #MemoryRegion to be updated; should be an alias. + * @offset: the new offset into the target memory region + */ +void memory_region_set_alias_offset(MemoryRegion *mr, + hwaddr offset); + +/** + * memory_region_find: locate a MemoryRegion in an address space + * + * Locates the first #MemoryRegion within an address space given by + * @address_space that overlaps the range given by @addr and @size. + * + * Returns a #MemoryRegionSection that describes a contiguous overlap. + * It will have the following characteristics: + * .@offset_within_address_space >= @addr + * .@offset_within_address_space + .@size <= @addr + @size + * .@size = 0 iff no overlap was found + * .@mr is non-%NULL iff an overlap was found + * + * @address_space: a top-level (i.e. parentless) region that contains + * the region to be found + * @addr: start of the area within @address_space to be searched + * @size: size of the area to be searched + */ +MemoryRegionSection memory_region_find(MemoryRegion *address_space, + hwaddr addr, uint64_t size); + +/** + * memory_region_section_addr: get offset within MemoryRegionSection + * + * Returns offset within MemoryRegionSection + * + * @section: the memory region section being queried + * @addr: address in address space + */ +STATIC_INLINE hwaddr +memory_region_section_addr(MemoryRegionSection *section, + hwaddr addr) +{ + addr -= section->offset_within_address_space; + addr += section->offset_within_region; + return addr; +} + +/** + * memory_global_sync_dirty_bitmap: synchronize the dirty log for all memory + * + * Synchronizes the dirty page log for an entire address space. + * @address_space: a top-level (i.e. parentless) region that contains the + * memory being synchronized + */ +void memory_global_sync_dirty_bitmap(MemoryRegion *address_space); + +/** + * memory_region_transaction_begin: Start a transaction. + * + * During a transaction, changes will be accumulated and made visible + * only when the transaction ends (is committed). + */ +void memory_region_transaction_begin(void); + +/** + * memory_region_transaction_commit: Commit a transaction and make changes + * visible to the guest. + */ +void memory_region_transaction_commit(void); + +/** + * memory_listener_register: register callbacks to be called when memory + * sections are mapped or unmapped into an address + * space + * + * @listener: an object containing the callbacks to be called + * @filter: if non-%NULL, only regions in this address space will be observed + */ +void memory_listener_register(MemoryListener *listener, AddressSpace *filter); + +/** + * memory_listener_unregister: undo the effect of memory_listener_register() + * + * @listener: an object containing the callbacks to be removed + */ +void memory_listener_unregister(MemoryListener *listener); + +/** + * memory_global_dirty_log_start: begin dirty logging for all regions + */ +void memory_global_dirty_log_start(void); + +/** + * memory_global_dirty_log_stop: end dirty logging for all regions + */ +void memory_global_dirty_log_stop(void); + +#if 0 +void mtree_info(fprintf_function mon_printf, void *f); +#endif + +/** + * address_space_init: initializes an address space + * + * @as: an uninitialized #AddressSpace + * @root: a #MemoryRegion that routes addesses for the address space + */ +void address_space_init(AddressSpace *as, MemoryRegion *root); + + +/** + * address_space_destroy: destroy an address space + * + * Releases all resources associated with an address space. After an address space + * is destroyed, its root memory region (given by address_space_init()) may be destroyed + * as well. + * + * @as: address space to be destroyed + */ +void address_space_destroy(AddressSpace *as); + +/** + * address_space_rw: read from or write to an address space. + * + * @as: #AddressSpace to be accessed + * @addr: address within that address space + * @buf: buffer with the data transferred + * @is_write: indicates the transfer direction + */ +void address_space_rw(AddressSpace *as, hwaddr addr, uint8_t *buf, + int len, bool is_write); + +/** + * address_space_write: write to address space. + * + * @as: #AddressSpace to be accessed + * @addr: address within that address space + * @buf: buffer with the data transferred + */ +void address_space_write(AddressSpace *as, hwaddr addr, + const uint8_t *buf, int len); + +/** + * address_space_read: read from an address space. + * + * @as: #AddressSpace to be accessed + * @addr: address within that address space + * @buf: buffer with the data transferred + */ +void address_space_read(AddressSpace *as, hwaddr addr, uint8_t *buf, int len); + +/* address_space_map: map a physical memory region into a host virtual address + * + * May map a subset of the requested range, given by and returned in @plen. + * May return %NULL if resources needed to perform the mapping are exhausted. + * Use only for reads OR writes - not for read-modify-write operations. + * Use cpu_register_map_client() to know when retrying the map operation is + * likely to succeed. + * + * @as: #AddressSpace to be accessed + * @addr: address within that address space + * @plen: pointer to length of buffer; updated on return + * @is_write: indicates the transfer direction + */ +void *address_space_map(AddressSpace *as, hwaddr addr, + hwaddr *plen, bool is_write); + +/* address_space_unmap: Unmaps a memory region previously mapped by address_space_map() + * + * Will also mark the memory as dirty if @is_write == %true. @access_len gives + * the amount of memory that was actually read or written by the caller. + * + * @as: #AddressSpace used + * @addr: address within that address space + * @len: buffer length as returned by address_space_map() + * @access_len: amount of data actually transferred + * @is_write: indicates the transfer direction + */ +void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, + int is_write, hwaddr access_len); + + +#endif + +#endif diff --git a/qemuvga/qemuuaeglue.cpp b/qemuvga/qemuuaeglue.cpp new file mode 100644 index 00000000..701be0b3 --- /dev/null +++ b/qemuvga/qemuuaeglue.cpp @@ -0,0 +1,127 @@ + +#include "qemuuaeglue.h" +#include "vga_int.h" + + +void memory_region_transaction_begin(void) +{ +} +void memory_region_transaction_commit(void) +{ +} +void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size) +{ +} +void memory_region_set_enabled(MemoryRegion *mr, bool enabled) +{ +} +void memory_region_set_alias_offset(MemoryRegion *mr, + hwaddr offset) +{ +} +void memory_region_add_subregion(MemoryRegion *mr, + hwaddr offset, + MemoryRegion *subregion) +{ +} +void memory_region_add_subregion_overlap(MemoryRegion *mr, + hwaddr offset, + MemoryRegion *subregion, + unsigned priority) +{ +} +void memory_region_del_subregion(MemoryRegion *mr, + MemoryRegion *subregion) +{ +} +void memory_region_destroy(MemoryRegion *mr) +{ +} +void memory_region_init_alias(MemoryRegion *mr, + const char *name, + MemoryRegion *orig, + hwaddr offset, + uint64_t size) +{ +} +void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) +{ +} +void memory_region_init(MemoryRegion *mr, + const char *name, + uint64_t size) +{ +} +void memory_region_set_flush_coalesced(MemoryRegion *mr) +{ +} +uint64_t memory_region_size(MemoryRegion *mr) +{ + return 0; +} +void memory_region_sync_dirty_bitmap(MemoryRegion *mr) +{ +} +void memory_region_set_coalescing(MemoryRegion *mr) +{ +} + +uint16_t le16_to_cpu(uint16_t v) +{ + return v; +} +uint32_t le32_to_cpu(uint32_t v) +{ + return v; +} + +void graphic_hw_update(QemuConsole *con) +{ +} +void qemu_console_copy(QemuConsole *con, int src_x, int src_y, + int dst_x, int dst_y, int w, int h) +{ +} + +void qemu_flush_coalesced_mmio_buffer(void) +{ +} + +QEMUClock *vm_clock; +vga_retrace_method vga_retrace_method_value = VGA_RETRACE_DUMB; + +int64_t qemu_get_clock_ns(QEMUClock *clock) +{ + return 0; +} + +void portio_list_init(PortioList *piolist, + const struct MemoryRegionPortio *callbacks, + void *opaque, const char *name) +{ +} +void portio_list_destroy(PortioList *piolist) +{ +} +void portio_list_add(PortioList *piolist, + struct MemoryRegion *address_space, + uint32_t addr) +{ +} +void portio_list_del(PortioList *piolist) +{ +} + +void dpy_text_cursor(QemuConsole *con, int x, int y) +{ +} +void dpy_text_update(QemuConsole *con, int x, int y, int w, int h) +{ +} +void dpy_text_resize(QemuConsole *con, int w, int h) +{ +} +void dpy_gfx_replace_surface(QemuConsole *con, + DisplaySurface *surface) +{ +} diff --git a/qemuvga/qemuuaeglue.h b/qemuvga/qemuuaeglue.h new file mode 100644 index 00000000..c3064859 --- /dev/null +++ b/qemuvga/qemuuaeglue.h @@ -0,0 +1,288 @@ + + +#include +#include + +//#define DEBUG_VGA_REG +//#define DEBUG_VGA + +extern void write_log (const char *, ...); + +#ifndef glue +#define xglue(x, y) x ## y +#define glue(x, y) xglue(x, y) +#define stringify(s) tostring(s) +#define tostring(s) #s +#endif + +#ifndef likely +#if __GNUC__ < 3 +#define __builtin_expect(x, n) (x) +#endif +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#endif + +#ifdef _MSC_VER +#include +#define container_of(address, type, field) ((type *)( \ + (PCHAR)(address) - \ + (ULONG_PTR)(&((type *)0)->field))) +#define STATIC_INLINE static __forceinline + +#define snprintf c99_snprintf +inline int c99_vsnprintf(char* str, size_t size, const char* format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(str, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} +inline int c99_snprintf(char* str, size_t size, const char* format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(str, size, format, ap); + va_end(ap); + + return count; +} + + +#else +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof(((type *) 0)->member) *__mptr = (ptr); \ + (type *) ((char *) __mptr - offsetof(type, member));}) +#endif +#endif + +#ifndef ABS +#define ABS(x) abs(x) +#endif + +#define g_free free +#define g_malloc malloc +#define g_new(type, num) ((type*)calloc(sizeof(type),num)) + +enum device_endian { + DEVICE_NATIVE_ENDIAN, + DEVICE_BIG_ENDIAN, + DEVICE_LITTLE_ENDIAN, +}; +enum vga_retrace_method { + VGA_RETRACE_DUMB, + VGA_RETRACE_PRECISE +}; +extern vga_retrace_method vga_retrace_method_value; + +typedef uint32_t QEMUClock; +extern QEMUClock *vm_clock; +static inline int64_t qemu_get_clock_ms(QEMUClock *clock) +{ + return 0; +} +static inline int64_t get_ticks_per_sec(void) +{ + return 1000000000LL; +} +int64_t qemu_get_clock_ns(QEMUClock *clock); + + +#define isa_mem_base 0 + +#define QemuConsole uint32_t +#define console_ch_t uint8_t +typedef struct GraphicHwOps { + void (*invalidate)(void *opaque); + void (*gfx_update)(void *opaque); + void (*text_update)(void *opaque, console_ch_t *text); + void (*update_interval)(void *opaque, uint64_t interval); +} GraphicHwOps; + +#define VMStateDescription uint32_t +#define hwaddr uint32_t +#define ram_addr_t uint32_t + +typedef struct DisplaySurface { + void *bah; +} DisplaySurface; + +uint16_t le16_to_cpu(uint16_t v); +uint32_t le32_to_cpu(uint32_t v); + +static inline void cpu_to_32wu(uint32_t *p, uint32_t v) +{ +} + +void graphic_hw_update(QemuConsole *con); +void qemu_console_copy(QemuConsole *con, int src_x, int src_y, + int dst_x, int dst_y, int w, int h); +void qemu_console_resize(QemuConsole *con, int width, int height); +DisplaySurface *qemu_console_surface(QemuConsole *con); +DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp, + int linesize, uint8_t *data, + bool byteswap); +int surface_stride(DisplaySurface *s); +uint8_t *surface_data(DisplaySurface *s); +int is_surface_bgr(DisplaySurface *surface); + +static inline int is_buffer_shared(DisplaySurface *surface) +{ + return 0; +} + +void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h); +void dpy_text_cursor(QemuConsole *con, int x, int y); +void dpy_text_update(QemuConsole *con, int x, int y, int w, int h); +void dpy_text_resize(QemuConsole *con, int w, int h); +void dpy_gfx_replace_surface(QemuConsole *con, + DisplaySurface *surface); + +static inline void console_write_ch(console_ch_t *dest, uint32_t ch) +{ + if (!(ch & 0xff)) + ch |= ' '; + *dest = ch; +} + +void qemu_flush_coalesced_mmio_buffer(void); + +int surface_bits_per_pixel(DisplaySurface *s); +int surface_bytes_per_pixel(DisplaySurface *s); + +typedef struct PortioList { + const struct MemoryRegionPortio *ports; + struct MemoryRegion *address_space; + unsigned nr; + struct MemoryRegion **regions; + struct MemoryRegion **aliases; + void *opaque; + const char *name; +} PortioList; + +void portio_list_init(PortioList *piolist, + const struct MemoryRegionPortio *callbacks, + void *opaque, const char *name); +void portio_list_destroy(PortioList *piolist); +void portio_list_add(PortioList *piolist, + struct MemoryRegion *address_space, + uint32_t addr); +void portio_list_del(PortioList *piolist); + + +typedef struct IORange IORange; +typedef struct IORangeOps IORangeOps; + +struct IORangeOps { + void (*read)(IORange *iorange, uint64_t offset, unsigned width, + uint64_t *data); + void (*write)(IORange *iorange, uint64_t offset, unsigned width, + uint64_t data); + void (*destructor)(IORange *iorange); +}; + +typedef struct IORange { + const IORangeOps *ops; + uint64_t base; + uint64_t len; +} IORange; + +typedef void (IOPortWriteFunc)(void *opaque, uint32_t address, uint32_t data); +typedef uint32_t (IOPortReadFunc)(void *opaque, uint32_t address); + +typedef void CPUWriteMemoryFunc(void *opaque, hwaddr addr, uint32_t value); +typedef uint32_t CPUReadMemoryFunc(void *opaque, hwaddr addr); + +#include "qemumemory.h" +#include "pixel_ops.h" + + +static inline uint32_t lduw_raw(void *p) +{ + return ((uint32_t*)p)[0]; +} + +typedef void QEMUResetHandler(void *opaque); +void qemu_register_reset(QEMUResetHandler *func, void *opaque); + +#include "vga_int.h" + +// ID +#define CIRRUS_ID_CLGD5422 (0x23<<2) +#define CIRRUS_ID_CLGD5426 (0x24<<2) +#define CIRRUS_ID_CLGD5424 (0x25<<2) +#define CIRRUS_ID_CLGD5428 (0x26<<2) +#define CIRRUS_ID_CLGD5430 (0x28<<2) +#define CIRRUS_ID_CLGD5434 (0x2A<<2) +#define CIRRUS_ID_CLGD5436 (0x2B<<2) +#define CIRRUS_ID_CLGD5446 (0x2E<<2) + +typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s, + uint8_t * dst, const uint8_t * src, + int dstpitch, int srcpitch, + int bltwidth, int bltheight); +typedef void (*cirrus_fill_t)(struct CirrusVGAState *s, + uint8_t *dst, int dst_pitch, int width, int height); + +typedef struct CirrusVGAState { + VGACommonState vga; + + MemoryRegion cirrus_vga_io; + MemoryRegion cirrus_linear_io; + MemoryRegion cirrus_linear_bitblt_io; + MemoryRegion cirrus_mmio_io; + MemoryRegion pci_bar; + bool linear_vram; /* vga.vram mapped over cirrus_linear_io */ + MemoryRegion low_mem_container; /* container for 0xa0000-0xc0000 */ + MemoryRegion low_mem; /* always mapped, overridden by: */ + MemoryRegion cirrus_bank[2]; /* aliases at 0xa0000-0xb0000 */ + uint32_t cirrus_addr_mask; + uint32_t linear_mmio_mask; + uint8_t cirrus_shadow_gr0; + uint8_t cirrus_shadow_gr1; + uint8_t cirrus_hidden_dac_lockindex; + uint8_t cirrus_hidden_dac_data; + uint32_t cirrus_bank_base[2]; + uint32_t cirrus_bank_limit[2]; + uint8_t cirrus_hidden_palette[48]; + uint32_t hw_cursor_x; + uint32_t hw_cursor_y; + int cirrus_blt_pixelwidth; + int cirrus_blt_width; + int cirrus_blt_height; + int cirrus_blt_dstpitch; + int cirrus_blt_srcpitch; + uint32_t cirrus_blt_fgcol; + uint32_t cirrus_blt_bgcol; + uint32_t cirrus_blt_dstaddr; + uint32_t cirrus_blt_srcaddr; + uint8_t cirrus_blt_mode; + uint8_t cirrus_blt_modeext; + cirrus_bitblt_rop_t cirrus_rop; +#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */ + uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE]; + uint8_t *cirrus_srcptr; + uint8_t *cirrus_srcptr_end; + uint32_t cirrus_srccounter; + /* hwcursor display state */ + int last_hw_cursor_size; + int last_hw_cursor_x; + int last_hw_cursor_y; + int last_hw_cursor_y_start; + int last_hw_cursor_y_end; + int real_vram_size; /* XXX: suppress that */ + int device_id; + int bustype; +} CirrusVGAState; + +void cirrus_init_common(CirrusVGAState * s, int device_id, int is_pci, + MemoryRegion *system_memory, + MemoryRegion *system_io); + diff --git a/qemuvga/vga.cpp b/qemuvga/vga.cpp new file mode 100644 index 00000000..e245aa8f --- /dev/null +++ b/qemuvga/vga.cpp @@ -0,0 +1,2415 @@ +/* + * QEMU VGA Emulator. + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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. + */ +#if 0 +#include "hw/hw.h" +#include "vga.h" +#include "ui/console.h" +#include "hw/i386/pc.h" +#include "hw/pci/pci.h" +#include "vga_int.h" +#include "ui/pixel_ops.h" +#include "qemu/timer.h" +#include "hw/xen/xen.h" +#include "trace.h" +#else +#include "qemuuaeglue.h" +#include "vga.h" +#include "vga_int.h" +#endif +//#define DEBUG_VGA +//#define DEBUG_VGA_MEM +//#define DEBUG_VGA_REG + +//#define DEBUG_BOCHS_VBE + +/* 16 state changes per vertical frame @60 Hz */ +#define VGA_TEXT_CURSOR_PERIOD_MS (1000 * 2 * 16 / 60) + +/* + * Video Graphics Array (VGA) + * + * Chipset docs for original IBM VGA: + * http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf + * + * FreeVGA site: + * http://www.osdever.net/FreeVGA/home.htm + * + * Standard VGA features and Bochs VBE extensions are implemented. + */ + +/* force some bits to zero */ +const uint8_t sr_mask[8] = { + 0x03, + 0x3d, + 0x0f, + 0x3f, + 0x0e, + 0x00, + 0x00, + 0xff, +}; + +const uint8_t gr_mask[16] = { + 0x0f, /* 0x00 */ + 0x0f, /* 0x01 */ + 0x0f, /* 0x02 */ + 0x1f, /* 0x03 */ + 0x03, /* 0x04 */ + 0x7b, /* 0x05 */ + 0x0f, /* 0x06 */ + 0x0f, /* 0x07 */ + 0xff, /* 0x08 */ + 0x00, /* 0x09 */ + 0x00, /* 0x0a */ + 0x00, /* 0x0b */ + 0x00, /* 0x0c */ + 0x00, /* 0x0d */ + 0x00, /* 0x0e */ + 0x00, /* 0x0f */ +}; + +#define cbswap_32(__x) \ +((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) + +#ifdef HOST_WORDS_BIGENDIAN +#define PAT(x) cbswap_32(x) +#else +#define PAT(x) (x) +#endif + +#ifdef HOST_WORDS_BIGENDIAN +#define BIG 1 +#else +#define BIG 0 +#endif + +#ifdef HOST_WORDS_BIGENDIAN +#define GET_PLANE(data, p) (((data) >> (24 - (p) * 8)) & 0xff) +#else +#define GET_PLANE(data, p) (((data) >> ((p) * 8)) & 0xff) +#endif + +static const uint32_t mask16[16] = { + PAT(0x00000000), + PAT(0x000000ff), + PAT(0x0000ff00), + PAT(0x0000ffff), + PAT(0x00ff0000), + PAT(0x00ff00ff), + PAT(0x00ffff00), + PAT(0x00ffffff), + PAT(0xff000000), + PAT(0xff0000ff), + PAT(0xff00ff00), + PAT(0xff00ffff), + PAT(0xffff0000), + PAT(0xffff00ff), + PAT(0xffffff00), + PAT(0xffffffff), +}; + +#undef PAT + +#ifdef HOST_WORDS_BIGENDIAN +#define PAT(x) (x) +#else +#define PAT(x) cbswap_32(x) +#endif + +static const uint32_t dmask16[16] = { + PAT(0x00000000), + PAT(0x000000ff), + PAT(0x0000ff00), + PAT(0x0000ffff), + PAT(0x00ff0000), + PAT(0x00ff00ff), + PAT(0x00ffff00), + PAT(0x00ffffff), + PAT(0xff000000), + PAT(0xff0000ff), + PAT(0xff00ff00), + PAT(0xff00ffff), + PAT(0xffff0000), + PAT(0xffff00ff), + PAT(0xffffff00), + PAT(0xffffffff), +}; + +static const uint32_t dmask4[4] = { + PAT(0x00000000), + PAT(0x0000ffff), + PAT(0xffff0000), + PAT(0xffffffff), +}; + +static uint32_t expand4[256]; +static uint16_t expand2[256]; +static uint8_t expand4to8[16]; + +static void vga_update_memory_access(VGACommonState *s) +{ + MemoryRegion *region, *old_region = s->chain4_alias; + hwaddr base, offset, size; + + s->chain4_alias = NULL; + + if ((s->sr[VGA_SEQ_PLANE_WRITE] & VGA_SR02_ALL_PLANES) == + VGA_SR02_ALL_PLANES && s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + offset = 0; + switch ((s->gr[VGA_GFX_MISC] >> 2) & 3) { + case 0: + base = 0xa0000; + size = 0x20000; + break; + case 1: + base = 0xa0000; + size = 0x10000; + offset = s->bank_offset; + break; + case 2: + base = 0xb0000; + size = 0x8000; + break; + case 3: + default: + base = 0xb8000; + size = 0x8000; + break; + } + base += isa_mem_base; + region = (MemoryRegion*)g_malloc(sizeof(*region)); + memory_region_init_alias(region, "vga.chain4", &s->vram, offset, size); + memory_region_add_subregion_overlap(s->legacy_address_space, base, + region, 2); + s->chain4_alias = region; + } + if (old_region) { + memory_region_del_subregion(s->legacy_address_space, old_region); + memory_region_destroy(old_region); + g_free(old_region); + s->plane_updated = 0xf; + } +} + +static void vga_dumb_update_retrace_info(VGACommonState *s) +{ + (void) s; +} + +static void vga_precise_update_retrace_info(VGACommonState *s) +{ + int htotal_chars; + int hretr_start_char; + int hretr_skew_chars; + int hretr_end_char; + + int vtotal_lines; + int vretr_start_line; + int vretr_end_line; + + int dots; +#if 0 + int div2, sldiv2; +#endif + int clocking_mode; + int clock_sel; + const int clk_hz[] = {25175000, 28322000, 25175000, 25175000}; + int64_t chars_per_sec; + struct vga_precise_retrace *r = &s->retrace_info.precise; + + htotal_chars = s->cr[VGA_CRTC_H_TOTAL] + 5; + hretr_start_char = s->cr[VGA_CRTC_H_SYNC_START]; + hretr_skew_chars = (s->cr[VGA_CRTC_H_SYNC_END] >> 5) & 3; + hretr_end_char = s->cr[VGA_CRTC_H_SYNC_END] & 0x1f; + + vtotal_lines = (s->cr[VGA_CRTC_V_TOTAL] | + (((s->cr[VGA_CRTC_OVERFLOW] & 1) | + ((s->cr[VGA_CRTC_OVERFLOW] >> 4) & 2)) << 8)) + 2; + vretr_start_line = s->cr[VGA_CRTC_V_SYNC_START] | + ((((s->cr[VGA_CRTC_OVERFLOW] >> 2) & 1) | + ((s->cr[VGA_CRTC_OVERFLOW] >> 6) & 2)) << 8); + vretr_end_line = s->cr[VGA_CRTC_V_SYNC_END] & 0xf; + + clocking_mode = (s->sr[VGA_SEQ_CLOCK_MODE] >> 3) & 1; + clock_sel = (s->msr >> 2) & 3; + dots = (s->msr & 1) ? 8 : 9; + + chars_per_sec = clk_hz[clock_sel] / dots; + + htotal_chars <<= clocking_mode; + + r->total_chars = vtotal_lines * htotal_chars; + if (r->freq) { + r->ticks_per_char = get_ticks_per_sec() / (r->total_chars * r->freq); + } else { + r->ticks_per_char = get_ticks_per_sec() / chars_per_sec; + } + + r->vstart = vretr_start_line; + r->vend = r->vstart + vretr_end_line + 1; + + r->hstart = hretr_start_char + hretr_skew_chars; + r->hend = r->hstart + hretr_end_char + 1; + r->htotal = htotal_chars; + +#if 0 + div2 = (s->cr[VGA_CRTC_MODE] >> 2) & 1; + sldiv2 = (s->cr[VGA_CRTC_MODE] >> 3) & 1; + write_log ( + "hz=%f\n" + "htotal = %d\n" + "hretr_start = %d\n" + "hretr_skew = %d\n" + "hretr_end = %d\n" + "vtotal = %d\n" + "vretr_start = %d\n" + "vretr_end = %d\n" + "div2 = %d sldiv2 = %d\n" + "clocking_mode = %d\n" + "clock_sel = %d %d\n" + "dots = %d\n" + "ticks/char = %" PRId64 "\n" + "\n", + (double) get_ticks_per_sec() / (r->ticks_per_char * r->total_chars), + htotal_chars, + hretr_start_char, + hretr_skew_chars, + hretr_end_char, + vtotal_lines, + vretr_start_line, + vretr_end_line, + div2, sldiv2, + clocking_mode, + clock_sel, + clk_hz[clock_sel], + dots, + r->ticks_per_char + ); +#endif +} + +static uint8_t vga_precise_retrace(VGACommonState *s) +{ + struct vga_precise_retrace *r = &s->retrace_info.precise; + uint8_t val = s->st01 & ~(ST01_V_RETRACE | ST01_DISP_ENABLE); + + if (r->total_chars) { + int cur_line, cur_line_char, cur_char; + int64_t cur_tick; + + cur_tick = qemu_get_clock_ns(vm_clock); + + cur_char = (cur_tick / r->ticks_per_char) % r->total_chars; + cur_line = cur_char / r->htotal; + + if (cur_line >= r->vstart && cur_line <= r->vend) { + val |= ST01_V_RETRACE | ST01_DISP_ENABLE; + } else { + cur_line_char = cur_char % r->htotal; + if (cur_line_char >= r->hstart && cur_line_char <= r->hend) { + val |= ST01_DISP_ENABLE; + } + } + + return val; + } else { + return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); + } +} + +static uint8_t vga_dumb_retrace(VGACommonState *s) +{ + return s->st01 ^ (ST01_V_RETRACE | ST01_DISP_ENABLE); +} + +int vga_ioport_invalid(VGACommonState *s, uint32_t addr) +{ + if (s->msr & VGA_MIS_COLOR) { + /* Color */ + return (addr >= 0x3b0 && addr <= 0x3bf); + } else { + /* Monochrome */ + return (addr >= 0x3d0 && addr <= 0x3df); + } +} + +uint32_t vga_ioport_read(void *opaque, uint32_t addr) +{ + VGACommonState *s = (VGACommonState*)opaque; + int val, index; + + qemu_flush_coalesced_mmio_buffer(); + + if (vga_ioport_invalid(s, addr)) { + val = 0xff; + } else { + switch(addr) { + case VGA_ATT_W: + if (s->ar_flip_flop == 0) { + val = s->ar_index; + } else { + val = 0; + } + break; + case VGA_ATT_R: + index = s->ar_index & 0x1f; + if (index < VGA_ATT_C) { + val = s->ar[index]; + } else { + val = 0; + } + break; + case VGA_MIS_W: + val = s->st00; + break; + case VGA_SEQ_I: + val = s->sr_index; + break; + case VGA_SEQ_D: + val = s->sr[s->sr_index]; +#ifdef DEBUG_VGA_REG + write_log("vga: read SR%x = 0x%02x\n", s->sr_index, val); +#endif + break; + case VGA_PEL_IR: + val = s->dac_state; + break; + case VGA_PEL_IW: + val = s->dac_write_index; + break; + case VGA_PEL_D: + val = s->palette[s->dac_read_index * 3 + s->dac_sub_index]; + if (++s->dac_sub_index == 3) { + s->dac_sub_index = 0; + s->dac_read_index++; + } + break; + case VGA_FTC_R: + val = s->fcr; + break; + case VGA_MIS_R: + val = s->msr; + break; + case VGA_GFX_I: + val = s->gr_index; + break; + case VGA_GFX_D: + val = s->gr[s->gr_index]; +#ifdef DEBUG_VGA_REG + write_log("vga: read GR%x = 0x%02x\n", s->gr_index, val); +#endif + break; + case VGA_CRT_IM: + case VGA_CRT_IC: + val = s->cr_index; + break; + case VGA_CRT_DM: + case VGA_CRT_DC: + val = s->cr[s->cr_index]; +#ifdef DEBUG_VGA_REG + write_log("vga: read CR%x = 0x%02x\n", s->cr_index, val); +#endif + break; + case VGA_IS1_RM: + case VGA_IS1_RC: + /* just toggle to fool polling */ + val = s->st01 = s->retrace(s); + s->ar_flip_flop = 0; + break; + default: + val = 0x00; + break; + } + } +#if defined(DEBUG_VGA) + write_log("VGA: read addr=0x%04x data=0x%02x\n", addr, val); +#endif + return val; +} + +void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val) +{ + VGACommonState *s = (VGACommonState*)opaque; + int index; + + qemu_flush_coalesced_mmio_buffer(); + + /* check port range access depending on color/monochrome mode */ + if (vga_ioport_invalid(s, addr)) { + return; + } +#ifdef DEBUG_VGA + write_log("VGA: write addr=0x%04x data=0x%02x\n", addr, val); +#endif + + switch(addr) { + case VGA_ATT_W: + if (s->ar_flip_flop == 0) { + val &= 0x3f; + s->ar_index = val; + } else { + index = s->ar_index & 0x1f; + switch(index) { + case VGA_ATC_PALETTE0: + case VGA_ATC_PALETTE1: + case VGA_ATC_PALETTE2: + case VGA_ATC_PALETTE3: + case VGA_ATC_PALETTE4: + case VGA_ATC_PALETTE5: + case VGA_ATC_PALETTE6: + case VGA_ATC_PALETTE7: + case VGA_ATC_PALETTE8: + case VGA_ATC_PALETTE9: + case VGA_ATC_PALETTEA: + case VGA_ATC_PALETTEB: + case VGA_ATC_PALETTEC: + case VGA_ATC_PALETTED: + case VGA_ATC_PALETTEE: + case VGA_ATC_PALETTEF: + s->ar[index] = val & 0x3f; + break; + case VGA_ATC_MODE: + s->ar[index] = val & ~0x10; + break; + case VGA_ATC_OVERSCAN: + s->ar[index] = val; + break; + case VGA_ATC_PLANE_ENABLE: + s->ar[index] = val & ~0xc0; + break; + case VGA_ATC_PEL: + s->ar[index] = val & ~0xf0; + break; + case VGA_ATC_COLOR_PAGE: + s->ar[index] = val & ~0xf0; + break; + default: + break; + } + } + s->ar_flip_flop ^= 1; + break; + case VGA_MIS_W: + s->msr = val & ~0x10; + s->update_retrace_info(s); + break; + case VGA_SEQ_I: + s->sr_index = val & 7; + break; + case VGA_SEQ_D: +#ifdef DEBUG_VGA_REG + write_log("vga: write SR%x = 0x%02x\n", s->sr_index, val); +#endif + s->sr[s->sr_index] = val & sr_mask[s->sr_index]; + if (s->sr_index == VGA_SEQ_CLOCK_MODE) { + s->update_retrace_info(s); + } + vga_update_memory_access(s); + break; + case VGA_PEL_IR: + s->dac_read_index = val; + s->dac_sub_index = 0; + s->dac_state = 3; + break; + case VGA_PEL_IW: + s->dac_write_index = val; + s->dac_sub_index = 0; + s->dac_state = 0; + break; + case VGA_PEL_D: + s->dac_cache[s->dac_sub_index] = val; + if (++s->dac_sub_index == 3) { + memcpy(&s->palette[s->dac_write_index * 3], s->dac_cache, 3); + s->dac_sub_index = 0; + s->dac_write_index++; + } + break; + case VGA_GFX_I: + s->gr_index = val & 0x0f; + break; + case VGA_GFX_D: +#ifdef DEBUG_VGA_REG + write_log("vga: write GR%x = 0x%02x\n", s->gr_index, val); +#endif + s->gr[s->gr_index] = val & gr_mask[s->gr_index]; + vga_update_memory_access(s); + break; + case VGA_CRT_IM: + case VGA_CRT_IC: + s->cr_index = val; + break; + case VGA_CRT_DM: + case VGA_CRT_DC: +#ifdef DEBUG_VGA_REG + write_log("vga: write CR%x = 0x%02x\n", s->cr_index, val); +#endif + /* handle CR0-7 protection */ + if ((s->cr[VGA_CRTC_V_SYNC_END] & VGA_CR11_LOCK_CR0_CR7) && + s->cr_index <= VGA_CRTC_OVERFLOW) { + /* can always write bit 4 of CR7 */ + if (s->cr_index == VGA_CRTC_OVERFLOW) { + s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x10) | + (val & 0x10); + } + return; + } + s->cr[s->cr_index] = val; + + switch(s->cr_index) { + case VGA_CRTC_H_TOTAL: + case VGA_CRTC_H_SYNC_START: + case VGA_CRTC_H_SYNC_END: + case VGA_CRTC_V_TOTAL: + case VGA_CRTC_OVERFLOW: + case VGA_CRTC_V_SYNC_END: + case VGA_CRTC_MODE: + s->update_retrace_info(s); + break; + } + break; + case VGA_IS1_RM: + case VGA_IS1_RC: + s->fcr = val & 0x10; + break; + } +} + +static uint32_t vbe_ioport_read_index(void *opaque, uint32_t addr) +{ + VGACommonState *s = (VGACommonState*)opaque; + uint32_t val; + val = s->vbe_index; + return val; +} + +uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr) +{ + VGACommonState *s = (VGACommonState*)opaque; + uint32_t val; + + if (s->vbe_index < VBE_DISPI_INDEX_NB) { + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_GETCAPS) { + switch(s->vbe_index) { + /* XXX: do not hardcode ? */ + case VBE_DISPI_INDEX_XRES: + val = VBE_DISPI_MAX_XRES; + break; + case VBE_DISPI_INDEX_YRES: + val = VBE_DISPI_MAX_YRES; + break; + case VBE_DISPI_INDEX_BPP: + val = VBE_DISPI_MAX_BPP; + break; + default: + val = s->vbe_regs[s->vbe_index]; + break; + } + } else { + val = s->vbe_regs[s->vbe_index]; + } + } else if (s->vbe_index == VBE_DISPI_INDEX_VIDEO_MEMORY_64K) { + val = s->vram_size / (64 * 1024); + } else { + val = 0; + } +#ifdef DEBUG_BOCHS_VBE + write_log("VBE: read index=0x%x val=0x%x\n", s->vbe_index, val); +#endif + return val; +} + +void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val) +{ + VGACommonState *s = (VGACommonState*)opaque; + s->vbe_index = val; +} + +void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val) +{ + VGACommonState *s = (VGACommonState*)opaque; + + if (s->vbe_index <= VBE_DISPI_INDEX_NB) { +#ifdef DEBUG_BOCHS_VBE + write_log("VBE: write index=0x%x val=0x%x\n", s->vbe_index, val); +#endif + switch(s->vbe_index) { + case VBE_DISPI_INDEX_ID: + if (val == VBE_DISPI_ID0 || + val == VBE_DISPI_ID1 || + val == VBE_DISPI_ID2 || + val == VBE_DISPI_ID3 || + val == VBE_DISPI_ID4) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_XRES: + if ((val <= VBE_DISPI_MAX_XRES) && ((val & 7) == 0)) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_YRES: + if (val <= VBE_DISPI_MAX_YRES) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_BPP: + if (val == 0) + val = 8; + if (val == 4 || val == 8 || val == 15 || + val == 16 || val == 24 || val == 32) { + s->vbe_regs[s->vbe_index] = val; + } + break; + case VBE_DISPI_INDEX_BANK: + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { + val &= (s->vbe_bank_mask >> 2); + } else { + val &= s->vbe_bank_mask; + } + s->vbe_regs[s->vbe_index] = val; + s->bank_offset = (val << 16); + vga_update_memory_access(s); + break; + case VBE_DISPI_INDEX_ENABLE: + if ((val & VBE_DISPI_ENABLED) && + !(s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED)) { + int h, shift_control; + + s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = + s->vbe_regs[VBE_DISPI_INDEX_XRES]; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = + s->vbe_regs[VBE_DISPI_INDEX_YRES]; + s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET] = 0; + s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET] = 0; + + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) + s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 1; + else + s->vbe_line_offset = s->vbe_regs[VBE_DISPI_INDEX_XRES] * + ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); + s->vbe_start_addr = 0; + + /* clear the screen (should be done in BIOS) */ + if (!(val & VBE_DISPI_NOCLEARMEM)) { + memset(s->vram_ptr, 0, + s->vbe_regs[VBE_DISPI_INDEX_YRES] * s->vbe_line_offset); + } + + /* we initialize the VGA graphic mode (should be done + in BIOS) */ + /* graphic mode + memory map 1 */ + s->gr[VGA_GFX_MISC] = (s->gr[VGA_GFX_MISC] & ~0x0c) | 0x04 | + VGA_GR06_GRAPHICS_MODE; + s->cr[VGA_CRTC_MODE] |= 3; /* no CGA modes */ + s->cr[VGA_CRTC_OFFSET] = s->vbe_line_offset >> 3; + /* width */ + s->cr[VGA_CRTC_H_DISP] = + (s->vbe_regs[VBE_DISPI_INDEX_XRES] >> 3) - 1; + /* height (only meaningful if < 1024) */ + h = s->vbe_regs[VBE_DISPI_INDEX_YRES] - 1; + s->cr[VGA_CRTC_V_DISP_END] = h; + s->cr[VGA_CRTC_OVERFLOW] = (s->cr[VGA_CRTC_OVERFLOW] & ~0x42) | + ((h >> 7) & 0x02) | ((h >> 3) & 0x40); + /* line compare to 1023 */ + s->cr[VGA_CRTC_LINE_COMPARE] = 0xff; + s->cr[VGA_CRTC_OVERFLOW] |= 0x10; + s->cr[VGA_CRTC_MAX_SCAN] |= 0x40; + + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) { + shift_control = 0; + s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */ + } else { + shift_control = 2; + /* set chain 4 mode */ + s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M; + /* activate all planes */ + s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES; + } + s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) | + (shift_control << 5); + s->cr[VGA_CRTC_MAX_SCAN] &= ~0x9f; /* no double scan */ + } else { + /* XXX: the bios should do that */ + s->bank_offset = 0; + } + s->dac_8bit = (val & VBE_DISPI_8BIT_DAC) > 0; + s->vbe_regs[s->vbe_index] = val; + vga_update_memory_access(s); + break; + case VBE_DISPI_INDEX_VIRT_WIDTH: + { + int w, h, line_offset; + + if (val < s->vbe_regs[VBE_DISPI_INDEX_XRES]) + return; + w = val; + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) + line_offset = w >> 1; + else + line_offset = w * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); + h = s->vram_size / line_offset; + /* XXX: support weird bochs semantics ? */ + if (h < s->vbe_regs[VBE_DISPI_INDEX_YRES]) + return; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_WIDTH] = w; + s->vbe_regs[VBE_DISPI_INDEX_VIRT_HEIGHT] = h; + s->vbe_line_offset = line_offset; + } + break; + case VBE_DISPI_INDEX_X_OFFSET: + case VBE_DISPI_INDEX_Y_OFFSET: + { + int x; + s->vbe_regs[s->vbe_index] = val; + s->vbe_start_addr = s->vbe_line_offset * s->vbe_regs[VBE_DISPI_INDEX_Y_OFFSET]; + x = s->vbe_regs[VBE_DISPI_INDEX_X_OFFSET]; + if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) + s->vbe_start_addr += x >> 1; + else + s->vbe_start_addr += x * ((s->vbe_regs[VBE_DISPI_INDEX_BPP] + 7) >> 3); + s->vbe_start_addr >>= 2; + } + break; + default: + break; + } + } +} + +/* called for accesses between 0xa0000 and 0xc0000 */ +uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr) +{ + int memory_map_mode, plane; + uint32_t ret; + + /* convert to VGA memory offset */ + memory_map_mode = (s->gr[VGA_GFX_MISC] >> 2) & 3; + addr &= 0x1ffff; + switch(memory_map_mode) { + case 0: + break; + case 1: + if (addr >= 0x10000) + return 0xff; + addr += s->bank_offset; + break; + case 2: + addr -= 0x10000; + if (addr >= 0x8000) + return 0xff; + break; + default: + case 3: + addr -= 0x18000; + if (addr >= 0x8000) + return 0xff; + break; + } + + if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + /* chain 4 mode : simplest access */ + ret = s->vram_ptr[addr]; + } else if (s->gr[VGA_GFX_MODE] & 0x10) { + /* odd/even mode (aka text mode mapping) */ + plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); + ret = s->vram_ptr[((addr & ~1) << 1) | plane]; + } else { + /* standard VGA latched access */ + s->latch = ((uint32_t *)s->vram_ptr)[addr]; + + if (!(s->gr[VGA_GFX_MODE] & 0x08)) { + /* read mode 0 */ + plane = s->gr[VGA_GFX_PLANE_READ]; + ret = GET_PLANE(s->latch, plane); + } else { + /* read mode 1 */ + ret = (s->latch ^ mask16[s->gr[VGA_GFX_COMPARE_VALUE]]) & + mask16[s->gr[VGA_GFX_COMPARE_MASK]]; + ret |= ret >> 16; + ret |= ret >> 8; + ret = (~ret) & 0xff; + } + } + return ret; +} + +/* called for accesses between 0xa0000 and 0xc0000 */ +void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) +{ + int memory_map_mode, plane, write_mode, b, func_select, mask; + uint32_t write_mask, bit_mask, set_mask; + +#ifdef DEBUG_VGA_MEM + write_log("vga: [0x" TARGET_FMT_plx "] = 0x%02x\n", addr, val); +#endif + /* convert to VGA memory offset */ + memory_map_mode = (s->gr[VGA_GFX_MISC] >> 2) & 3; + addr &= 0x1ffff; + switch(memory_map_mode) { + case 0: + break; + case 1: + if (addr >= 0x10000) + return; + addr += s->bank_offset; + break; + case 2: + addr -= 0x10000; + if (addr >= 0x8000) + return; + break; + default: + case 3: + addr -= 0x18000; + if (addr >= 0x8000) + return; + break; + } + + if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) { + /* chain 4 mode : simplest access */ + plane = addr & 3; + mask = (1 << plane); + if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { + s->vram_ptr[addr] = val; +#ifdef DEBUG_VGA_MEM + write_log("vga: chain4: [0x" TARGET_FMT_plx "]\n", addr); +#endif + s->plane_updated |= mask; /* only used to detect font change */ + memory_region_set_dirty(&s->vram, addr, 1); + } + } else if (s->gr[VGA_GFX_MODE] & 0x10) { + /* odd/even mode (aka text mode mapping) */ + plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1); + mask = (1 << plane); + if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) { + addr = ((addr & ~1) << 1) | plane; + s->vram_ptr[addr] = val; +#ifdef DEBUG_VGA_MEM + write_log("vga: odd/even: [0x" TARGET_FMT_plx "]\n", addr); +#endif + s->plane_updated |= mask; /* only used to detect font change */ + memory_region_set_dirty(&s->vram, addr, 1); + } + } else { + /* standard VGA latched access */ + write_mode = s->gr[VGA_GFX_MODE] & 3; + switch(write_mode) { + default: + case 0: + /* rotate */ + b = s->gr[VGA_GFX_DATA_ROTATE] & 7; + val = ((val >> b) | (val << (8 - b))) & 0xff; + val |= val << 8; + val |= val << 16; + + /* apply set/reset mask */ + set_mask = mask16[s->gr[VGA_GFX_SR_ENABLE]]; + val = (val & ~set_mask) | + (mask16[s->gr[VGA_GFX_SR_VALUE]] & set_mask); + bit_mask = s->gr[VGA_GFX_BIT_MASK]; + break; + case 1: + val = s->latch; + goto do_write; + case 2: + val = mask16[val & 0x0f]; + bit_mask = s->gr[VGA_GFX_BIT_MASK]; + break; + case 3: + /* rotate */ + b = s->gr[VGA_GFX_DATA_ROTATE] & 7; + val = (val >> b) | (val << (8 - b)); + + bit_mask = s->gr[VGA_GFX_BIT_MASK] & val; + val = mask16[s->gr[VGA_GFX_SR_VALUE]]; + break; + } + + /* apply logical operation */ + func_select = s->gr[VGA_GFX_DATA_ROTATE] >> 3; + switch(func_select) { + case 0: + default: + /* nothing to do */ + break; + case 1: + /* and */ + val &= s->latch; + break; + case 2: + /* or */ + val |= s->latch; + break; + case 3: + /* xor */ + val ^= s->latch; + break; + } + + /* apply bit mask */ + bit_mask |= bit_mask << 8; + bit_mask |= bit_mask << 16; + val = (val & bit_mask) | (s->latch & ~bit_mask); + + do_write: + /* mask data according to sr[2] */ + mask = s->sr[VGA_SEQ_PLANE_WRITE]; + s->plane_updated |= mask; /* only used to detect font change */ + write_mask = mask16[mask]; + ((uint32_t *)s->vram_ptr)[addr] = + (((uint32_t *)s->vram_ptr)[addr] & ~write_mask) | + (val & write_mask); +#ifdef DEBUG_VGA_MEM + write_log("vga: latch: [0x" TARGET_FMT_plx "] mask=0x%08x val=0x%08x\n", + addr * 4, write_mask, val); +#endif + memory_region_set_dirty(&s->vram, addr << 2, sizeof(uint32_t)); + } +} + +typedef void vga_draw_glyph8_func(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol); +typedef void vga_draw_glyph9_func(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol, int dup9); +typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width); + +#define DEPTH 8 +#include "vga_template.h" + +#define DEPTH 15 +#include "vga_template.h" + +#define BGR_FORMAT +#define DEPTH 15 +#include "vga_template.h" + +#define DEPTH 16 +#include "vga_template.h" + +#define BGR_FORMAT +#define DEPTH 16 +#include "vga_template.h" + +#define DEPTH 32 +#include "vga_template.h" + +#define BGR_FORMAT +#define DEPTH 32 +#include "vga_template.h" + +static unsigned int rgb_to_pixel8_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel8(r, g, b); + col |= col << 8; + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel15_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel15(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel15bgr_dup(unsigned int r, unsigned int g, + unsigned int b) +{ + unsigned int col; + col = rgb_to_pixel15bgr(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel16_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel16(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel16bgr_dup(unsigned int r, unsigned int g, + unsigned int b) +{ + unsigned int col; + col = rgb_to_pixel16bgr(r, g, b); + col |= col << 16; + return col; +} + +static unsigned int rgb_to_pixel32_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel32(r, g, b); + return col; +} + +static unsigned int rgb_to_pixel32bgr_dup(unsigned int r, unsigned int g, unsigned b) +{ + unsigned int col; + col = rgb_to_pixel32bgr(r, g, b); + return col; +} + +/* return true if the palette was modified */ +static int update_palette16(VGACommonState *s) +{ + int full_update, i; + uint32_t v, col, *palette; + + full_update = 0; + palette = s->last_palette; + for(i = 0; i < 16; i++) { + v = s->ar[i]; + if (s->ar[VGA_ATC_MODE] & 0x80) { + v = ((s->ar[VGA_ATC_COLOR_PAGE] & 0xf) << 4) | (v & 0xf); + } else { + v = ((s->ar[VGA_ATC_COLOR_PAGE] & 0xc) << 4) | (v & 0x3f); + } + v = v * 3; + col = s->rgb_to_pixel(c6_to_8(s->palette[v]), + c6_to_8(s->palette[v + 1]), + c6_to_8(s->palette[v + 2])); + if (col != palette[i]) { + full_update = 1; + palette[i] = col; + } + } + return full_update; +} + +/* return true if the palette was modified */ +static int update_palette256(VGACommonState *s) +{ + int full_update, i; + uint32_t v, col, *palette; + + full_update = 0; + palette = s->last_palette; + v = 0; + for(i = 0; i < 256; i++) { + if (s->dac_8bit) { + col = s->rgb_to_pixel(s->palette[v], + s->palette[v + 1], + s->palette[v + 2]); + } else { + col = s->rgb_to_pixel(c6_to_8(s->palette[v]), + c6_to_8(s->palette[v + 1]), + c6_to_8(s->palette[v + 2])); + } + if (col != palette[i]) { + full_update = 1; + palette[i] = col; + } + v += 3; + } + return full_update; +} + +static void vga_get_offsets(VGACommonState *s, + uint32_t *pline_offset, + uint32_t *pstart_addr, + uint32_t *pline_compare) +{ + uint32_t start_addr, line_offset, line_compare; + + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + line_offset = s->vbe_line_offset; + start_addr = s->vbe_start_addr; + line_compare = 65535; + } else { + /* compute line_offset in bytes */ + line_offset = s->cr[VGA_CRTC_OFFSET]; + line_offset <<= 3; + + /* starting address */ + start_addr = s->cr[VGA_CRTC_START_LO] | + (s->cr[VGA_CRTC_START_HI] << 8); + + /* line compare */ + line_compare = s->cr[VGA_CRTC_LINE_COMPARE] | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x10) << 4) | + ((s->cr[VGA_CRTC_MAX_SCAN] & 0x40) << 3); + } + *pline_offset = line_offset; + *pstart_addr = start_addr; + *pline_compare = line_compare; +} + +/* update start_addr and line_offset. Return TRUE if modified */ +static int update_basic_params(VGACommonState *s) +{ + int full_update; + uint32_t start_addr, line_offset, line_compare; + + full_update = 0; + + s->get_offsets(s, &line_offset, &start_addr, &line_compare); + + if (line_offset != s->line_offset || + start_addr != s->start_addr || + line_compare != s->line_compare) { + s->line_offset = line_offset; + s->start_addr = start_addr; + s->line_compare = line_compare; + full_update = 1; + } + return full_update; +} + +#define NB_DEPTHS 7 + +static inline int get_depth_index(DisplaySurface *s) +{ + switch (surface_bits_per_pixel(s)) { + default: + case 8: + return 0; + case 15: + return 1; + case 16: + return 2; + case 32: + if (is_surface_bgr(s)) { + return 4; + } else { + return 3; + } + } +} + +static vga_draw_glyph8_func * const vga_draw_glyph8_table[NB_DEPTHS] = { + vga_draw_glyph8_8, + vga_draw_glyph8_16, + vga_draw_glyph8_16, + vga_draw_glyph8_32, + vga_draw_glyph8_32, + vga_draw_glyph8_16, + vga_draw_glyph8_16, +}; + +static vga_draw_glyph8_func * const vga_draw_glyph16_table[NB_DEPTHS] = { + vga_draw_glyph16_8, + vga_draw_glyph16_16, + vga_draw_glyph16_16, + vga_draw_glyph16_32, + vga_draw_glyph16_32, + vga_draw_glyph16_16, + vga_draw_glyph16_16, +}; + +static vga_draw_glyph9_func * const vga_draw_glyph9_table[NB_DEPTHS] = { + vga_draw_glyph9_8, + vga_draw_glyph9_16, + vga_draw_glyph9_16, + vga_draw_glyph9_32, + vga_draw_glyph9_32, + vga_draw_glyph9_16, + vga_draw_glyph9_16, +}; + +static const uint8_t cursor_glyph[32 * 4] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static void vga_get_text_resolution(VGACommonState *s, int *pwidth, int *pheight, + int *pcwidth, int *pcheight) +{ + int width, cwidth, height, cheight; + + /* total width & height */ + cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; + cwidth = 8; + if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { + cwidth = 9; + } + if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { + cwidth = 16; /* NOTE: no 18 pixel wide */ + } + width = (s->cr[VGA_CRTC_H_DISP] + 1); + if (s->cr[VGA_CRTC_V_TOTAL] == 100) { + /* ugly hack for CGA 160x100x16 - explain me the logic */ + height = 100; + } else { + height = s->cr[VGA_CRTC_V_DISP_END] | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); + height = (height + 1) / cheight; + } + + *pwidth = width; + *pheight = height; + *pcwidth = cwidth; + *pcheight = cheight; +} + +typedef unsigned int rgb_to_pixel_dup_func(unsigned int r, unsigned int g, unsigned b); + +static rgb_to_pixel_dup_func * const rgb_to_pixel_dup_table[NB_DEPTHS] = { + rgb_to_pixel8_dup, + rgb_to_pixel15_dup, + rgb_to_pixel16_dup, + rgb_to_pixel32_dup, + rgb_to_pixel32bgr_dup, + rgb_to_pixel15bgr_dup, + rgb_to_pixel16bgr_dup, +}; + +/* + * Text mode update + * Missing: + * - double scan + * - double width + * - underline + * - flashing + */ +static void vga_draw_text(VGACommonState *s, int full_update) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int cx, cy, cheight, cw, ch, cattr, height, width, ch_attr; + int cx_min, cx_max, linesize, x_incr, line, line1; + uint32_t offset, fgcol, bgcol, v, cursor_offset; + uint8_t *d1, *d, *src, *dest, *cursor_ptr; + const uint8_t *font_ptr, *font_base[2]; + int dup9, line_offset, depth_index; + uint32_t *palette; + uint32_t *ch_attr_ptr; + vga_draw_glyph8_func *vga_draw_glyph8; + vga_draw_glyph9_func *vga_draw_glyph9; + int64_t now = qemu_get_clock_ms(vm_clock); + + /* compute font data address (in plane 2) */ + v = s->sr[VGA_SEQ_CHARACTER_MAP]; + offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2; + if (offset != s->font_offsets[0]) { + s->font_offsets[0] = offset; + full_update = 1; + } + font_base[0] = s->vram_ptr + offset; + + offset = (((v >> 5) & 1) | ((v >> 1) & 6)) * 8192 * 4 + 2; + font_base[1] = s->vram_ptr + offset; + if (offset != s->font_offsets[1]) { + s->font_offsets[1] = offset; + full_update = 1; + } + if (s->plane_updated & (1 << 2) || s->chain4_alias) { + /* if the plane 2 was modified since the last display, it + indicates the font may have been modified */ + s->plane_updated = 0; + full_update = 1; + } + full_update |= update_basic_params(s); + + line_offset = s->line_offset; + + vga_get_text_resolution(s, &width, &height, &cw, &cheight); + if ((height * width) <= 1) { + /* better than nothing: exit if transient size is too small */ + return; + } + if ((height * width) > CH_ATTR_SIZE) { + /* better than nothing: exit if transient size is too big */ + return; + } + + if (width != s->last_width || height != s->last_height || + cw != s->last_cw || cheight != s->last_ch || s->last_depth) { + s->last_scr_width = width * cw; + s->last_scr_height = height * cheight; + qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); + surface = qemu_console_surface(s->con); + dpy_text_resize(s->con, width, height); + s->last_depth = 0; + s->last_width = width; + s->last_height = height; + s->last_ch = cheight; + s->last_cw = cw; + full_update = 1; + } + s->rgb_to_pixel = + rgb_to_pixel_dup_table[get_depth_index(surface)]; + full_update |= update_palette16(s); + palette = s->last_palette; + x_incr = cw * surface_bytes_per_pixel(surface); + + if (full_update) { + s->full_update_text = 1; + } + if (s->full_update_gfx) { + s->full_update_gfx = 0; + full_update |= 1; + } + + cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | + s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; + if (cursor_offset != s->cursor_offset || + s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || + s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end) { + /* if the cursor position changed, we update the old and new + chars */ + if (s->cursor_offset < CH_ATTR_SIZE) + s->last_ch_attr[s->cursor_offset] = -1; + if (cursor_offset < CH_ATTR_SIZE) + s->last_ch_attr[cursor_offset] = -1; + s->cursor_offset = cursor_offset; + s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; + s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; + } + cursor_ptr = s->vram_ptr + (s->start_addr + cursor_offset) * 4; + if (now >= s->cursor_blink_time) { + s->cursor_blink_time = now + VGA_TEXT_CURSOR_PERIOD_MS / 2; + s->cursor_visible_phase = !s->cursor_visible_phase; + } + + depth_index = get_depth_index(surface); + if (cw == 16) + vga_draw_glyph8 = vga_draw_glyph16_table[depth_index]; + else + vga_draw_glyph8 = vga_draw_glyph8_table[depth_index]; + vga_draw_glyph9 = vga_draw_glyph9_table[depth_index]; + + dest = surface_data(surface); + linesize = surface_stride(surface); + ch_attr_ptr = s->last_ch_attr; + line = 0; + offset = s->start_addr * 4; + for(cy = 0; cy < height; cy++) { + d1 = dest; + src = s->vram_ptr + offset; + cx_min = width; + cx_max = -1; + for(cx = 0; cx < width; cx++) { + ch_attr = *(uint16_t *)src; + if (full_update || ch_attr != *ch_attr_ptr || src == cursor_ptr) { + if (cx < cx_min) + cx_min = cx; + if (cx > cx_max) + cx_max = cx; + *ch_attr_ptr = ch_attr; +#ifdef HOST_WORDS_BIGENDIAN + ch = ch_attr >> 8; + cattr = ch_attr & 0xff; +#else + ch = ch_attr & 0xff; + cattr = ch_attr >> 8; +#endif + font_ptr = font_base[(cattr >> 3) & 1]; + font_ptr += 32 * 4 * ch; + bgcol = palette[cattr >> 4]; + fgcol = palette[cattr & 0x0f]; + if (cw != 9) { + vga_draw_glyph8(d1, linesize, + font_ptr, cheight, fgcol, bgcol); + } else { + dup9 = 0; + if (ch >= 0xb0 && ch <= 0xdf && + (s->ar[VGA_ATC_MODE] & 0x04)) { + dup9 = 1; + } + vga_draw_glyph9(d1, linesize, + font_ptr, cheight, fgcol, bgcol, dup9); + } + if (src == cursor_ptr && + !(s->cr[VGA_CRTC_CURSOR_START] & 0x20) && + s->cursor_visible_phase) { + int line_start, line_last, h; + /* draw the cursor */ + line_start = s->cr[VGA_CRTC_CURSOR_START] & 0x1f; + line_last = s->cr[VGA_CRTC_CURSOR_END] & 0x1f; + /* XXX: check that */ + if (line_last > cheight - 1) + line_last = cheight - 1; + if (line_last >= line_start && line_start < cheight) { + h = line_last - line_start + 1; + d = d1 + linesize * line_start; + if (cw != 9) { + vga_draw_glyph8(d, linesize, + cursor_glyph, h, fgcol, bgcol); + } else { + vga_draw_glyph9(d, linesize, + cursor_glyph, h, fgcol, bgcol, 1); + } + } + } + } + d1 += x_incr; + src += 4; + ch_attr_ptr++; + } + if (cx_max != -1) { + dpy_gfx_update(s->con, cx_min * cw, cy * cheight, + (cx_max - cx_min + 1) * cw, cheight); + } + dest += linesize * cheight; + line1 = line + cheight; + offset += line_offset; + if (line < s->line_compare && line1 >= s->line_compare) { + offset = 0; + } + line = line1; + } +} + +enum { + VGA_DRAW_LINE2, + VGA_DRAW_LINE2D2, + VGA_DRAW_LINE4, + VGA_DRAW_LINE4D2, + VGA_DRAW_LINE8D2, + VGA_DRAW_LINE8, + VGA_DRAW_LINE15, + VGA_DRAW_LINE16, + VGA_DRAW_LINE24, + VGA_DRAW_LINE32, + VGA_DRAW_LINE_NB, +}; + +static vga_draw_line_func * const vga_draw_line_table[NB_DEPTHS * VGA_DRAW_LINE_NB] = { + vga_draw_line2_8, + vga_draw_line2_16, + vga_draw_line2_16, + vga_draw_line2_32, + vga_draw_line2_32, + vga_draw_line2_16, + vga_draw_line2_16, + + vga_draw_line2d2_8, + vga_draw_line2d2_16, + vga_draw_line2d2_16, + vga_draw_line2d2_32, + vga_draw_line2d2_32, + vga_draw_line2d2_16, + vga_draw_line2d2_16, + + vga_draw_line4_8, + vga_draw_line4_16, + vga_draw_line4_16, + vga_draw_line4_32, + vga_draw_line4_32, + vga_draw_line4_16, + vga_draw_line4_16, + + vga_draw_line4d2_8, + vga_draw_line4d2_16, + vga_draw_line4d2_16, + vga_draw_line4d2_32, + vga_draw_line4d2_32, + vga_draw_line4d2_16, + vga_draw_line4d2_16, + + vga_draw_line8d2_8, + vga_draw_line8d2_16, + vga_draw_line8d2_16, + vga_draw_line8d2_32, + vga_draw_line8d2_32, + vga_draw_line8d2_16, + vga_draw_line8d2_16, + + vga_draw_line8_8, + vga_draw_line8_16, + vga_draw_line8_16, + vga_draw_line8_32, + vga_draw_line8_32, + vga_draw_line8_16, + vga_draw_line8_16, + + vga_draw_line15_8, + vga_draw_line15_15, + vga_draw_line15_16, + vga_draw_line15_32, + vga_draw_line15_32bgr, + vga_draw_line15_15bgr, + vga_draw_line15_16bgr, + + vga_draw_line16_8, + vga_draw_line16_15, + vga_draw_line16_16, + vga_draw_line16_32, + vga_draw_line16_32bgr, + vga_draw_line16_15bgr, + vga_draw_line16_16bgr, + + vga_draw_line24_8, + vga_draw_line24_15, + vga_draw_line24_16, + vga_draw_line24_32, + vga_draw_line24_32bgr, + vga_draw_line24_15bgr, + vga_draw_line24_16bgr, + + vga_draw_line32_8, + vga_draw_line32_15, + vga_draw_line32_16, + vga_draw_line32_32, + vga_draw_line32_32bgr, + vga_draw_line32_15bgr, + vga_draw_line32_16bgr, +}; + +static int vga_get_bpp(VGACommonState *s) +{ + int ret; + + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + ret = s->vbe_regs[VBE_DISPI_INDEX_BPP]; + } else { + ret = 0; + } + return ret; +} + +static void vga_get_resolution(VGACommonState *s, int *pwidth, int *pheight) +{ + int width, height; + + if (s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED) { + width = s->vbe_regs[VBE_DISPI_INDEX_XRES]; + height = s->vbe_regs[VBE_DISPI_INDEX_YRES]; + } else { + width = (s->cr[VGA_CRTC_H_DISP] + 1) * 8; + height = s->cr[VGA_CRTC_V_DISP_END] | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); + height = (height + 1); + } + *pwidth = width; + *pheight = height; +} + +void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2) +{ + int y; + if (y1 >= VGA_MAX_HEIGHT) + return; + if (y2 >= VGA_MAX_HEIGHT) + y2 = VGA_MAX_HEIGHT; + for(y = y1; y < y2; y++) { + s->invalidated_y_table[y >> 5] |= 1 << (y & 0x1f); + } +} + +void vga_sync_dirty_bitmap(VGACommonState *s) +{ + memory_region_sync_dirty_bitmap(&s->vram); +} + +void vga_dirty_log_start(VGACommonState *s) +{ + memory_region_set_log(&s->vram, true, DIRTY_MEMORY_VGA); +} + +void vga_dirty_log_stop(VGACommonState *s) +{ + memory_region_set_log(&s->vram, false, DIRTY_MEMORY_VGA); +} + +/* + * graphic modes + */ +static void vga_draw_graphic(VGACommonState *s, int full_update) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int y1, y, update, linesize, y_start, double_scan, mask, depth; + int width, height, shift_control, line_offset, bwidth, bits; + ram_addr_t page0, page1, page_min, page_max; + int disp_width, multi_scan, multi_run; + uint8_t *d; + uint32_t v, addr1, addr; + vga_draw_line_func *vga_draw_line; +#if defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) + static const bool byteswap = false; +#else + static const bool byteswap = true; +#endif + + full_update |= update_basic_params(s); + + if (!full_update) + vga_sync_dirty_bitmap(s); + + s->get_resolution(s, &width, &height); + disp_width = width; + + shift_control = (s->gr[VGA_GFX_MODE] >> 5) & 3; + double_scan = (s->cr[VGA_CRTC_MAX_SCAN] >> 7); + if (shift_control != 1) { + multi_scan = (((s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1) << double_scan) + - 1; + } else { + /* in CGA modes, multi_scan is ignored */ + /* XXX: is it correct ? */ + multi_scan = double_scan; + } + multi_run = multi_scan; + if (shift_control != s->shift_control || + double_scan != s->double_scan) { + full_update = 1; + s->shift_control = shift_control; + s->double_scan = double_scan; + } + + if (shift_control == 0) { + if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + disp_width <<= 1; + } + } else if (shift_control == 1) { + if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + disp_width <<= 1; + } + } + + depth = s->get_bpp(s); + if (s->line_offset != s->last_line_offset || + disp_width != s->last_width || + height != s->last_height || + s->last_depth != depth) { + if (depth == 32 || (depth == 16 && !byteswap)) { + surface = qemu_create_displaysurface_from(disp_width, + height, depth, s->line_offset, + s->vram_ptr + (s->start_addr * 4), byteswap); + dpy_gfx_replace_surface(s->con, surface); + } else { + qemu_console_resize(s->con, disp_width, height); + surface = qemu_console_surface(s->con); + } + s->last_scr_width = disp_width; + s->last_scr_height = height; + s->last_width = disp_width; + s->last_height = height; + s->last_line_offset = s->line_offset; + s->last_depth = depth; + full_update = 1; + } else if (is_buffer_shared(surface) && + (full_update || surface_data(surface) != s->vram_ptr + + (s->start_addr * 4))) { + DisplaySurface *surface; + surface = qemu_create_displaysurface_from(disp_width, + height, depth, s->line_offset, + s->vram_ptr + (s->start_addr * 4), byteswap); + dpy_gfx_replace_surface(s->con, surface); + } + + s->rgb_to_pixel = + rgb_to_pixel_dup_table[get_depth_index(surface)]; + + if (shift_control == 0) { + full_update |= update_palette16(s); + if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + v = VGA_DRAW_LINE4D2; + } else { + v = VGA_DRAW_LINE4; + } + bits = 4; + } else if (shift_control == 1) { + full_update |= update_palette16(s); + if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) { + v = VGA_DRAW_LINE2D2; + } else { + v = VGA_DRAW_LINE2; + } + bits = 4; + } else { + switch(s->get_bpp(s)) { + default: + case 0: + full_update |= update_palette256(s); + v = VGA_DRAW_LINE8D2; + bits = 4; + break; + case 8: + full_update |= update_palette256(s); + v = VGA_DRAW_LINE8; + bits = 8; + break; + case 15: + v = VGA_DRAW_LINE15; + bits = 16; + break; + case 16: + v = VGA_DRAW_LINE16; + bits = 16; + break; + case 24: + v = VGA_DRAW_LINE24; + bits = 24; + break; + case 32: + v = VGA_DRAW_LINE32; + bits = 32; + break; + } + } + vga_draw_line = vga_draw_line_table[v * NB_DEPTHS + + get_depth_index(surface)]; + + if (!is_buffer_shared(surface) && s->cursor_invalidate) { + s->cursor_invalidate(s); + } + + line_offset = s->line_offset; +#if 0 + write_log("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n", + width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE], + s->line_compare, s->sr[VGA_SEQ_CLOCK_MODE]); +#endif + addr1 = (s->start_addr * 4); + bwidth = (width * bits + 7) / 8; + y_start = -1; + page_min = -1; + page_max = 0; + d = surface_data(surface); + linesize = surface_stride(surface); + y1 = 0; + for(y = 0; y < height; y++) { + addr = addr1; + if (!(s->cr[VGA_CRTC_MODE] & 1)) { + int shift; + /* CGA compatibility handling */ + shift = 14 + ((s->cr[VGA_CRTC_MODE] >> 6) & 1); + addr = (addr & ~(1 << shift)) | ((y1 & 1) << shift); + } + if (!(s->cr[VGA_CRTC_MODE] & 2)) { + addr = (addr & ~0x8000) | ((y1 & 2) << 14); + } + update = full_update; + page0 = addr; + page1 = addr + bwidth - 1; + update |= memory_region_get_dirty(&s->vram, page0, page1 - page0, + DIRTY_MEMORY_VGA) != 0; + /* explicit invalidation for the hardware cursor */ + update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1; + if (update) { + if (y_start < 0) + y_start = y; + if (page0 < page_min) + page_min = page0; + if (page1 > page_max) + page_max = page1; + if (!(is_buffer_shared(surface))) { + vga_draw_line(s, d, s->vram_ptr + addr, width); + if (s->cursor_draw_line) + s->cursor_draw_line(s, d, y); + } + } else { + if (y_start >= 0) { + /* flush to display */ + dpy_gfx_update(s->con, 0, y_start, + disp_width, y - y_start); + y_start = -1; + } + } + if (!multi_run) { + mask = (s->cr[VGA_CRTC_MODE] & 3) ^ 3; + if ((y1 & mask) == mask) + addr1 += line_offset; + y1++; + multi_run = multi_scan; + } else { + multi_run--; + } + /* line compare acts on the displayed lines */ + if (y == s->line_compare) + addr1 = 0; + d += linesize; + } + if (y_start >= 0) { + /* flush to display */ + dpy_gfx_update(s->con, 0, y_start, + disp_width, y - y_start); + } + /* reset modified pages */ + if (page_max >= page_min) { + memory_region_reset_dirty(&s->vram, + page_min, + page_max - page_min, + DIRTY_MEMORY_VGA); + } + memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4); +} + +static void vga_draw_blank(VGACommonState *s, int full_update) +{ + DisplaySurface *surface = qemu_console_surface(s->con); + int i, w, val; + uint8_t *d; + + if (!full_update) + return; + if (s->last_scr_width <= 0 || s->last_scr_height <= 0) + return; + + s->rgb_to_pixel = + rgb_to_pixel_dup_table[get_depth_index(surface)]; + if (surface_bits_per_pixel(surface) == 8) { + val = s->rgb_to_pixel(0, 0, 0); + } else { + val = 0; + } + w = s->last_scr_width * surface_bytes_per_pixel(surface); + d = surface_data(surface); + for(i = 0; i < s->last_scr_height; i++) { + memset(d, val, w); + d += surface_stride(surface); + } + dpy_gfx_update(s->con, 0, 0, + s->last_scr_width, s->last_scr_height); +} + +#define GMODE_TEXT 0 +#define GMODE_GRAPH 1 +#define GMODE_BLANK 2 + +static void vga_update_display(void *opaque) +{ + VGACommonState *s = (VGACommonState*)opaque; + DisplaySurface *surface = qemu_console_surface(s->con); + int full_update, graphic_mode; + + qemu_flush_coalesced_mmio_buffer(); + + if (surface_bits_per_pixel(surface) == 0) { + /* nothing to do */ + } else { + full_update = 0; + if (!(s->ar_index & 0x20)) { + graphic_mode = GMODE_BLANK; + } else { + graphic_mode = s->gr[VGA_GFX_MISC] & VGA_GR06_GRAPHICS_MODE; + } + if (graphic_mode != s->graphic_mode) { + s->graphic_mode = graphic_mode; + s->cursor_blink_time = qemu_get_clock_ms(vm_clock); + full_update = 1; + } + switch(graphic_mode) { + case GMODE_TEXT: + vga_draw_text(s, full_update); + break; + case GMODE_GRAPH: + vga_draw_graphic(s, full_update); + break; + case GMODE_BLANK: + default: + vga_draw_blank(s, full_update); + break; + } + } +} + +/* force a full display refresh */ +static void vga_invalidate_display(void *opaque) +{ + VGACommonState *s = (VGACommonState*)opaque; + + s->last_width = -1; + s->last_height = -1; +} + +void vga_common_reset(VGACommonState *s) +{ + s->sr_index = 0; + memset(s->sr, '\0', sizeof(s->sr)); + s->gr_index = 0; + memset(s->gr, '\0', sizeof(s->gr)); + s->ar_index = 0; + memset(s->ar, '\0', sizeof(s->ar)); + s->ar_flip_flop = 0; + s->cr_index = 0; + memset(s->cr, '\0', sizeof(s->cr)); + s->msr = 0; + s->fcr = 0; + s->st00 = 0; + s->st01 = 0; + s->dac_state = 0; + s->dac_sub_index = 0; + s->dac_read_index = 0; + s->dac_write_index = 0; + memset(s->dac_cache, '\0', sizeof(s->dac_cache)); + s->dac_8bit = 0; + memset(s->palette, '\0', sizeof(s->palette)); + s->bank_offset = 0; + s->vbe_index = 0; + memset(s->vbe_regs, '\0', sizeof(s->vbe_regs)); + s->vbe_regs[VBE_DISPI_INDEX_ID] = VBE_DISPI_ID5; + s->vbe_start_addr = 0; + s->vbe_line_offset = 0; + s->vbe_bank_mask = (s->vram_size >> 16) - 1; + memset(s->font_offsets, '\0', sizeof(s->font_offsets)); + s->graphic_mode = -1; /* force full update */ + s->shift_control = 0; + s->double_scan = 0; + s->line_offset = 0; + s->line_compare = 0; + s->start_addr = 0; + s->plane_updated = 0; + s->last_cw = 0; + s->last_ch = 0; + s->last_width = 0; + s->last_height = 0; + s->last_scr_width = 0; + s->last_scr_height = 0; + s->cursor_start = 0; + s->cursor_end = 0; + s->cursor_offset = 0; + memset(s->invalidated_y_table, '\0', sizeof(s->invalidated_y_table)); + memset(s->last_palette, '\0', sizeof(s->last_palette)); + memset(s->last_ch_attr, '\0', sizeof(s->last_ch_attr)); + switch (vga_retrace_method_value) { + case VGA_RETRACE_DUMB: + break; + case VGA_RETRACE_PRECISE: + memset(&s->retrace_info, 0, sizeof (s->retrace_info)); + break; + } + vga_update_memory_access(s); +} + +static void vga_reset(void *opaque) +{ + VGACommonState *s = (VGACommonState*)opaque; + vga_common_reset(s); +} + +#define TEXTMODE_X(x) ((x) % width) +#define TEXTMODE_Y(x) ((x) / width) +#define VMEM2CHTYPE(v) ((v & 0xff0007ff) | \ + ((v & 0x00000800) << 10) | ((v & 0x00007000) >> 1)) +/* relay text rendering to the display driver + * instead of doing a full vga_update_display() */ +static void vga_update_text(void *opaque, console_ch_t *chardata) +{ + VGACommonState *s = (VGACommonState*)opaque; + int graphic_mode, i, cursor_offset, cursor_visible; + int cw, cheight, width, height, size, c_min, c_max; + uint32_t *src; + console_ch_t *dst, val; + char msg_buffer[80]; + int full_update = 0; + + qemu_flush_coalesced_mmio_buffer(); + + if (!(s->ar_index & 0x20)) { + graphic_mode = GMODE_BLANK; + } else { + graphic_mode = s->gr[VGA_GFX_MISC] & VGA_GR06_GRAPHICS_MODE; + } + if (graphic_mode != s->graphic_mode) { + s->graphic_mode = graphic_mode; + full_update = 1; + } + if (s->last_width == -1) { + s->last_width = 0; + full_update = 1; + } + + switch (graphic_mode) { + case GMODE_TEXT: + /* TODO: update palette */ + full_update |= update_basic_params(s); + + /* total width & height */ + cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1; + cw = 8; + if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) { + cw = 9; + } + if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) { + cw = 16; /* NOTE: no 18 pixel wide */ + } + width = (s->cr[VGA_CRTC_H_DISP] + 1); + if (s->cr[VGA_CRTC_V_TOTAL] == 100) { + /* ugly hack for CGA 160x100x16 - explain me the logic */ + height = 100; + } else { + height = s->cr[VGA_CRTC_V_DISP_END] | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x02) << 7) | + ((s->cr[VGA_CRTC_OVERFLOW] & 0x40) << 3); + height = (height + 1) / cheight; + } + + size = (height * width); + if (size > CH_ATTR_SIZE) { + if (!full_update) + return; + + snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Text mode", + width, height); + break; + } + + if (width != s->last_width || height != s->last_height || + cw != s->last_cw || cheight != s->last_ch) { + s->last_scr_width = width * cw; + s->last_scr_height = height * cheight; + qemu_console_resize(s->con, s->last_scr_width, s->last_scr_height); + dpy_text_resize(s->con, width, height); + s->last_depth = 0; + s->last_width = width; + s->last_height = height; + s->last_ch = cheight; + s->last_cw = cw; + full_update = 1; + } + + if (full_update) { + s->full_update_gfx = 1; + } + if (s->full_update_text) { + s->full_update_text = 0; + full_update |= 1; + } + + /* Update "hardware" cursor */ + cursor_offset = ((s->cr[VGA_CRTC_CURSOR_HI] << 8) | + s->cr[VGA_CRTC_CURSOR_LO]) - s->start_addr; + if (cursor_offset != s->cursor_offset || + s->cr[VGA_CRTC_CURSOR_START] != s->cursor_start || + s->cr[VGA_CRTC_CURSOR_END] != s->cursor_end || full_update) { + cursor_visible = !(s->cr[VGA_CRTC_CURSOR_START] & 0x20); + if (cursor_visible && cursor_offset < size && cursor_offset >= 0) + dpy_text_cursor(s->con, + TEXTMODE_X(cursor_offset), + TEXTMODE_Y(cursor_offset)); + else + dpy_text_cursor(s->con, -1, -1); + s->cursor_offset = cursor_offset; + s->cursor_start = s->cr[VGA_CRTC_CURSOR_START]; + s->cursor_end = s->cr[VGA_CRTC_CURSOR_END]; + } + + src = (uint32_t *) s->vram_ptr + s->start_addr; + dst = chardata; + + if (full_update) { + for (i = 0; i < size; src ++, dst ++, i ++) + console_write_ch(dst, VMEM2CHTYPE(le32_to_cpu(*src))); + + dpy_text_update(s->con, 0, 0, width, height); + } else { + c_max = 0; + + for (i = 0; i < size; src ++, dst ++, i ++) { + console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); + if (*dst != val) { + *dst = val; + c_max = i; + break; + } + } + c_min = i; + for (; i < size; src ++, dst ++, i ++) { + console_write_ch(&val, VMEM2CHTYPE(le32_to_cpu(*src))); + if (*dst != val) { + *dst = val; + c_max = i; + } + } + + if (c_min <= c_max) { + i = TEXTMODE_Y(c_min); + dpy_text_update(s->con, 0, i, width, TEXTMODE_Y(c_max) - i + 1); + } + } + + return; + case GMODE_GRAPH: + if (!full_update) + return; + + s->get_resolution(s, &width, &height); + snprintf(msg_buffer, sizeof(msg_buffer), "%i x %i Graphic mode", + width, height); + break; + case GMODE_BLANK: + default: + if (!full_update) + return; + + snprintf(msg_buffer, sizeof(msg_buffer), "VGA Blank mode"); + break; + } + + /* Display a message */ + s->last_width = 60; + s->last_height = height = 3; + dpy_text_cursor(s->con, -1, -1); + dpy_text_resize(s->con, s->last_width, height); + + for (dst = chardata, i = 0; i < s->last_width * height; i ++) + console_write_ch(dst ++, ' '); + + size = strlen(msg_buffer); + width = (s->last_width - size) / 2; + dst = chardata + s->last_width + width; + for (i = 0; i < size; i ++) + console_write_ch(dst ++, 0x00200100 | msg_buffer[i]); + + dpy_text_update(s->con, 0, 0, s->last_width, height); +} + +static uint64_t vga_mem_read(void *opaque, hwaddr addr, + unsigned size) +{ + VGACommonState *s = (VGACommonState*)opaque; + + return vga_mem_readb(s, addr); +} + +static void vga_mem_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VGACommonState *s = (VGACommonState*)opaque; + + return vga_mem_writeb(s, addr, data); +} + +const MemoryRegionOps vga_mem_ops = { + vga_mem_read, + vga_mem_write, + DEVICE_LITTLE_ENDIAN, + 1, + 1 +}; + +#if 0 +static int vga_common_post_load(void *opaque, int version_id) +{ + VGACommonState *s = (VGACommonState*)opaque; + + /* force refresh */ + s->graphic_mode = -1; + return 0; +} + +const VMStateDescription vmstate_vga_common = { + .name = "vga", + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, + .post_load = vga_common_post_load, + .fields = (VMStateField []) { + VMSTATE_UINT32(latch, VGACommonState), + VMSTATE_UINT8(sr_index, VGACommonState), + VMSTATE_PARTIAL_BUFFER(sr, VGACommonState, 8), + VMSTATE_UINT8(gr_index, VGACommonState), + VMSTATE_PARTIAL_BUFFER(gr, VGACommonState, 16), + VMSTATE_UINT8(ar_index, VGACommonState), + VMSTATE_BUFFER(ar, VGACommonState), + VMSTATE_INT32(ar_flip_flop, VGACommonState), + VMSTATE_UINT8(cr_index, VGACommonState), + VMSTATE_BUFFER(cr, VGACommonState), + VMSTATE_UINT8(msr, VGACommonState), + VMSTATE_UINT8(fcr, VGACommonState), + VMSTATE_UINT8(st00, VGACommonState), + VMSTATE_UINT8(st01, VGACommonState), + + VMSTATE_UINT8(dac_state, VGACommonState), + VMSTATE_UINT8(dac_sub_index, VGACommonState), + VMSTATE_UINT8(dac_read_index, VGACommonState), + VMSTATE_UINT8(dac_write_index, VGACommonState), + VMSTATE_BUFFER(dac_cache, VGACommonState), + VMSTATE_BUFFER(palette, VGACommonState), + + VMSTATE_INT32(bank_offset, VGACommonState), + VMSTATE_UINT8_EQUAL(is_vbe_vmstate, VGACommonState), + VMSTATE_UINT16(vbe_index, VGACommonState), + VMSTATE_UINT16_ARRAY(vbe_regs, VGACommonState, VBE_DISPI_INDEX_NB), + VMSTATE_UINT32(vbe_start_addr, VGACommonState), + VMSTATE_UINT32(vbe_line_offset, VGACommonState), + VMSTATE_UINT32(vbe_bank_mask, VGACommonState), + VMSTATE_END_OF_LIST() + } +}; +#endif + +static const GraphicHwOps vga_ops = { + vga_invalidate_display, + vga_update_display, + vga_update_text +}; + +void vga_common_init(VGACommonState *s) +{ + int i, j, v, b; + + for(i = 0;i < 256; i++) { + v = 0; + for(j = 0; j < 8; j++) { + v |= ((i >> j) & 1) << (j * 4); + } + expand4[i] = v; + + v = 0; + for(j = 0; j < 4; j++) { + v |= ((i >> (2 * j)) & 3) << (j * 4); + } + expand2[i] = v; + } + for(i = 0; i < 16; i++) { + v = 0; + for(j = 0; j < 4; j++) { + b = ((i >> j) & 1); + v |= b << (2 * j); + v |= b << (2 * j + 1); + } + expand4to8[i] = v; + } + + /* valid range: 1 MB -> 256 MB */ + s->vram_size = 1024 * 1024; + while (s->vram_size < (s->vram_size_mb << 20) && + s->vram_size < (256 << 20)) { + s->vram_size <<= 1; + } + s->vram_size_mb = s->vram_size >> 20; + + s->is_vbe_vmstate = 1; + memory_region_init_ram(&s->vram, "vga.vram", s->vram_size); +// vmstate_register_ram_global(&s->vram); +// xen_register_framebuffer(&s->vram); + s->vram_ptr = (uint8_t*)memory_region_get_ram_ptr(&s->vram); + s->get_bpp = vga_get_bpp; + s->get_offsets = vga_get_offsets; + s->get_resolution = vga_get_resolution; + s->hw_ops = &vga_ops; + switch (vga_retrace_method_value) { + case VGA_RETRACE_DUMB: + s->retrace = vga_dumb_retrace; + s->update_retrace_info = vga_dumb_update_retrace_info; + break; + + case VGA_RETRACE_PRECISE: + s->retrace = vga_precise_retrace; + s->update_retrace_info = vga_precise_update_retrace_info; + break; + } + vga_dirty_log_start(s); +} + +static const MemoryRegionPortio vga_portio_list[] = { + { 0x04, 2, 1, vga_ioport_read, vga_ioport_write }, /* 3b4 */ + { 0x0a, 1, 1, vga_ioport_read, vga_ioport_write }, /* 3ba */ + { 0x10, 16, 1, vga_ioport_read, vga_ioport_write }, /* 3c0 */ + { 0x24, 2, 1, vga_ioport_read, vga_ioport_write }, /* 3d4 */ + { 0x2a, 1, 1, vga_ioport_read, vga_ioport_write }, /* 3da */ + PORTIO_END_OF_LIST(), +}; + +static const MemoryRegionPortio vbe_portio_list[] = { + { 0, 1, 2, vbe_ioport_read_index, vbe_ioport_write_index }, +# ifdef TARGET_I386 + { 1, 1, 2, vbe_ioport_read_data, vbe_ioport_write_data }, +# endif + { 2, 1, 2, vbe_ioport_read_data, vbe_ioport_write_data }, + PORTIO_END_OF_LIST(), +}; + +/* Used by both ISA and PCI */ +MemoryRegion *vga_init_io(VGACommonState *s, + const MemoryRegionPortio **vga_ports, + const MemoryRegionPortio **vbe_ports) +{ + MemoryRegion *vga_mem; + + *vga_ports = vga_portio_list; + *vbe_ports = vbe_portio_list; + + vga_mem = (MemoryRegion*)g_malloc(sizeof(*vga_mem)); + memory_region_init_io(vga_mem, &vga_mem_ops, s, + "vga-lowmem", 0x20000); + memory_region_set_flush_coalesced(vga_mem); + + return vga_mem; +} + +void vga_init(VGACommonState *s, MemoryRegion *address_space, + MemoryRegion *address_space_io, bool init_vga_ports) +{ + MemoryRegion *vga_io_memory; + const MemoryRegionPortio *vga_ports, *vbe_ports; + PortioList *vga_port_list = g_new(PortioList, 1); + PortioList *vbe_port_list = g_new(PortioList, 1); + +// qemu_register_reset(vga_reset, s); + + s->bank_offset = 0; + + s->legacy_address_space = address_space; + + vga_io_memory = vga_init_io(s, &vga_ports, &vbe_ports); + memory_region_add_subregion_overlap(address_space, + isa_mem_base + 0x000a0000, + vga_io_memory, + 1); + memory_region_set_coalescing(vga_io_memory); + if (init_vga_ports) { + portio_list_init(vga_port_list, vga_ports, s, "vga"); + portio_list_add(vga_port_list, address_space_io, 0x3b0); + } + if (vbe_ports) { + portio_list_init(vbe_port_list, vbe_ports, s, "vbe"); + portio_list_add(vbe_port_list, address_space_io, 0x1ce); + } +} + +void vga_init_vbe(VGACommonState *s, MemoryRegion *system_memory) +{ + /* With pc-0.12 and below we map both the PCI BAR and the fixed VBE region, + * so use an alias to avoid double-mapping the same region. + */ + memory_region_init_alias(&s->vram_vbe, "vram.vbe", + &s->vram, 0, memory_region_size(&s->vram)); + /* XXX: use optimized standard vga accesses */ + memory_region_add_subregion(system_memory, + VBE_DISPI_LFB_PHYSICAL_ADDRESS, + &s->vram_vbe); + s->vbe_mapped = 1; +} diff --git a/qemuvga/vga.h b/qemuvga/vga.h new file mode 100644 index 00000000..d917046d --- /dev/null +++ b/qemuvga/vga.h @@ -0,0 +1,159 @@ +/* + * linux/include/video/vga.h -- standard VGA chipset interaction + * + * Copyright 1999 Jeff Garzik + * + * Copyright history from vga16fb.c: + * Copyright 1999 Ben Pfaff and Petr Vandrovec + * Based on VGA info at http://www.osdever.net/FreeVGA/home.htm + * Based on VESA framebuffer (c) 1998 Gerd Knorr + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file COPYING in the main directory of this + * archive for more details. + * + */ + +#ifndef __linux_video_vga_h__ +#define __linux_video_vga_h__ + +/* Some of the code below is taken from SVGAlib. The original, + unmodified copyright notice for that code is below. */ +/* VGAlib version 1.2 - (c) 1993 Tommy Frandsen */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it without any restrictions. This library is distributed */ +/* in the hope that it will be useful, but without any warranty. */ + +/* Multi-chipset support Copyright 1993 Harm Hanemaayer */ +/* partially copyrighted (C) 1993 by Hartmut Schirmer */ + +/* VGA data register ports */ +#define VGA_CRT_DC 0x3D5 /* CRT Controller Data Register - color emulation */ +#define VGA_CRT_DM 0x3B5 /* CRT Controller Data Register - mono emulation */ +#define VGA_ATT_R 0x3C1 /* Attribute Controller Data Read Register */ +#define VGA_ATT_W 0x3C0 /* Attribute Controller Data Write Register */ +#define VGA_GFX_D 0x3CF /* Graphics Controller Data Register */ +#define VGA_SEQ_D 0x3C5 /* Sequencer Data Register */ +#define VGA_MIS_R 0x3CC /* Misc Output Read Register */ +#define VGA_MIS_W 0x3C2 /* Misc Output Write Register */ +#define VGA_FTC_R 0x3CA /* Feature Control Read Register */ +#define VGA_IS1_RC 0x3DA /* Input Status Register 1 - color emulation */ +#define VGA_IS1_RM 0x3BA /* Input Status Register 1 - mono emulation */ +#define VGA_PEL_D 0x3C9 /* PEL Data Register */ +#define VGA_PEL_MSK 0x3C6 /* PEL mask register */ + +/* EGA-specific registers */ +#define EGA_GFX_E0 0x3CC /* Graphics enable processor 0 */ +#define EGA_GFX_E1 0x3CA /* Graphics enable processor 1 */ + +/* VGA index register ports */ +#define VGA_CRT_IC 0x3D4 /* CRT Controller Index - color emulation */ +#define VGA_CRT_IM 0x3B4 /* CRT Controller Index - mono emulation */ +#define VGA_ATT_IW 0x3C0 /* Attribute Controller Index & Data Write Register */ +#define VGA_GFX_I 0x3CE /* Graphics Controller Index */ +#define VGA_SEQ_I 0x3C4 /* Sequencer Index */ +#define VGA_PEL_IW 0x3C8 /* PEL Write Index */ +#define VGA_PEL_IR 0x3C7 /* PEL Read Index */ + +/* standard VGA indexes max counts */ +#define VGA_CRT_C 0x19 /* Number of CRT Controller Registers */ +#define VGA_ATT_C 0x15 /* Number of Attribute Controller Registers */ +#define VGA_GFX_C 0x09 /* Number of Graphics Controller Registers */ +#define VGA_SEQ_C 0x05 /* Number of Sequencer Registers */ +#define VGA_MIS_C 0x01 /* Number of Misc Output Register */ + +/* VGA misc register bit masks */ +#define VGA_MIS_COLOR 0x01 +#define VGA_MIS_ENB_MEM_ACCESS 0x02 +#define VGA_MIS_DCLK_28322_720 0x04 +#define VGA_MIS_ENB_PLL_LOAD (0x04 | 0x08) +#define VGA_MIS_SEL_HIGH_PAGE 0x20 + +/* VGA CRT controller register indices */ +#define VGA_CRTC_H_TOTAL 0 +#define VGA_CRTC_H_DISP 1 +#define VGA_CRTC_H_BLANK_START 2 +#define VGA_CRTC_H_BLANK_END 3 +#define VGA_CRTC_H_SYNC_START 4 +#define VGA_CRTC_H_SYNC_END 5 +#define VGA_CRTC_V_TOTAL 6 +#define VGA_CRTC_OVERFLOW 7 +#define VGA_CRTC_PRESET_ROW 8 +#define VGA_CRTC_MAX_SCAN 9 +#define VGA_CRTC_CURSOR_START 0x0A +#define VGA_CRTC_CURSOR_END 0x0B +#define VGA_CRTC_START_HI 0x0C +#define VGA_CRTC_START_LO 0x0D +#define VGA_CRTC_CURSOR_HI 0x0E +#define VGA_CRTC_CURSOR_LO 0x0F +#define VGA_CRTC_V_SYNC_START 0x10 +#define VGA_CRTC_V_SYNC_END 0x11 +#define VGA_CRTC_V_DISP_END 0x12 +#define VGA_CRTC_OFFSET 0x13 +#define VGA_CRTC_UNDERLINE 0x14 +#define VGA_CRTC_V_BLANK_START 0x15 +#define VGA_CRTC_V_BLANK_END 0x16 +#define VGA_CRTC_MODE 0x17 +#define VGA_CRTC_LINE_COMPARE 0x18 +#define VGA_CRTC_REGS VGA_CRT_C + +/* VGA CRT controller bit masks */ +#define VGA_CR11_LOCK_CR0_CR7 0x80 /* lock writes to CR0 - CR7 */ +#define VGA_CR17_H_V_SIGNALS_ENABLED 0x80 + +/* VGA attribute controller register indices */ +#define VGA_ATC_PALETTE0 0x00 +#define VGA_ATC_PALETTE1 0x01 +#define VGA_ATC_PALETTE2 0x02 +#define VGA_ATC_PALETTE3 0x03 +#define VGA_ATC_PALETTE4 0x04 +#define VGA_ATC_PALETTE5 0x05 +#define VGA_ATC_PALETTE6 0x06 +#define VGA_ATC_PALETTE7 0x07 +#define VGA_ATC_PALETTE8 0x08 +#define VGA_ATC_PALETTE9 0x09 +#define VGA_ATC_PALETTEA 0x0A +#define VGA_ATC_PALETTEB 0x0B +#define VGA_ATC_PALETTEC 0x0C +#define VGA_ATC_PALETTED 0x0D +#define VGA_ATC_PALETTEE 0x0E +#define VGA_ATC_PALETTEF 0x0F +#define VGA_ATC_MODE 0x10 +#define VGA_ATC_OVERSCAN 0x11 +#define VGA_ATC_PLANE_ENABLE 0x12 +#define VGA_ATC_PEL 0x13 +#define VGA_ATC_COLOR_PAGE 0x14 + +#define VGA_AR_ENABLE_DISPLAY 0x20 + +/* VGA sequencer register indices */ +#define VGA_SEQ_RESET 0x00 +#define VGA_SEQ_CLOCK_MODE 0x01 +#define VGA_SEQ_PLANE_WRITE 0x02 +#define VGA_SEQ_CHARACTER_MAP 0x03 +#define VGA_SEQ_MEMORY_MODE 0x04 + +/* VGA sequencer register bit masks */ +#define VGA_SR01_CHAR_CLK_8DOTS 0x01 /* bit 0: character clocks 8 dots wide are generated */ +#define VGA_SR01_SCREEN_OFF 0x20 /* bit 5: Screen is off */ +#define VGA_SR02_ALL_PLANES 0x0F /* bits 3-0: enable access to all planes */ +#define VGA_SR04_EXT_MEM 0x02 /* bit 1: allows complete mem access to 256K */ +#define VGA_SR04_SEQ_MODE 0x04 /* bit 2: directs system to use a sequential addressing mode */ +#define VGA_SR04_CHN_4M 0x08 /* bit 3: selects modulo 4 addressing for CPU access to display memory */ + +/* VGA graphics controller register indices */ +#define VGA_GFX_SR_VALUE 0x00 +#define VGA_GFX_SR_ENABLE 0x01 +#define VGA_GFX_COMPARE_VALUE 0x02 +#define VGA_GFX_DATA_ROTATE 0x03 +#define VGA_GFX_PLANE_READ 0x04 +#define VGA_GFX_MODE 0x05 +#define VGA_GFX_MISC 0x06 +#define VGA_GFX_COMPARE_MASK 0x07 +#define VGA_GFX_BIT_MASK 0x08 + +/* VGA graphics controller bit masks */ +#define VGA_GR06_GRAPHICS_MODE 0x01 + +#endif /* __linux_video_vga_h__ */ diff --git a/qemuvga/vga_int.h b/qemuvga/vga_int.h new file mode 100644 index 00000000..fad7640e --- /dev/null +++ b/qemuvga/vga_int.h @@ -0,0 +1,216 @@ +/* + * QEMU internal VGA defines. + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * 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. + */ +#ifndef HW_VGA_INT_H +#define HW_VGA_INT_H 1 + +#if 0 +#include +#include "qapi/error.h" +#include "exec/memory.h" +#endif + +#define ST01_V_RETRACE 0x08 +#define ST01_DISP_ENABLE 0x01 + +#define VBE_DISPI_MAX_XRES 16000 +#define VBE_DISPI_MAX_YRES 12000 +#define VBE_DISPI_MAX_BPP 32 + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_NB 0xa /* size of vbe_regs[] */ +#define VBE_DISPI_INDEX_VIDEO_MEMORY_64K 0xa /* read-only, not in vbe_regs */ + +#define VBE_DISPI_ID0 0xB0C0 +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 +#define VBE_DISPI_ID3 0xB0C3 +#define VBE_DISPI_ID4 0xB0C4 +#define VBE_DISPI_ID5 0xB0C5 + +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +#define VBE_DISPI_LFB_PHYSICAL_ADDRESS 0xE0000000 + +#define CH_ATTR_SIZE (160 * 100) +#define VGA_MAX_HEIGHT 2048 + +struct vga_precise_retrace { + int64_t ticks_per_char; + int64_t total_chars; + int htotal; + int hstart; + int hend; + int vstart; + int vend; + int freq; +}; + +union vga_retrace { + struct vga_precise_retrace precise; +}; + +struct VGACommonState; +typedef uint8_t (* vga_retrace_fn)(struct VGACommonState *s); +typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s); + +typedef struct VGACommonState { + MemoryRegion *legacy_address_space; + uint8_t *vram_ptr; + MemoryRegion vram; + MemoryRegion vram_vbe; + uint32_t vram_size; + uint32_t vram_size_mb; /* property */ + uint32_t latch; + MemoryRegion *chain4_alias; + uint8_t sr_index; + uint8_t sr[256]; + uint8_t gr_index; + uint8_t gr[256]; + uint8_t ar_index; + uint8_t ar[21]; + int ar_flip_flop; + uint8_t cr_index; + uint8_t cr[256]; /* CRT registers */ + uint8_t msr; /* Misc Output Register */ + uint8_t fcr; /* Feature Control Register */ + uint8_t st00; /* status 0 */ + uint8_t st01; /* status 1 */ + uint8_t dac_state; + uint8_t dac_sub_index; + uint8_t dac_read_index; + uint8_t dac_write_index; + uint8_t dac_cache[3]; /* used when writing */ + int dac_8bit; + uint8_t palette[768]; + int32_t bank_offset; + int (*get_bpp)(struct VGACommonState *s); + void (*get_offsets)(struct VGACommonState *s, + uint32_t *pline_offset, + uint32_t *pstart_addr, + uint32_t *pline_compare); + void (*get_resolution)(struct VGACommonState *s, + int *pwidth, + int *pheight); + /* bochs vbe state */ + uint16_t vbe_index; + uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; + uint32_t vbe_start_addr; + uint32_t vbe_line_offset; + uint32_t vbe_bank_mask; + int vbe_mapped; + /* display refresh support */ + QemuConsole *con; + uint32_t font_offsets[2]; + int graphic_mode; + uint8_t shift_control; + uint8_t double_scan; + uint32_t line_offset; + uint32_t line_compare; + uint32_t start_addr; + uint32_t plane_updated; + uint32_t last_line_offset; + uint8_t last_cw, last_ch; + uint32_t last_width, last_height; /* in chars or pixels */ + uint32_t last_scr_width, last_scr_height; /* in pixels */ + uint32_t last_depth; /* in bits */ + uint8_t cursor_start, cursor_end; + bool cursor_visible_phase; + int64_t cursor_blink_time; + uint32_t cursor_offset; + unsigned int (*rgb_to_pixel)(unsigned int r, + unsigned int g, unsigned b); + const GraphicHwOps *hw_ops; + bool full_update_text; + bool full_update_gfx; + /* hardware mouse cursor support */ + uint32_t invalidated_y_table[VGA_MAX_HEIGHT / 32]; + void (*cursor_invalidate)(struct VGACommonState *s); + void (*cursor_draw_line)(struct VGACommonState *s, uint8_t *d, int y); + /* tell for each page if it has been updated since the last time */ + uint32_t last_palette[256]; + uint32_t last_ch_attr[CH_ATTR_SIZE]; /* XXX: make it dynamic */ + /* retrace */ + vga_retrace_fn retrace; + vga_update_retrace_info_fn update_retrace_info; + union vga_retrace retrace_info; + uint8_t is_vbe_vmstate; +} VGACommonState; + +STATIC_INLINE int c6_to_8(int v) +{ + int b; + v &= 0x3f; + b = v & 1; + return (v << 2) | (b << 1) | b; +} + +void vga_common_init(VGACommonState *s); +void vga_init(VGACommonState *s, MemoryRegion *address_space, + MemoryRegion *address_space_io, bool init_vga_ports); +MemoryRegion *vga_init_io(VGACommonState *s, + const MemoryRegionPortio **vga_ports, + const MemoryRegionPortio **vbe_ports); +void vga_common_reset(VGACommonState *s); + +void vga_sync_dirty_bitmap(VGACommonState *s); +void vga_dirty_log_start(VGACommonState *s); +void vga_dirty_log_stop(VGACommonState *s); + +extern const VMStateDescription vmstate_vga_common; +uint32_t vga_ioport_read(void *opaque, uint32_t addr); +void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val); +uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr); +void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val); +void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2); + +int vga_ioport_invalid(VGACommonState *s, uint32_t addr); + +void vga_init_vbe(VGACommonState *s, MemoryRegion *address_space); +uint32_t vbe_ioport_read_data(void *opaque, uint32_t addr); +void vbe_ioport_write_index(void *opaque, uint32_t addr, uint32_t val); +void vbe_ioport_write_data(void *opaque, uint32_t addr, uint32_t val); + +extern const uint8_t sr_mask[8]; +extern const uint8_t gr_mask[16]; + +#define VGABIOS_FILENAME "vgabios.bin" +#define VGABIOS_CIRRUS_FILENAME "vgabios-cirrus.bin" + +extern const MemoryRegionOps vga_mem_ops; + +#endif diff --git a/qemuvga/vga_template.h b/qemuvga/vga_template.h new file mode 100644 index 00000000..f6f6a01d --- /dev/null +++ b/qemuvga/vga_template.h @@ -0,0 +1,459 @@ +/* + * QEMU VGA Emulator templates + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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. + */ + +#if DEPTH == 8 +#define BPP 1 +#define PIXEL_TYPE uint8_t +#elif DEPTH == 15 || DEPTH == 16 +#define BPP 2 +#define PIXEL_TYPE uint16_t +#elif DEPTH == 32 +#define BPP 4 +#define PIXEL_TYPE uint32_t +#else +#error unsupport depth +#endif + +#ifdef BGR_FORMAT +#define PIXEL_NAME glue(DEPTH, bgr) +#else +#define PIXEL_NAME DEPTH +#endif /* BGR_FORMAT */ + +#if DEPTH != 15 && !defined(BGR_FORMAT) + +static inline void glue(vga_draw_glyph_line_, DEPTH)(uint8_t *d, + uint32_t font_data, + uint32_t xorcol, + uint32_t bgcol) +{ +#if BPP == 1 + ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol; +#elif BPP == 2 + ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol; + ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol; + ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol; +#else + ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol; +#endif +} + +static void glue(vga_draw_glyph8_, DEPTH)(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol) +{ + uint32_t font_data, xorcol; + + xorcol = bgcol ^ fgcol; + do { + font_data = font_ptr[0]; + glue(vga_draw_glyph_line_, DEPTH)(d, font_data, xorcol, bgcol); + font_ptr += 4; + d += linesize; + } while (--h); +} + +static void glue(vga_draw_glyph16_, DEPTH)(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol) +{ + uint32_t font_data, xorcol; + + xorcol = bgcol ^ fgcol; + do { + font_data = font_ptr[0]; + glue(vga_draw_glyph_line_, DEPTH)(d, + expand4to8[font_data >> 4], + xorcol, bgcol); + glue(vga_draw_glyph_line_, DEPTH)(d + 8 * BPP, + expand4to8[font_data & 0x0f], + xorcol, bgcol); + font_ptr += 4; + d += linesize; + } while (--h); +} + +static void glue(vga_draw_glyph9_, DEPTH)(uint8_t *d, int linesize, + const uint8_t *font_ptr, int h, + uint32_t fgcol, uint32_t bgcol, int dup9) +{ + uint32_t font_data, xorcol, v; + + xorcol = bgcol ^ fgcol; + do { + font_data = font_ptr[0]; +#if BPP == 1 + cpu_to_32wu((uint32_t *)d, (dmask16[(font_data >> 4)] & xorcol) ^ bgcol); + v = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol; + cpu_to_32wu(((uint32_t *)d)+1, v); + if (dup9) + ((uint8_t *)d)[8] = v >> (24 * (1 - BIG)); + else + ((uint8_t *)d)[8] = bgcol; + +#elif BPP == 2 + cpu_to_32wu(((uint32_t *)d)+0, (dmask4[(font_data >> 6)] & xorcol) ^ bgcol); + cpu_to_32wu(((uint32_t *)d)+1, (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol); + cpu_to_32wu(((uint32_t *)d)+2, (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol); + v = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol; + cpu_to_32wu(((uint32_t *)d)+3, v); + if (dup9) + ((uint16_t *)d)[8] = v >> (16 * (1 - BIG)); + else + ((uint16_t *)d)[8] = bgcol; +#else + ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol; + ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol; + v = (-((font_data >> 0) & 1) & xorcol) ^ bgcol; + ((uint32_t *)d)[7] = v; + if (dup9) + ((uint32_t *)d)[8] = v; + else + ((uint32_t *)d)[8] = bgcol; +#endif + font_ptr += 4; + d += linesize; + } while (--h); +} + +/* + * 4 color mode + */ +static void glue(vga_draw_line2_, DEPTH)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t plane_mask, *palette, data, v; + int x; + + palette = s1->last_palette; + plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; + width >>= 3; + for(x = 0; x < width; x++) { + data = ((uint32_t *)s)[0]; + data &= plane_mask; + v = expand2[GET_PLANE(data, 0)]; + v |= expand2[GET_PLANE(data, 2)] << 2; + ((PIXEL_TYPE *)d)[0] = palette[v >> 12]; + ((PIXEL_TYPE *)d)[1] = palette[(v >> 8) & 0xf]; + ((PIXEL_TYPE *)d)[2] = palette[(v >> 4) & 0xf]; + ((PIXEL_TYPE *)d)[3] = palette[(v >> 0) & 0xf]; + + v = expand2[GET_PLANE(data, 1)]; + v |= expand2[GET_PLANE(data, 3)] << 2; + ((PIXEL_TYPE *)d)[4] = palette[v >> 12]; + ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf]; + ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf]; + ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf]; + d += BPP * 8; + s += 4; + } +} + +#if BPP == 1 +#define PUT_PIXEL2(d, n, v) ((uint16_t *)d)[(n)] = (v) +#elif BPP == 2 +#define PUT_PIXEL2(d, n, v) ((uint32_t *)d)[(n)] = (v) +#else +#define PUT_PIXEL2(d, n, v) \ +((uint32_t *)d)[2*(n)] = ((uint32_t *)d)[2*(n)+1] = (v) +#endif + +/* + * 4 color mode, dup2 horizontal + */ +static void glue(vga_draw_line2d2_, DEPTH)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t plane_mask, *palette, data, v; + int x; + + palette = s1->last_palette; + plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; + width >>= 3; + for(x = 0; x < width; x++) { + data = ((uint32_t *)s)[0]; + data &= plane_mask; + v = expand2[GET_PLANE(data, 0)]; + v |= expand2[GET_PLANE(data, 2)] << 2; + PUT_PIXEL2(d, 0, palette[v >> 12]); + PUT_PIXEL2(d, 1, palette[(v >> 8) & 0xf]); + PUT_PIXEL2(d, 2, palette[(v >> 4) & 0xf]); + PUT_PIXEL2(d, 3, palette[(v >> 0) & 0xf]); + + v = expand2[GET_PLANE(data, 1)]; + v |= expand2[GET_PLANE(data, 3)] << 2; + PUT_PIXEL2(d, 4, palette[v >> 12]); + PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]); + PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]); + PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]); + d += BPP * 16; + s += 4; + } +} + +/* + * 16 color mode + */ +static void glue(vga_draw_line4_, DEPTH)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t plane_mask, data, v, *palette; + int x; + + palette = s1->last_palette; + plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; + width >>= 3; + for(x = 0; x < width; x++) { + data = ((uint32_t *)s)[0]; + data &= plane_mask; + v = expand4[GET_PLANE(data, 0)]; + v |= expand4[GET_PLANE(data, 1)] << 1; + v |= expand4[GET_PLANE(data, 2)] << 2; + v |= expand4[GET_PLANE(data, 3)] << 3; + ((PIXEL_TYPE *)d)[0] = palette[v >> 28]; + ((PIXEL_TYPE *)d)[1] = palette[(v >> 24) & 0xf]; + ((PIXEL_TYPE *)d)[2] = palette[(v >> 20) & 0xf]; + ((PIXEL_TYPE *)d)[3] = palette[(v >> 16) & 0xf]; + ((PIXEL_TYPE *)d)[4] = palette[(v >> 12) & 0xf]; + ((PIXEL_TYPE *)d)[5] = palette[(v >> 8) & 0xf]; + ((PIXEL_TYPE *)d)[6] = palette[(v >> 4) & 0xf]; + ((PIXEL_TYPE *)d)[7] = palette[(v >> 0) & 0xf]; + d += BPP * 8; + s += 4; + } +} + +/* + * 16 color mode, dup2 horizontal + */ +static void glue(vga_draw_line4d2_, DEPTH)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t plane_mask, data, v, *palette; + int x; + + palette = s1->last_palette; + plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; + width >>= 3; + for(x = 0; x < width; x++) { + data = ((uint32_t *)s)[0]; + data &= plane_mask; + v = expand4[GET_PLANE(data, 0)]; + v |= expand4[GET_PLANE(data, 1)] << 1; + v |= expand4[GET_PLANE(data, 2)] << 2; + v |= expand4[GET_PLANE(data, 3)] << 3; + PUT_PIXEL2(d, 0, palette[v >> 28]); + PUT_PIXEL2(d, 1, palette[(v >> 24) & 0xf]); + PUT_PIXEL2(d, 2, palette[(v >> 20) & 0xf]); + PUT_PIXEL2(d, 3, palette[(v >> 16) & 0xf]); + PUT_PIXEL2(d, 4, palette[(v >> 12) & 0xf]); + PUT_PIXEL2(d, 5, palette[(v >> 8) & 0xf]); + PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]); + PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]); + d += BPP * 16; + s += 4; + } +} + +/* + * 256 color mode, double pixels + * + * XXX: add plane_mask support (never used in standard VGA modes) + */ +static void glue(vga_draw_line8d2_, DEPTH)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t *palette; + int x; + + palette = s1->last_palette; + width >>= 3; + for(x = 0; x < width; x++) { + PUT_PIXEL2(d, 0, palette[s[0]]); + PUT_PIXEL2(d, 1, palette[s[1]]); + PUT_PIXEL2(d, 2, palette[s[2]]); + PUT_PIXEL2(d, 3, palette[s[3]]); + d += BPP * 8; + s += 4; + } +} + +/* + * standard 256 color mode + * + * XXX: add plane_mask support (never used in standard VGA modes) + */ +static void glue(vga_draw_line8_, DEPTH)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + uint32_t *palette; + int x; + + palette = s1->last_palette; + width >>= 3; + for(x = 0; x < width; x++) { + ((PIXEL_TYPE *)d)[0] = palette[s[0]]; + ((PIXEL_TYPE *)d)[1] = palette[s[1]]; + ((PIXEL_TYPE *)d)[2] = palette[s[2]]; + ((PIXEL_TYPE *)d)[3] = palette[s[3]]; + ((PIXEL_TYPE *)d)[4] = palette[s[4]]; + ((PIXEL_TYPE *)d)[5] = palette[s[5]]; + ((PIXEL_TYPE *)d)[6] = palette[s[6]]; + ((PIXEL_TYPE *)d)[7] = palette[s[7]]; + d += BPP * 8; + s += 8; + } +} + +#endif /* DEPTH != 15 */ + + +/* XXX: optimize */ + +/* + * 15 bit color + */ +static void glue(vga_draw_line15_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ +#if DEPTH == 15 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) + memcpy(d, s, width * 2); +#else + int w; + uint32_t v, r, g, b; + + w = width; + do { + v = lduw_raw((void *)s); + r = (v >> 7) & 0xf8; + g = (v >> 2) & 0xf8; + b = (v << 3) & 0xf8; + ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s += 2; + d += BPP; + } while (--w != 0); +#endif +} + +/* + * 16 bit color + */ +static void glue(vga_draw_line16_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ +#if DEPTH == 16 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) + memcpy(d, s, width * 2); +#else + int w; + uint32_t v, r, g, b; + + w = width; + do { + v = lduw_raw((void *)s); + r = (v >> 8) & 0xf8; + g = (v >> 3) & 0xfc; + b = (v << 3) & 0xf8; + ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s += 2; + d += BPP; + } while (--w != 0); +#endif +} + +/* + * 24 bit color + */ +static void glue(vga_draw_line24_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ + int w; + uint32_t r, g, b; + + w = width; + do { +#if defined(TARGET_WORDS_BIGENDIAN) + r = s[0]; + g = s[1]; + b = s[2]; +#else + b = s[0]; + g = s[1]; + r = s[2]; +#endif + ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s += 3; + d += BPP; + } while (--w != 0); +} + +/* + * 32 bit color + */ +static void glue(vga_draw_line32_, PIXEL_NAME)(VGACommonState *s1, uint8_t *d, + const uint8_t *s, int width) +{ +#if DEPTH == 32 && defined(HOST_WORDS_BIGENDIAN) == defined(TARGET_WORDS_BIGENDIAN) && !defined(BGR_FORMAT) + memcpy(d, s, width * 4); +#else + int w; + uint32_t r, g, b; + + w = width; + do { +#if defined(TARGET_WORDS_BIGENDIAN) + r = s[1]; + g = s[2]; + b = s[3]; +#else + b = s[0]; + g = s[1]; + r = s[2]; +#endif + ((PIXEL_TYPE *)d)[0] = glue(rgb_to_pixel, PIXEL_NAME)(r, g, b); + s += 4; + d += BPP; + } while (--w != 0); +#endif +} + +#undef PUT_PIXEL2 +#undef DEPTH +#undef BPP +#undef PIXEL_TYPE +#undef PIXEL_NAME +#undef BGR_FORMAT