Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

chip_cirrus_gd5446.c

/*
 * $Id: chip_cirrus_gd5446.c,v 1.112 2009-01-28 12:59:17 potyra Exp $
 *
 * Copyright (C) 2003-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 *
 * Implementation of a Cirrus Logic CL-GD5446
 *
 * Inspiration for the bitblitter and some other details
 * came from qemu's cirrus_vga. Thanks for that!
 *
 * LIMITATIONS:
 * - Doesn't handle monochrome register shift 3dx -> 3bx.
 * - Nothing of the video support stuff is implemented.
 * - No DDC2B support.
 *
 * STILL NOT TESTED:
 * - some bitblts
 * - system-to-screen bitblts
 * - byte-swapping apertures
 */

/*
 * Debugging.
 * If CIRRUS_DEBUG is:
 *   0: important warnings only
 *   1: all warnings
 *   2: debugging
 *   3: overkill ;-)
 */

#define CIRRUS_DEBUG 0
/*
#define CIRRUS_DEBUG_BITBLT
#define CIRRUS_DEBUG_GEN
#define CIRRUS_DEBUG_CONFIGSPACE
*/

#include "config.h"

#include "compiler.h"

#include <inttypes.h>

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include "fixme.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "pci.h"
#include "glue-log.h"
#include "glue-main.h"
#include "glue-shm.h"

#include "chip_cirrus_gd5446.h"

#define COMP "chip_cirrus_gd5446"

/*
 * Needed to concatenate function names.
 */

#define xpaste(front, back) front ## back
#define paste(front, back) xpaste(front, back)

/*
 * With a given VSYNC rate, a total refresh rate of
 *
 *         VSYNC * (REFRESH_CYCLES + 1) Hz
 *
 * is resulting.
 */

#define VSYNC 5
#define REFRESH_CYCLES 25

/*
 * vga_core defines
 */

#define PA_VGABIOS      0x000c0000
#define SZ_VGABIOS      (64*1024)   /* Maximum size of ROM */
#define PA_VIDEO_MEMORY_LOW   0x000a0000
#define SZ_VIDEO_MEMORY_LOW   (128*1024)
#define VGA_CORE_VRAM_SIZE    (256*1024)  /* VRAM reserved for vga core */
                                /* doesn't work with 128k */

/*
 * cirrus defines
 */

#define CIRRUS_IOPORT  0x3c0    /* standard iorange for all vgas is 3c0-3df */

#define CIRRUS_MAX_VRAM_SIZE  0x400000
#define CIRRUS_MAX_VRAM_MASK (0x400000 - 1)

#define DISPLAY_MEMORY_ADDRESS \
   (pci_getconfigl(cpssp->pci_config_space, PCI_BASE_ADDRESS_0) \
 & (~0x01UL))                                  /* PCI10 */
#define DISPLAY_MEMORY_SIZE          0x2000000 /* 32 MB */
#define DISPLAY_MEMORY_APERTURE_SIZE 0x0400000 /*  4 MB */

#define REGISTER_MEMORY_ADDRESS \
   (pci_getconfigl(cpssp->pci_config_space, PCI_BASE_ADDRESS_1) \
 & (~0x01UL))                           /* PCI14 */
#define REGISTER_MEMORY_SIZE  0x01000   /* 4 KB */

#define BIOS_MEMORY_ADDRESS \
   (pci_getconfigl(cpssp->pci_config_space, PCI_ROM_ADDRESS) \
 & (~0x01UL))                           /* PCI30 */
#define BIOS_MEMORY_SIZE      0x10000   /* 64 KB - real hw has only a 32 */

#define BANK_MEMORY_ADDRESS   0xa0000   /* 0xa0000 - 0xaffff */
#define BANK_MEMORY_SIZE      0x10000

#define LEGACY_MMIO_ADDRESS   0xb8000   /* 0xb8000 - 0xbffff */
#define LEGACY_MMIO_SIZE      0x08000

/* Defines for bitblt mode register */
#define BITBLT_COLOR_EXPAND                   0x80
#define BITBLT_8X8_PATTERN_COPY               0x40
#define BITBLT_COLOR_EXPAND_WIDTH             0x30
#define BITBLT_TRANSPARENCY                   0x08
#define BITBLT_SOURCE_SYSTEM_MEMORY           0x04
#define BITBLT_REVERSE                        0x01

/* Defines for bitblt extended mode register */
#define BITBLT_SYNCHRONOUS_DISPLAY_SWITCHING  0x10
#define BITBLT_BACKGROUND_ONLY_CLIPPING       0x08
#define BITBLT_SOLID_COLOR_FILL               0x04
#define BITBLT_INVERT_COLOR_EXPAND            0x02
#define BITBLT_32BIT_GRANULARITY              0x01

/* Defines for bitblt start/status register */
#define BITBLT_AUTOSTART                      0x80
#define BITBLT_SYSTEM_SOURCE_LOCATION           0x40
#define BITBLT_BUFFERED_REGISTER_STATUS       0x10
#define BITBLT_RESET                          0x04
#define BITBLT_START                          0x02
#define BITBLT_STATUS                         0x01

/* Defines for bitblt destination left-side clipping register */
#define BITBLT_SYSTEM_TO_SCREEN_DWORD_POINTER 0x60

/* Buffer for one line of source data (system-to-screen operation) */
#define BITBLT_LINE_BUFFER_SIZE (2048 * 4)
#define BITBLT_LINE_BUFFER_START CIRRUS_MAX_VRAM_SIZE

/* Bitblt raster operation functions */

struct cpssp {

      uint32_t pci_config_space[16];  /* 64 bytes */

      unsigned int state_power;
      struct sig_pci_bus_main *port_pci_bus;
      struct sig_vga *port_vga;
      struct sig_cs *port_mem_cs[8];
      struct sig_cs *port_rom_cs;

      enum {
            VGA,
            CIRRUS
      } mode;

      enum {
            B64_NOSWAP_2MB_2B,
            B64_NOSWAP_2MB_1B,
            B64_NOSWAP_1MB_2B,
            B64_NOSWAP_1MB_1B,
            B64_SWAP,
            B32
      } mem_access_mode;

      unsigned int scanline;
      unsigned long offset;

      uint8_t colregs[259][3];       /* 256 + 3 dac extended colors */

      /* BitBLT Registers */
      /* (index 0x3ce, value 0x3cf) */
      unsigned char blt_color_exp_fg_bg[6];             /* 0x10-0x15 5.1  */
      unsigned char blt_width[2];                       /* 0x20 0x21 5.2  */
      unsigned char blt_height[2];                      /* 0x22 0x23 5.3  */
      unsigned char blt_dest_pitch[2];                  /* 0x24 0x25 5.4  */
      unsigned char blt_source_pitch[2];                /* 0x26 0x27 5.5  */
      unsigned char blt_dest_start[3];                  /* 0x28-0x2a 5.6  */
      unsigned char blt_source_start[3];                /* 0x2c-0x2e 5.7  */
      unsigned char blt_dest_left_side_clipping;        /* 0x2f      5.8  */
      unsigned char blt_mode;                           /* 0x30      5.9  */
      unsigned char blt_start_status;                   /* 0x31      5.10 */
      unsigned char blt_rop;                            /* 0x32      5.11 */
      unsigned char blt_mode_extensions;                /* 0x33      5.12 */
      unsigned char blt_transp_blt_key_color[2];        /* 0x34 0x35 5.13 */

      /* Miscellaneous Extension Registers */
      /* (index 0x3c4, value 0x3c5) */
      unsigned char ext_key;                            /* 0x06      8.1  */
      unsigned char ext_ext_sequencer_mode;             /* 0x07      8.2  */
      unsigned char ext_ddc2b_eeprom_control;           /* 0x08      8.3  */
      unsigned char ext_scratch_pad_01[2];              /* 0x09 0x0a 8.4  */
      unsigned char ext_vclk_numerator[4];              /* 0x0b-0x0e 8.5  */
      unsigned char ext_dram_control;                   /* 0x0f      8.6  */
      unsigned char ext_graph_curs_x_pos;               /* 0x10      8.7  */
      unsigned char ext_graph_curs_y_pos;               /* 0x11      8.8  */
      unsigned char ext_graph_curs_attr;                /* 0x12      8.9  */
      unsigned char ext_graph_curs_pattern_addr_offset; /* 0x13      8.10 */
      unsigned char ext_scratch_pad_23[2];              /* 0x14 0x15 8.11 */
      unsigned char ext_display_fifo_threshold_control; /* 0x16      8.12 */
      unsigned char ext_config_rb_ext_control;          /* 0x17      8.13 */
      unsigned char ext_sig_gen_control;                /* 0x18      8.14 */
      unsigned char ext_sig_gen_result_lb;              /* 0x19      8.15 */
      unsigned char ext_sig_gen_result_hb;              /* 0x1a      8.16 */
      unsigned char ext_vclk_denominator[4];            /* 0x1b-0x1e 8.17 */
      unsigned char ext_mclk_select;                    /* 0x1f      8.18 */
      /* (index 0x3ce, value 0x3cf) */
      unsigned char ext_offset_reg_0;                   /* 0x09      8.19 */
      unsigned char ext_offset_reg_1;                   /* 0x0a      8.20 */
      unsigned char ext_graph_contr_mode_ext;           /* 0x0b      8.21 */
      unsigned char ext_color_chroma_key_comp;          /* 0x0c      8.22 */
      unsigned char ext_color_mask_chroma_key;          /* 0x0d      8.23 */
      unsigned char ext_power_management;               /* 0x0e      8.24 */
      /* readonline, generated from scanline */         /* 0x16      8.25 */
      unsigned char ext_act_disp_line_rb_1;             /* 0x17      8.26 */
      unsigned char ext_ext_dram_controls;              /* 0x18      8.27 */
      unsigned char ext_gpio_port_config;               /* 0x19      8.28 */
      unsigned char ext_scratch_pad_45[2];              /* 0x1a 0x1b 8.29 */
      /* (index 03d4, value 0x3d5) */
      unsigned char ext_interlace_end;                  /* 0x19      8.30 */
      unsigned char ext_misc_control;                   /* 0x1a      8.31 */
      unsigned char ext_ext_disp_controls;              /* 0x1b      8.32 */
      unsigned char ext_sync_adjust_genlock;            /* 0x1c      8.33 */
      unsigned char ext_overlay_ext_control;            /* 0x1d      8.34 */
      unsigned char ext_part_status;                    /* 0x25      8.35 */
      unsigned char ext_id;                             /* 0x27      8.36 */
      /* (register 0x3c6) (same as 4.14 pixel mask) */
      unsigned char ext_hidden_dac;                     /*           8.37 */
      unsigned char ext_hidden_dac_counter;

      /* current position of hw cursor */
      unsigned int hw_cursor_x;
      unsigned int hw_cursor_y;

      /* shadow versions of SR0 and SR1 for BitBLT color expansion */
      unsigned char shadow_gr0;
      unsigned char shadow_gr1;

      /* byte-swapping reads and writes might not always be whole chunks */
      unsigned char byte_swap_cache[3];
      int byte_swap_cache_fill;

      /* state of the bitblt engine */
      struct bitblt {
            unsigned long fg_color;
            unsigned long bg_color;
            unsigned int width;
            unsigned int height;
            int dest_pitch; /* pitches may be negative */
            int src_pitch;
            unsigned long destination_pointer;
            unsigned long source_pointer;
            unsigned char mode;
            unsigned char color_expand_width;
            unsigned char mode_extensions;
            unsigned char dest_left_side_clipping;
            unsigned char rop;
            unsigned char key_color[2];

            void (*function)(struct cpssp *cpssp);

            /* we cache scanlines for system-to-screen bitblts */
            unsigned long line_pointer;
            unsigned long line_pointer_end;
            unsigned long src_counter;

            /* line buffer for bitblt engine */
            uint8_t line_buffer[BITBLT_LINE_BUFFER_SIZE];
      } bitblt;

      /* state of the i2c/ddc bus */
      struct {
            /* value of the clock signal */
            bool clk;
            /* value of the data signal */
            bool data;
      } i2c_ddc_bus;

#define STATE
#define NAME            vga
#define NAME_(x)  vga_ ## x
#define SNAME           "vga"
#include "arch_vga.c"
#undef SNAME
#undef NAME_
#undef NAME
#undef STATE
};

/*
 * Memory and color register access functions.
 * (Needed by both vga core and native cirrus core.)
 */

static inline __attribute__((always_inline)) uint8_t
video_col_get(struct cpssp * cpssp, unsigned int nr, unsigned int rgb)
{
      assert(nr < 259);
      assert(rgb < 3);

      return cpssp->colregs[nr][rgb];
}

static inline __attribute__((always_inline)) void
video_col_set(struct cpssp * cpssp, unsigned int nr, unsigned int rgb, uint8_t val)
{
      assert(nr < 259);
      assert(rgb < 3);

      cpssp->colregs[nr][rgb] = val;
}

/* 
 * Valid memory configurations, due to gd5446trm:
 *
 * (The mappings from linear frame buffer adresses
 * to the memory chip array represent the current
 * implementation below and are tested against the
 * memory probing algorithm of a real Cirrus VGA BIOS.)
 *
 * 1MB: 2 x 256Kx16, 32 bit mode, half a bank
 *      address mask: 0x0fffff, mode: B32
 *       2            1
 *      10 9876 5432 1098 7654 3210
 *      00 1111 1111 1111 1111 1111
 *      -- RRRR RRRR RCCC CCCC CCMS
 *          RRR RRRR RRCC CCCC CCCS (addr)
 *                               1M (chip)
 *
 * 2MB: 4 x 256Kx16, 64 bit mode, 1 bank
 *      address mask: 0x1fffff, mode: B64_NOSWAP_2MB_1B
 *       2            1
 *      10 9876 5432 1098 7654 3210
 *      01 1111 1111 1111 1111 1111
 *      -R RRRR RRRR CCCC CCCC CMMS
 *          RRR RRRR RRCC CCCC CCCS (addr)
 *                               MM (chip)
 *
 * 4MB: 8 x 256Kx16, 64 bit mode, 2 banks
 *      address mask: 0x3fffff, mode: B64_NOSWAP_2MB_2B
 *       2            1
 *      10 9876 5432 1098 7654 3210
 *      11 1111 1111 1111 1111 1111
 *      BR RRRR RRRR CCCC CCCC CMMS
 *          RRR RRRR RRCC CCCC CCCS (addr)
 *                              BMM (chip)
 *
 * 1MB: 4 x 128Kx16, 64 bit mode, 1 bank
 *      address mask: 0x0fffff, mode: B64_NOSWAP_1MB_1B
 *       2            1
 *      10 9876 5432 1098 7654 3210
 *      00 1111 1111 1111 1111 1111
 *      -- RRRR RRRR RCCC CCCC CMMS
 *           RR RRRR RRRC CCCC CCCS (addr)
 *                               MM (chip)
 *
 * 2MB: 8 x 128Kx16, 64 bit mode, 2 banks
 *      address mask: 0x1fffff, mode: B64_NOSWAP_1MB_2B 
 *       2            1
 *      10 9876 5432 1098 7654 3210
 *      01 1111 1111 1111 1111 1111
 *      -B RRRR RRRR RCCC CCCC CMMS
 *           RR RRRR RRRC CCCC CCCS (addr)
 *                              BMM (chip)
 *
 * 3MB: 4 x 128Kx16, 4 x 256Kx16, 64 bit mode, 2 banks, bank swap
 *      address mask: 0x3fffff, mode: B64_SWAP
 *       2            1
 *      10 9876 5432 1098 7654 3210
 *      11 1111 1111 1111 1111 1111
 *      0R RRRR RRRR CCCC CCCC CMMS (Bank 1, 256Kx16s)
 *          RRR RRRR RRCC CCCC CCCS (addr)
 *                              1MM (chip)
 *      1- RRRR RRRR RCCC CCCC CMMS (Bank 0, 128Kx16s)
 *           RR RRRR RRRC CCCC CCCS (addr)
 *                              0MM (chip)
 *
 * S = byte select (within chip)
 * M = module (=chip) number (within bank)
 * C = column address
 * R = row address
 * B = bank select
 * - = don't care
 */

#define ADDRESS_MAP switch(cpssp->mem_access_mode) {        \
                case B64_NOSWAP_2MB_2B:               \
                     chip = ((pa & 6) >> 1)           \
                        | ((pa & 0x200000) >> 19);    \
                     addr = (pa & 1)                  \
                        | ((pa & 0x1ffff8) >> 2);     \
                     break;                     \
                                                \
                case B64_NOSWAP_2MB_1B:               \
                     chip = ((pa & 6) >> 1);          \
                     addr = (pa & 1)                  \
                        | ((pa & 0x1ffff8) >> 2);     \
                     break;                     \
                                                \
                case B32:                             \
                     chip = (pa & 2) >> 1 | 2;        \
                     addr = (pa & 1)                  \
                        | ((pa & 0x0ffffc) >> 1);     \
                     break;                     \
                                                \
                case B64_NOSWAP_1MB_2B:               \
                     chip = ((pa & 6) >> 1)           \
                        | ((pa & 0x100000) >> 18);    \
                     addr = (pa & 1)                  \
                        | ((pa & 0x0ffff8) >> 2);     \
                     break;                     \
                                                \
                case B64_NOSWAP_1MB_1B:               \
                     chip = ((pa & 6) >> 1);          \
                     addr = (pa & 1)                  \
                        | ((pa & 0x0ffff8) >> 2);     \
                     break;                     \
                                                \
                case B64_SWAP:                        \
                     if (pa & 0x200000) {             \
                        chip = ((pa & 6) >> 1);       \
                        addr = (pa & 1)               \
                             | ((pa & 0x0ffff8) >> 2);      \
                     } else {                   \
                        chip = ((pa & 6) >> 1) | 4;   \
                        addr = (pa & 1)               \
                             | ((pa & 0x1ffff8) >> 2);      \
                     }                          \
                     break;                     \
                                                \
                default:                              \
                     assert(0);                       \
                    }

/*
 * There is still one quite ugly hack:
 * the bit blitter line buffer memory is made
 * accessible through the same video_read/video_write
 * functions as the graphics memory to allow a
 * transparent implementation of the blitting
 * functions. (But, of course, the bit blitter line
 * buffer is _not_ directly accessible via the PCI
 * bus, only indirectly when writing to the system-
 * to-screen aperture!)
 */
static uint8_t 
video_readb(struct cpssp * cpssp, unsigned long pa)
{
      int chip;
      unsigned long addr;
      uint8_t val;

      if (pa < BITBLT_LINE_BUFFER_START) {

            ADDRESS_MAP;

            sig_cs_readb(cpssp->port_mem_cs[chip], cpssp, &val, addr);
            return val;

      } else {
            pa -= BITBLT_LINE_BUFFER_START;
            assert(pa < BITBLT_LINE_BUFFER_SIZE);
            return cpssp->bitblt.line_buffer[pa];
      }
}

static uint16_t
video_readw(struct cpssp * cpssp, unsigned long pa)
{
      int chip;
      unsigned long addr;
      uint16_t val;

      if (pa < BITBLT_LINE_BUFFER_START) {
            assert((pa & 1) == 0);

            ADDRESS_MAP;

            sig_cs_readw(cpssp->port_mem_cs[chip], cpssp, &val, addr);
            return val;

      } else {
            pa -= BITBLT_LINE_BUFFER_START;
            assert(pa < BITBLT_LINE_BUFFER_SIZE);
            return *((uint16_t*)&cpssp->bitblt.line_buffer[pa]);
      }
}

static uint32_t
video_readl(struct cpssp * cpssp, unsigned long pa)
{
      int chip;
      unsigned long addr;
      uint16_t val_lo;
      uint16_t val_hi;

      if (pa < BITBLT_LINE_BUFFER_START) {
            assert((pa & 3) == 0);

            ADDRESS_MAP;

            sig_cs_readw(cpssp->port_mem_cs[chip+0], cpssp, &val_lo, addr);
            sig_cs_readw(cpssp->port_mem_cs[chip+1], cpssp, &val_hi, addr);
            return (val_lo << 0) | (val_hi << 16);

      } else {
            pa -= BITBLT_LINE_BUFFER_START;
            assert(pa < BITBLT_LINE_BUFFER_SIZE);
            return *((uint32_t*)&cpssp->bitblt.line_buffer[pa]);
      }
}

static void
video_writeb(struct cpssp * cpssp, unsigned long pa, uint8_t val)
{
      int chip;
      unsigned long addr;

      if (pa < BITBLT_LINE_BUFFER_START) {

            ADDRESS_MAP;

            sig_cs_writeb(cpssp->port_mem_cs[chip], cpssp, val, addr);

      } else {
            cpssp->bitblt.line_buffer[pa - BITBLT_LINE_BUFFER_START] = val;
      }
}

static void
video_writew(struct cpssp * cpssp, unsigned long pa, uint16_t val)
{
      int chip;
      unsigned long addr;

      if (pa < BITBLT_LINE_BUFFER_START) {
            assert((pa & 1) == 0);

            ADDRESS_MAP;

            sig_cs_writew(cpssp->port_mem_cs[chip], cpssp, val, addr);

      } else {
            *((uint16_t*) &cpssp->bitblt.line_buffer[pa - BITBLT_LINE_BUFFER_START]) = val;
      }
}

static void
video_writel(struct cpssp * cpssp, unsigned long pa, uint32_t val)
{
      int chip;
      unsigned long addr;

      if (pa < BITBLT_LINE_BUFFER_START) {
            assert((pa & 3) == 0);

            ADDRESS_MAP;

            sig_cs_writew(cpssp->port_mem_cs[chip+0], cpssp, (uint16_t)((val >>  0) & 0xffff), addr);
            sig_cs_writew(cpssp->port_mem_cs[chip+1], cpssp, (uint16_t)((val >> 16) & 0xffff), addr);

      } else {
            *((uint32_t*) &cpssp->bitblt.line_buffer[pa - BITBLT_LINE_BUFFER_START]) = val;
      }
}

#undef ADDRESS_MAP

/*
 * Include core VGA functionality
 */

#define ARCH_VGA_PORT CIRRUS_IOPORT
#define BEHAVIOUR
#define NAME            vga
#define NAME_(x)  vga_ ## x
#define SNAME           "vga"
#include "arch_vga.c"
#undef SNAME
#undef NAME_
#undef NAME
#undef BEHAVIOUR
#undef ARCH_VGA_PORT

/*
 * Function prototypes
 */
static void _cirrus_bitblt_start(struct cpssp *cpssp);
static void _cirrus_write_blt_start(struct cpssp *cpssp,
            unsigned char val);
static unsigned char _cirrus_vga_inb(struct cpssp *cpssp,
            unsigned short port);
static void _cirrus_vga_outb(struct cpssp *cpssp,
            unsigned char val, unsigned short port);
static void _cirrus_bitblt_system_to_screen_next(struct cpssp *cpssp);

/*************************************************************************/
/*                                                                       */
/* Memory space access functions                                         */
/*                                                                       */
/*************************************************************************/

#define MEGABYTE                      (1024 * 1024)

static void
_cirrus_vga_mmio_vga_read(
      struct cpssp *cpssp,
      unsigned short offset,
      void *_to,
      unsigned long len
)
{
      unsigned char *to;
      unsigned short count;

      assert(offset < 0x20);

      /* Real hardware only accepts byte or word reads,
         but we get and accept dwords, too. */

      to = (unsigned char *) _to;
      count = 0;
      offset += CIRRUS_IOPORT;

      while (count < len) {
            /* See table 9-3 in trm */
            switch (offset + count) {
            case 0x3C0 ... 0x3CA:
            case 0x3CC:
            case 0x3CE ... 0x3CF:
            case 0x3D4 ... 0x3D5:
            case 0x3DA:
                  *to = _cirrus_vga_inb(cpssp, offset + count);
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         "Read 0x%02hx from %3X\n",
                         *to, offset + count);
#endif
                  break;
            default:
#if (0 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Unknown register %3X\n",
                         offset + count);
#endif
                  break;
            };
            count++;
            to++;
      }
}

static void
_cirrus_vga_mmio_bitblt_read(
      struct cpssp *cpssp,
      unsigned short offset,
      void *_to,
      unsigned long len
)
{
      unsigned char *to;
      unsigned short count;

      assert(0x100 <= offset && offset < 0x200);

      /* Real hardware only accepts byte or word reads,
         but we get and accept dwords, too. */

      to = (unsigned char *) _to;
      count = 0;

      while (count < len) {
            /*
             * See table 9-4 in trm.
             */
            switch (offset + count) {

            case 0x100: /* GR0 */
                  *to = cpssp->shadow_gr0;
                  break;
            case 0x101: /* GR10 */
                  *to = cpssp->blt_color_exp_fg_bg[0];
                  break;
            case 0x102: /* GR12 */
                  *to = cpssp->blt_color_exp_fg_bg[2];
                  break;
            case 0x103: /* GR14 */
                  *to = cpssp->blt_color_exp_fg_bg[4];
                  break;
            case 0x104: /* GR1 */
                  *to = cpssp->shadow_gr1;
                  break;
            case 0x105: /* GR11 */
                  *to = cpssp->blt_color_exp_fg_bg[1];
                  break;
            case 0x106: /* GR13 */
                  *to = cpssp->blt_color_exp_fg_bg[3];
                  break;
            case 0x107: /* GR15 */
                  *to = cpssp->blt_color_exp_fg_bg[5];
                  break;
            case 0x108: /* GR20 */
                  *to = cpssp->blt_width[0];
                  break;
            case 0x109: /* GR21 */
                  *to = cpssp->blt_width[1];
                  break;
            case 0x10A: /* GR22 */
                  *to = cpssp->blt_height[0];
                  break;
            case 0x10B: /* GR23 */
                  *to = cpssp->blt_height[1];
                  break;
            case 0x10C: /* GR24 */
                  *to = cpssp->blt_dest_pitch[0];
                  break;
            case 0x10D: /* GR25 */
                  *to = cpssp->blt_dest_pitch[1];
                  break;
            case 0x10E: /* GR26 */
                  *to = cpssp->blt_source_pitch[0];
                  break;
            case 0x10F: /* GR27 */
                  *to = cpssp->blt_source_pitch[1];
                  break;
            case 0x110: /* GR28 */
                  *to = cpssp->blt_dest_start[0];
                  break;
            case 0x111: /* GR29 */
                  *to = cpssp->blt_dest_start[1];
                  break;
            case 0x112: /* GR2A */
                  *to = cpssp->blt_dest_start[2];
                  break;
            case 0x114: /* GR2C */
                  *to = cpssp->blt_source_start[0];
                  break;
            case 0x115: /* GR2D */
                  *to = cpssp->blt_source_start[1];
                  break;
            case 0x116: /* GR2E */
                  *to = cpssp->blt_source_start[2];
                  break;
            case 0x117: /* GR2F */
                  *to = cpssp->blt_dest_left_side_clipping;
                  break;
            case 0x118: /* GR30 */
                  *to = cpssp->blt_mode;
                  break;
            case 0x11A: /* GR32 */
                  *to = cpssp->blt_rop;
                  break;
            case 0x11B: /* GR33 */
                  *to = cpssp->blt_mode_extensions;
                  break;
            case 0x11C: /* GR34 */
                  *to = cpssp->blt_transp_blt_key_color[0];
                  break;
            case 0x11D: /* GR35 */
                  *to = cpssp->blt_transp_blt_key_color[1];
                  break;
            case 0x140: /* GR31 */
                  *to = cpssp->blt_start_status;
                  break;

            case 0x113:           /* GR2B reserved */
            case 0x119:           /* ? reserved */
            case 0x11E:           /* ? reserved */
            case 0x11F:           /* ? reserved */
            case 0x120 ... 0x13F: /* ? reserved */
            case 0x141 ... 0x1FF: /* ? reserved */
#ifdef CIRRUS_DEBUG_BITBLT
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Reserved register at offset 0x%03x\n",
                         offset + count);
#endif
                  break;

            default:
#ifdef CIRRUS_DEBUG_BITBLT
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Unknown register at offset 0x%03x\n",
                         offset + count);
#endif
                  break;
            };
#if (2 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   " offset 0x%03x -> 0x%02hx\n",
                   offset + count, *to);
#endif
            count++;
            to++;
      }
}

static void
_cirrus_vga_linear_fb_read(
      struct cpssp *cpssp,
      unsigned long offset,
      void *_to,
      unsigned long len
)
{
      unsigned char *to;
      int count;

#if (1 < CIRRUS_DEBUG)
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
             "Display memory read at offset 0x%08lx length %ld\n",
             offset, len);
#endif

      to = (uint8_t *) _to;
      count = 0;

      if (offset < 4 * MEGABYTE) {
            /* no swap */

            if (len == 4) {
                  *((uint32_t*)to) = video_readl(cpssp, offset);

            } else if (len == 1) {
                  *to = video_readb(cpssp, offset);

            } else if (len == 2) {
                  if (unlikely(offset & 1)) {
                        *(to+0) = video_readb(cpssp, offset+0);
                        *(to+1) = video_readb(cpssp, offset+1);
                  } else {
                        *((uint16_t*)to) = video_readw(cpssp, offset);
                  }

            } else if (len == 3) {
                  if (offset & 1) {
                        *to = video_readb(cpssp, offset);
                        *((uint16_t*)to+1) = video_readw(cpssp, offset+1);
                  } else {
                        *((uint16_t*)to) = video_readw(cpssp, offset);
                        *(to+2) = video_readb(cpssp, offset+2);
                  }

            } else {
                  /* should not happen, though */
                  while (count < len) {
                        *to = video_readb(cpssp, offset + count);
                        count++;
                        to++;
                  }
            }

      } else if (4 * MEGABYTE <= offset && offset < 8 * MEGABYTE) {
            /* word swap */

            offset -= 4 * MEGABYTE;

            while (count < len) {
                  if ((offset + count) % 2) {
                        *to = video_readb(cpssp, offset + count - 1);
                  } else {
                        *to = video_readb(cpssp, offset + count + 1);
                  }
                  count++;
                  to++;
            }

      } else if (8 * MEGABYTE <= offset && offset < 12 * MEGABYTE) {
            /* dword swap */

            offset -= 8 * MEGABYTE;

            while (count < len) {
                  switch ((offset + count) % 4) {
                  case 0:
                        *to = video_readb(cpssp, offset + count + 3);
                        break;
                  case 1:
                        *to = video_readb(cpssp, offset + count + 1);
                        break;
                  case 2:
                        *to = video_readb(cpssp, offset + count - 1);
                        break;
                  case 3:
                        *to = video_readb(cpssp, offset + count - 3);
                        break;
                  default:
                        assert(0);
                  }
                  count++;
                  to++;
            }

      } else if (12 * MEGABYTE <= offset && offset < 16 * MEGABYTE) {

#if (0 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                   "Video aperture read not implemented\n");
#endif

      } else if (16 * MEGABYTE <= offset && offset < 20 * MEGABYTE) {

#if (0 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                   "System-to-screen memory reads not supported\n");
#endif

      } else if (20 * MEGABYTE <= offset && offset < 24 * MEGABYTE) {

#if (0 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                   "System-to-screen memory reads not supported\n");
#endif

      } else if (24 * MEGABYTE <= offset && offset < 28 * MEGABYTE) {

#if (0 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                   "System-to-screen memory reads not supported\n");
#endif

      } else if (28 * MEGABYTE <= offset && offset < 32 * MEGABYTE) {

#if (0 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                   "Memory reads of unused 28-32MB aperture\n");
#endif

      } else {
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                   "Memory read outside 32MB window\n");
      }
}

static unsigned long
_cirrus_vga_page_get_address(struct cpssp *cpssp, unsigned long cpu_addr)
{
      unsigned long address;

      if (!(cpssp->ext_graph_contr_mode_ext & 0x01)) {

            /* GRB[0] is 0 -> single page */

            if (!(cpssp->ext_graph_contr_mode_ext & 0x20)) {
                  /* GRB[5] is 0 -> 4KB granularity */
                  address = ((cpssp->ext_offset_reg_0 << 12) /* GR9 */
                           + cpu_addr)
                        & 0xfffff; /* truncate to 20 bits */

            } else {
                  /* GRB[5] is 1 -> 16KB granularity */
                  address = ((cpssp->ext_offset_reg_0 << 14) /* GR9 */
                           + cpu_addr)
                        & 0x3fffff; /* truncate to 22 bits */
            }
      } else {

            /* GRB[0] is 1 -> dual page */

            if (!(cpssp->ext_graph_contr_mode_ext & 0x20)) {
                  /* GRB[5] is 0 -> 4KB granularity */

                  if (!(cpu_addr >> 15)) {
                        /* cpu_addr[15] is 0 -> offset register 0 */
                        address = ((cpssp->ext_offset_reg_0 << 12) /* GR9 */
                                 + (cpu_addr & 0x7fff)) /* cpu_addr[0:14] */
                              & 0xfffff; /* truncate to 20 bits */

                  } else {
                        /* cpu_addr[15] is 1 -> offset register 1 */
                        address = ((cpssp->ext_offset_reg_1 << 12) /* GRA */
                                 + (cpu_addr & 0x7fff)) /* cpu_addr[0:14] */
                              & 0xfffff; /* truncate to 20 bits */
                  }

            } else {
                  /* GRB[5] is 1 -> 16KB granularity */

                  if (!(cpu_addr >> 15)) {
                        /* cpu_addr[15] is 0 -> offset register 0 */
                        address = ((cpssp->ext_offset_reg_0 << 14) /* GR9 */
                                 + (cpu_addr & 0x7fff)) /* cpu_addr[0:14] */
                              & 0x3fffff; /* truncate to 22 bits */

                  } else {
                        /* cpu_addr[15] is 1 -> offset register 1 */
                        address = ((cpssp->ext_offset_reg_1 << 14) /* GRA */
                                 + (cpu_addr & 0x7fff)) /* cpu_addr[0:14] */
                              & 0x3fffff; /* truncate to 22 bits */
                  }
            }
      }

      if ((cpssp->ext_graph_contr_mode_ext & 0x14) == 0x14) {

            /* GRB[2] and GRB[4] enabled -> BY16 addressing */
            address <<= 4;

      } else if (cpssp->ext_graph_contr_mode_ext & 0x02) {

            /* GRB[1] enabled -> BY8 addressing */
            address <<= 3;
      }

      address &= CIRRUS_MAX_VRAM_MASK;

      return address;
}

static uint8_t
_cirrus_vga_page_readb(struct cpssp *cpssp, unsigned long offset)
{
      unsigned long address;

      address = _cirrus_vga_page_get_address(cpssp, offset);

#if (2 < CIRRUS_DEBUG)
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
             "offset 0x%08lx == address 0x%08lx -> value 0x%02hx\n",
             offset, address, video_readb(cpssp, address));
#endif

      return video_readb(cpssp, address);
}

static void
_cirrus_vga_page_read(
      struct cpssp *cpssp,
      unsigned long offset,
      void *_to,
      unsigned long len
)
{
      uint8_t *to;
      int count;

      /*
       * CL-GD542X/3X compatibility:
       * Access framebuffer through one 64KB or
       * two 32KB windows in 0xa0000 - 0xaffff.
       */
      to = (uint8_t *) _to;
      count = 0;

      while (count < len) {
            *to = _cirrus_vga_page_readb(cpssp, offset + count);
            count++;
            to++;
      }
}

static long
cirrus_vga_read(
      void *_cpssp,
      unsigned long addr,
      void *to,
      unsigned long len
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      /*
       * Legacy VGA access?
       */
      if (cpssp->mode == VGA) {
            if (vga_read(cpssp, addr, to, len) == 0) {
                  return 0;
            }
      }

      /*
       * Display memory access?
       */
      if ((pci_getconfigb(cpssp->pci_config_space, 0x04) & PCI_COMMAND_MEMORY)
       && DISPLAY_MEMORY_ADDRESS <= addr
       && addr < DISPLAY_MEMORY_ADDRESS + DISPLAY_MEMORY_SIZE
       && (cpssp->ext_ext_sequencer_mode & 0xf0) != 0) {
            if (unlikely(DISPLAY_MEMORY_ADDRESS + DISPLAY_MEMORY_SIZE - 256 <= addr
                        && (cpssp->ext_config_rb_ext_control & 0x44) == 0x44)) {
                  addr &= 0xff;
                  _cirrus_vga_mmio_bitblt_read(cpssp,
                              (unsigned short) addr + 0x100, to, len);

            } else {
                  _cirrus_vga_linear_fb_read(cpssp,
                              addr - DISPLAY_MEMORY_ADDRESS, to, len);
            }
            return 0;
      }

      /*
       * Bank-based addressing (between 0xa0000 and 0xaffff)?
       */
      if (BANK_MEMORY_ADDRESS <= addr
       && addr < BANK_MEMORY_ADDRESS + BANK_MEMORY_SIZE
       && cpssp->mode == CIRRUS) {
            _cirrus_vga_page_read(cpssp,
                        addr - BANK_MEMORY_ADDRESS, to, len);
            return 0;
      }

      /*
       * Register memory-mapped i/o access?
       */
      if ((pci_getconfigb(cpssp->pci_config_space, 0x04) & PCI_COMMAND_MEMORY)
       && REGISTER_MEMORY_ADDRESS <= addr
       && addr < REGISTER_MEMORY_ADDRESS + REGISTER_MEMORY_SIZE) {
            if (addr < REGISTER_MEMORY_ADDRESS + 0x20 - len) {
                  /* The VGA registers are accessible
                     in the first 32 bytes. */

                  _cirrus_vga_mmio_vga_read(cpssp,
                              (unsigned short) (addr - REGISTER_MEMORY_ADDRESS),
                              to, len);
                  return 0;

            } else if (REGISTER_MEMORY_ADDRESS + 0x100 <= addr
                  && addr <= REGISTER_MEMORY_ADDRESS + 0x200 - len) {
                  /* The BitBLT control registers are not readable
                     at offset 0x100 on real hardware, but since
                     f.e. X is using this nonetheless... */

                  _cirrus_vga_mmio_bitblt_read(cpssp,
                              (unsigned short) (addr - REGISTER_MEMORY_ADDRESS),
                              to, len);
                  return 0;
            }
      }

      /*
       * Legacy memory-mapped i/o at 0xb8000?
       */
      if (LEGACY_MMIO_ADDRESS <= addr
       && addr < LEGACY_MMIO_ADDRESS + LEGACY_MMIO_SIZE) {
            if (((cpssp->ext_ext_sequencer_mode & 0xf0) == 0
              && (cpssp->ext_config_rb_ext_control & 0x04) == 0)
             || ((cpssp->ext_ext_sequencer_mode & 0xf0)
              && (cpssp->ext_config_rb_ext_control & 0x44) != 0x04)) {
#if (0 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                        "Memory-mapped i/o at 0xb8000 disabled\n");
#endif
                  return -1;
            }

#if (2 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "Legacy mm-i/o access at 0xb8000\n");
#endif
            addr &= 0xff;
            _cirrus_vga_mmio_bitblt_read(cpssp,
                        (unsigned short) addr + 0x100, to, len);
            return 0;
      }

      /*
       * VGA BIOS access over PCI30 configured window?
       * Will be used during POST to copy rom to 0xc000
       */
      if ((pci_getconfigb(cpssp->pci_config_space, 0x30) & 0x01)
       && BIOS_MEMORY_ADDRESS <= addr
       && addr < BIOS_MEMORY_ADDRESS + BIOS_MEMORY_SIZE) {
            /* VGA BIOS is mapped directly */
            assert(0);
      }

      /* Don't know what to do with addr. */
      return -1;
}

static void
_cirrus_vga_mmio_vga_write(
      struct cpssp *cpssp,
      unsigned short offset,
      const void *_from,
      unsigned long len
)
{
      const unsigned char *from;
      unsigned short count;

      assert(offset < 0x20);

      /* Real hardware only accepts byte or word writes,
         but we get and accept dwords, too. */

      from = (const unsigned char *) _from;
      count = 0;
      offset += CIRRUS_IOPORT;

      while (count < len) {
            /* See table 9-3 in trm */
            switch (offset + count) {
            case 0x3C0 ... 0x3CA:
            case 0x3CC:
            case 0x3CE ... 0x3CF:
            case 0x3D4 ... 0x3D5:
            case 0x3DA:
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         "Writing 0x%02hx to %3X\n",
                         *from, offset + count);
#endif
                  _cirrus_vga_outb(cpssp, *from, offset + count);
                  break;
            default:
#if (0 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Unknown register %3X\n",
                         offset + count);
#endif
                  break;
            };
            count++;
            from++;
      }
}

static void
_cirrus_vga_mmio_bitblt_write(
      struct cpssp *cpssp,
      unsigned short offset,
      const void *_from,
      unsigned long len
)
{
      const unsigned char *from;
      unsigned short count;

      assert(0x100 <= offset && offset < 0x200);

      /* Real hardware only accepts byte or word writes,
         but we get and accept dwords, too. */

      from = (const unsigned char *) _from;
      count = 0;

      while (count < len) {
            /*
             * See table 9-4 in trm.
             * If you change this also change _cirrus_vga_outb().
             */
#if (2 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "offset 0x%03x <- 0x%02hx\n",
                   offset + count, *from);
#endif
            switch (offset + count) {
            case 0x100: /* GR0 */
                  cpssp->shadow_gr0 = *from;
                  break;
            case 0x101: /* GR10 */
                  cpssp->blt_color_exp_fg_bg[0] = *from;
                  break;
            case 0x102: /* GR12 */
                  cpssp->blt_color_exp_fg_bg[2] = *from;
                  break;
            case 0x103: /* GR14 */
                  cpssp->blt_color_exp_fg_bg[4] = *from;
                  break;
            case 0x104: /* GR1 */
                  cpssp->shadow_gr1 = *from;
                  break;
            case 0x105: /* GR11 */
                  cpssp->blt_color_exp_fg_bg[1] = *from;
                  break;
            case 0x106: /* GR13 */
                  cpssp->blt_color_exp_fg_bg[3] = *from;
                  break;
            case 0x107: /* GR15 */
                  cpssp->blt_color_exp_fg_bg[5] = *from;
                  break;
            case 0x108: /* GR20 */
                  cpssp->blt_width[0] = *from;
                  break;
            case 0x109: /* GR21 */
                  cpssp->blt_width[1] = *from & 0x1f;
                  break;
            case 0x10A: /* GR22 */
                  cpssp->blt_height[0] = *from;
                  break;
            case 0x10B: /* GR23 */
                  cpssp->blt_height[1] = *from & 0x07;
                  break;
            case 0x10C: /* GR24 */
                  cpssp->blt_dest_pitch[0] = *from;
                  break;
            case 0x10D: /* GR25 */
                  cpssp->blt_dest_pitch[1] = *from & 0x1f;
                  break;
            case 0x10E: /* GR26 */
                  cpssp->blt_source_pitch[0] = *from;
                  break;
            case 0x10F: /* GR27 */
                  cpssp->blt_source_pitch[1] = *from & 0x1f;
                  break;
            case 0x110: /* GR28 */
                  cpssp->blt_dest_start[0] = *from;
                  break;
            case 0x111: /* GR29 */
                  cpssp->blt_dest_start[1] = *from;
                  break;
            case 0x112: /* GR2A */
                  cpssp->blt_dest_start[2] = *from & 0x3f;
                  if (cpssp->blt_start_status & BITBLT_AUTOSTART) {
#ifdef CIRRUS_DEBUG_BITBLT
                        faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                               "Wrote GR2A. Autostarting BitBLT...\n");
#endif
                        _cirrus_bitblt_start(cpssp);
                  }
                  break;
            case 0x114: /* GR2C */
                  cpssp->blt_source_start[0] = *from;
                  break;
            case 0x115: /* GR2D */
                  cpssp->blt_source_start[1] = *from;
                  break;
            case 0x116: /* GR2E */
                  cpssp->blt_source_start[2] = *from & 0x3f;
                  break;
            case 0x117: /* GR2F */
                  cpssp->blt_dest_left_side_clipping = *from;
                  break;
            case 0x118: /* GR30 */
                  cpssp->blt_mode = *from;
                  break;
            case 0x11A: /* GR32 */
                  cpssp->blt_rop = *from;
                  break;
            case 0x11B: /* GR33 */
                  if ((cpssp->ext_power_management & 0x20)
                   || (cpssp->blt_start_status & 0x80)) {
                        cpssp->blt_mode_extensions = *from;
                  } else {
#ifdef CIRRUS_DEBUG_BITBLT
                        faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                               "GR33 is write protected\n");
#endif
                  }
                  break;
            case 0x11C: /* GR34 */
                  cpssp->blt_transp_blt_key_color[0] = *from;
                  break;
            case 0x11D: /* GR35 */
                  cpssp->blt_transp_blt_key_color[1] = *from;
                  break;
            case 0x140: /* GR31 */
                  _cirrus_write_blt_start(cpssp, *from);
                  break;

            case 0x113:          /* GR2B reserved */
            case 0x119:          /* ? reserved */
            case 0x11E:          /* ? reserved */
            case 0x11F:          /* ? reserved */
            case 0x120 ... 0x13F: /* ? reserved */
            case 0x141 ... 0x1FF: /* ? reserved */
#ifdef CIRRUS_DEBUG_BITBLT
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Reserved register at offset 0x%03x\n",
                         offset + count);
#endif
                  break;

            default:
#ifdef CIRRUS_DEBUG_BITBLT
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Unknown register at offset 0x%03x\n",
                         offset + count);
#endif
                  break;
            };
            count++;
            from++;
      }
}

static void
_cirrus_vga_linear_fb_write(
      struct cpssp *cpssp,
      unsigned long offset,
      const void *_from,
      unsigned long len
)
{
      const uint8_t *from;
      int count;

#if (1 < CIRRUS_DEBUG)
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
             "Display memory write at offset 0x%08lx length %ld\n",
             offset, len);
#endif

      from = (const uint8_t *) _from;
      count = 0;

      if (unlikely(0 < cpssp->bitblt.src_counter
                  && !((cpssp->bitblt.mode & BITBLT_COLOR_EXPAND)
                       && (cpssp->blt_start_status
                             & BITBLT_SYSTEM_SOURCE_LOCATION)))) {
            /* system-to-screen bitblt (compatibility mode) */

#ifdef CIRRUS_DEBUG_BITBLT
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "system-to-screen bitblt length %ld\n",
                   len);
#endif

            /* NOTE: len _must_always_ be 4 here */
            while (count < len) {
                  video_writeb(cpssp, cpssp->bitblt.line_pointer++, *from);
                  count++;
                  from++;
            }
            if (cpssp->bitblt.line_pointer_end 
                        <= cpssp->bitblt.line_pointer)
                  _cirrus_bitblt_system_to_screen_next(cpssp);

      } else if (offset <= (4 * MEGABYTE) - len) {
            /* no swap */

            if (len == 4) {
                  video_writel(cpssp, offset, *((const uint32_t*)from));

            } else if (len == 1) {
                  video_writeb(cpssp, offset, *from);

            } else if (len == 2) {
                  if (unlikely(offset & 1)) {
                        video_writeb(cpssp, offset+0, *(from+0));
                        video_writeb(cpssp, offset+1, *(from+1));
                  } else {
                        video_writew(cpssp, offset, *((const uint16_t*)from));
                  }

            } else if (len == 3) {
                  if (offset & 1) {
                        video_writeb(cpssp, offset, *from);
                        video_writew(cpssp, offset+1, *((const uint16_t*)from+1));
                  } else {
                        video_writew(cpssp, offset, *((const uint16_t*)from));
                        video_writeb(cpssp, offset+2, *(from+2));
                  }

            } else {
                  /* should not happen, though */
                  while (count < len) {
                        video_writeb(cpssp, offset + count, *from);
                        count++;
                        from++;
                  }
            }

      /*
       * All word or dword swapping apertures are implemented with
       * a cache. This makes f.e. two contiguous word writes into
       * the dword-swap-aperture possible.
       */
      } else if (4 * MEGABYTE <= offset && offset <= (8 * MEGABYTE) - len) {
            /* word swap */

            offset -= 4 * MEGABYTE;

            while (count < len) {
                  switch (cpssp->byte_swap_cache_fill) {
                  case 0:
                        cpssp->byte_swap_cache[0] = *from;
                        cpssp->byte_swap_cache_fill++;
                        break;
                  case 1:
                        video_writeb(cpssp, offset + count - 1, *from);
                        video_writeb(cpssp, offset + count - 0, cpssp->byte_swap_cache[0]);
                        cpssp->byte_swap_cache_fill = 0;
                        break;
                  default:
                        faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                               "Byte-swap cache error\n");
                        cpssp->byte_swap_cache_fill = 0;
                  }
                  count++;
                  from++;
            }

      } else if (8 * MEGABYTE <= offset && offset <= (12 * MEGABYTE) - len) {
            /* dword swap */

            offset -= 8 * MEGABYTE;

            while (count < len) {
                  switch (cpssp->byte_swap_cache_fill) {
                  case 0:
                        cpssp->byte_swap_cache[0] = *from;
                        cpssp->byte_swap_cache_fill++;
                        break;
                  case 1:
                        cpssp->byte_swap_cache[1] = *from;
                        cpssp->byte_swap_cache_fill++;
                        break;
                  case 2:
                        cpssp->byte_swap_cache[2] = *from;
                        cpssp->byte_swap_cache_fill++;
                        break;
                  case 3:
                        video_writeb(cpssp, offset + count - 3, *from);
                        video_writeb(cpssp, offset + count - 2, cpssp->byte_swap_cache[2]);
                        video_writeb(cpssp, offset + count - 1, cpssp->byte_swap_cache[1]);
                        video_writeb(cpssp, offset + count - 0, cpssp->byte_swap_cache[0]);
                        cpssp->byte_swap_cache_fill = 0;
                        break;
                  default:
                        faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                               "Byte-swap cache error\n");
                        cpssp->byte_swap_cache_fill = 0;
                  }
                  count++;
                  from++;
            }

      } else if (12 * MEGABYTE <= offset && offset <= (16 * MEGABYTE) - len) {
#if (0 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                   "Video aperture write not implemented\n");
#endif

      } else if (16 * MEGABYTE <= offset && offset <= (20 * MEGABYTE) - len) {
            /* no swap system-to-screen bitblt
             * (revision B system source location) */

#ifdef CIRRUS_DEBUG_BITBLT
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "no swap system-to-screen bitblt length %ld\n",
                   len);
#endif

            while (count < len) {
                  video_writeb(cpssp, cpssp->bitblt.line_pointer++, *from);
                  count++;
                  from++;
            }
            if (cpssp->bitblt.line_pointer_end 
                        <= cpssp->bitblt.line_pointer)
            _cirrus_bitblt_system_to_screen_next(cpssp);

      } else if (20 * MEGABYTE <= offset && offset <= (24 * MEGABYTE) - len) {
            /* word swap system-to-screen bitblt
             * (revision B system source location) */
#ifdef CIRRUS_DEBUG_BITBLT
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "word swap system-to-screen bitblt length %ld\n",
                   len);
#endif

            while (count < len) {
                  switch (cpssp->byte_swap_cache_fill) {
                  case 0:
                        cpssp->byte_swap_cache[0] = *from;
                        cpssp->byte_swap_cache_fill++;
                        break;
                  case 1:
                        video_writeb(cpssp, cpssp->bitblt.line_pointer++, *from);
                        video_writeb(cpssp, cpssp->bitblt.line_pointer++,
                              cpssp->byte_swap_cache[0]);
                        cpssp->byte_swap_cache_fill = 0;
                        break;
                  default:
                        faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                               "Byte-swap cache error\n");
                        cpssp->byte_swap_cache_fill = 0;
                  }
                  count++;
                  from++;
            }
            if (cpssp->bitblt.line_pointer_end 
                        <= cpssp->bitblt.line_pointer)
                  _cirrus_bitblt_system_to_screen_next(cpssp);

      } else if (24 * MEGABYTE <= offset && offset <= (28 * MEGABYTE) - len) {
            /* dword swap system-to-screen bitblt
             * (revision B system source location) */
#ifdef CIRRUS_DEBUG_BITBLT
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "dword swap system-to-screen bitblt length %ld\n",
                   len);
#endif

            while (count < len) {
                  switch (cpssp->byte_swap_cache_fill) {
                  case 0:
                        cpssp->byte_swap_cache[0] = *from;
                        cpssp->byte_swap_cache_fill++;
                        break;
                  case 1:
                        cpssp->byte_swap_cache[1] = *from;
                        cpssp->byte_swap_cache_fill++;
                        break;
                  case 2:
                        cpssp->byte_swap_cache[2] = *from;
                        cpssp->byte_swap_cache_fill++;
                        break;
                  case 3:
                        video_writeb(cpssp, cpssp->bitblt.line_pointer++, *from);
                        video_writeb(cpssp, cpssp->bitblt.line_pointer++,
                              cpssp->byte_swap_cache[2]);
                        video_writeb(cpssp, cpssp->bitblt.line_pointer++,
                              cpssp->byte_swap_cache[1]);
                        video_writeb(cpssp, cpssp->bitblt.line_pointer++,
                              cpssp->byte_swap_cache[0]);
                        cpssp->byte_swap_cache_fill = 0;
                        break;

                  default:
                        faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                               "Byte-swap cache error\n");
                        cpssp->byte_swap_cache_fill = 0;
                  }
                  count++;
                  from++;
            }
            if (cpssp->bitblt.line_pointer_end 
                        <= cpssp->bitblt.line_pointer)
                  _cirrus_bitblt_system_to_screen_next(cpssp);

      } else if (28 * MEGABYTE <= offset && offset <= (32 * MEGABYTE) - len) {
#if (0 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "Memory write of unused 28-32MB aperture\n");
#endif

      } else {
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "Memory write outside 32MB window\n");
      }
}

static void
_cirrus_vga_by8_writeb(
      struct cpssp *cpssp,
      unsigned long offset,
      uint8_t val,
      int mode
)
{
      unsigned long to;
      int x;

#if (1 < CIRRUS_DEBUG)
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
             "0x%08lx <- 0x%02hx mode=%d\n",
             offset, val, mode);
#endif

      to = offset;

      for (x = 0; x < 8; x++) {
            if (val & 0x80) {
                  video_writeb(cpssp, to, cpssp->shadow_gr1);
            } else if (mode == 5) {
                  video_writeb(cpssp, to, cpssp->shadow_gr0);
            }

            val <<= 1;
            to++;
      }
}

static void
_cirrus_vga_by16_writeb(
      struct cpssp *cpssp,
      unsigned long offset,
      uint8_t val,
      int mode)
{
      unsigned long to;
      int x;

#if (1 < CIRRUS_DEBUG)
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
             "0x%08lx <- 0x%02hx mode=%d\n",
             offset, val, mode);
#endif

      to = offset;

      for (x = 0; x < 8; x++) {

            if (val & 0x80) {
                  video_writeb(cpssp, to+0, cpssp->shadow_gr1);
                  video_writeb(cpssp, to+1, cpssp->blt_color_exp_fg_bg[1]);
            } else if (mode == 5) {
                  video_writeb(cpssp, to+0, cpssp->shadow_gr0);
                  video_writeb(cpssp, to+1, cpssp->blt_color_exp_fg_bg[0]);
            }

            val <<= 1;
            to += 2;
      }
}

static void
_cirrus_vga_page_writeb(
      struct cpssp *cpssp,
      unsigned long offset,
      uint8_t value
)
{
      unsigned long address;
      unsigned char mode;

      address = _cirrus_vga_page_get_address(cpssp, offset);
      mode = cpssp->vga.gr_mode & 0x07; /* GR5[write mode] */

      if (mode < 4
       || 5 < mode
       || (! (cpssp->ext_graph_contr_mode_ext & 0x04))) {
            /* GRB[2] is 0 -> Extended write modes not enabled */
#if (2 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "offset 0x%08lx == address 0x%08lx <- value 0x%02hx\n",
                   offset, address, value);
#endif
            video_writeb(cpssp, address, value);

      } else {

            /* GR5[write mode] is 4 or 5 */
            if ((cpssp->ext_graph_contr_mode_ext & 0x14) != 0x14) {
                  _cirrus_vga_by8_writeb(cpssp, address, value, mode);

            } else {
                  _cirrus_vga_by16_writeb(cpssp, address, value, mode);
            }
      }
}

static void
_cirrus_vga_page_write(
      struct cpssp *cpssp,
      unsigned long offset,
      const void *_from,
      unsigned long len
)
{
      const uint8_t *from;
      int count;

      /*
       * CL-GD542X/3X compatibility:
       * Access framebuffer through one 64KB or
       * two 32KB windows in 0xa0000 - 0xaffff.
       */
      from = (const uint8_t *) _from;
      count = 0;

      while (count < len) {
            _cirrus_vga_page_writeb(cpssp, offset + count, *from);
            count++;
            from++;
      }
}

static long
cirrus_vga_write(
      void *_cpssp,
      unsigned long addr,
      const void *from,
      unsigned long len
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      /*
       * Legacy VGA access?
       */
      if (cpssp->mode == VGA) {
            if (vga_write(cpssp, addr, from, len) == 0) {
                  return 0;
            }
      }

      /*
       * Display memory access?
       */
      if ((pci_getconfigb(cpssp->pci_config_space, 0x04) & PCI_COMMAND_MEMORY)
       && DISPLAY_MEMORY_ADDRESS <= addr
       && addr < DISPLAY_MEMORY_ADDRESS + DISPLAY_MEMORY_SIZE
       && (cpssp->ext_ext_sequencer_mode & 0xf0) != 0) {
            if (unlikely(DISPLAY_MEMORY_ADDRESS + DISPLAY_MEMORY_SIZE - 256 <= addr
             && (cpssp->ext_config_rb_ext_control & 0x44) == 0x44)) {
                  addr &= 0xff;
                  _cirrus_vga_mmio_bitblt_write(cpssp,
                              (unsigned short) addr + 0x100,
                              from, len);
            } else {
                  _cirrus_vga_linear_fb_write(cpssp,
                              addr - DISPLAY_MEMORY_ADDRESS,
                              from, len);
            }
            return 0;
      }

      /*
       * Bank-based addressing (between 0xa0000 and 0xaffff)?
       */
      if (BANK_MEMORY_ADDRESS <= addr
       && addr < BANK_MEMORY_ADDRESS + BANK_MEMORY_SIZE
       && cpssp->mode == CIRRUS) {
            _cirrus_vga_page_write(cpssp,
                        addr - BANK_MEMORY_ADDRESS, from, len);
            return 0;
      }

      /*
       * Register memory-mapped i/o access?
       */
      if ((pci_getconfigb(cpssp->pci_config_space, 0x04) & PCI_COMMAND_MEMORY)
       && REGISTER_MEMORY_ADDRESS <= addr
       && addr < REGISTER_MEMORY_ADDRESS + REGISTER_MEMORY_SIZE) {
            if(addr < REGISTER_MEMORY_ADDRESS + 0x20 - len) {
                  /* The VGA registers are accessible
                     in the first 32 bytes. */

                  _cirrus_vga_mmio_vga_write(cpssp,
                              (unsigned short) (addr - REGISTER_MEMORY_ADDRESS),
                              from, len);
                  return 0;

            } else if (REGISTER_MEMORY_ADDRESS + 0x100 <= addr
                     && addr <= REGISTER_MEMORY_ADDRESS + 0x200 - len) {
                  /* The BitBLT control register are writeable at
                     offset 0x100 (table 9.4 trm) */

                  _cirrus_vga_mmio_bitblt_write(cpssp,
                              (unsigned short) (addr - REGISTER_MEMORY_ADDRESS),
                              from, len);
                  return 0;
            }
      }

      /*
       * Legacy memory-mapped i/o at 0xb8000?
       */
      if (LEGACY_MMIO_ADDRESS <= addr
       && addr < LEGACY_MMIO_ADDRESS + LEGACY_MMIO_SIZE) {
            if (((cpssp->ext_ext_sequencer_mode & 0xf0) == 0
              && (cpssp->ext_config_rb_ext_control & 0x04) == 0)
             || ((cpssp->ext_ext_sequencer_mode & 0xf0)
              && (cpssp->ext_config_rb_ext_control & 0x44) != 0x04)) {
#if (0 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                        "Memory-mapped i/o at 0xb8000 disabled\n");
#endif
                  return -1;
            }

#if (2 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "Legacy mm-i/o access at 0xb8000\n");
#endif
            addr &= 0xff;
            _cirrus_vga_mmio_bitblt_write(cpssp,
                        (unsigned short) addr + 0x100, from, len);
            return 0;
      }

      /*
       * VGA BIOS access over PCI30 configured window?
       * Will be used during POST to copy rom to 0xc000
       */
      if ((pci_getconfigb(cpssp->pci_config_space, 0x30) & 0x01)
       && BIOS_MEMORY_ADDRESS <= addr
       && addr < BIOS_MEMORY_ADDRESS + BIOS_MEMORY_SIZE) {
            /* VGA BIOS is mapped directly */
            assert(0);
      }

      /* Don't know what to do with addr */
      return -1;
}

static int
cirrus_vga_mr(void *_cpssp, uint32_t addr, unsigned int bs, uint32_t *valp)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;
      uint8_t val8;
      uint16_t val16;
      uint32_t val32;

      switch (bs) {
      case 0x1 << 0:
            if (cirrus_vga_read(cpssp, addr + 0, &val8, 1) == 0) {
                  *valp = val8 << 0;
                  return 0;
            }
            break;
      case 0x1 << 1:
            if (cirrus_vga_read(cpssp, addr + 1, &val8, 1) == 0) {
                  *valp = val8 << 8;
                  return 0;
            }
            break;
      case 0x1 << 2:
            if (cirrus_vga_read(cpssp, addr + 2, &val8, 1) == 0) {
                  *valp = val8 << 16;
                  return 0;
            }
            break;
      case 0x1 << 3:
            if (cirrus_vga_read(cpssp, addr + 3, &val8, 1) == 0) {
                  *valp = val8 << 24;
                  return 0;
            }
            break;
      case 0x3 << 0:
            if (cirrus_vga_read(cpssp, addr + 0, &val16, 2) == 0) {
                  *valp = val16 << 0;
                  return 0;
            }
            break;
      case 0x3 << 1:
            if (cirrus_vga_read(cpssp, addr + 1, &val16, 2) == 0) {
                  *valp = val16 << 8;
                  return 0;
            }
            break;
      case 0x3 << 2:
            if (cirrus_vga_read(cpssp, addr + 2, &val16, 2) == 0) {
                  *valp = val16 << 16;
                  return 0;
            }
            break;
      case 0x7 << 0:
            if (cirrus_vga_read(cpssp, addr + 0, &val32, 3) == 0) {
                  *valp = val32;
                  return 0;
            }
            break;
      case 0x7 << 1:
            if (cirrus_vga_read(cpssp, addr + 1, &val32, 3) == 0) {
                  *valp = val32 << 8;
                  return 0;
            }
            break;
      case 0xf << 0:
            if (cirrus_vga_read(cpssp, addr + 0, valp, 4) == 0) {
                  return 0;
            }
            break;
      }
      return -1;
}

static int
cirrus_vga_mw(void *_cpssp, uint32_t addr, unsigned int bs, uint32_t val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;
      uint8_t val8;
      uint16_t val16;

      switch (bs) {
      case 0x1 << 0:
            val8 = val >> 0;
            if (cirrus_vga_write(cpssp, addr + 0, &val8, 1) == 0) {
                  return 0;
            }
            break;
      case 0x1 << 1:
            val8 = val >> 8;
            if (cirrus_vga_write(cpssp, addr + 1, &val8, 1) == 0) {
                  return 0;
            }
            break;
      case 0x1 << 2:
            val8 = val >> 16;
            if (cirrus_vga_write(cpssp, addr + 2, &val8, 1) == 0) {
                  return 0;
            }
            break;
      case 0x1 << 3:
            val8 = val >> 24;
            if (cirrus_vga_write(cpssp, addr + 3, &val8, 1) == 0) {
                  return 0;
            }
            break;
      case 0x3 << 0:
            val16 = val >> 0;
            if (cirrus_vga_write(cpssp, addr + 0, &val16, 2) == 0) {
                  return 0;
            }
            break;
      case 0x3 << 1:
            val16 = val >> 8;
            if (cirrus_vga_write(cpssp, addr + 1, &val16, 2) == 0) {
                  return 0;
            }
            break;
      case 0x3 << 2:
            val16 = val >> 16;
            if (cirrus_vga_write(cpssp, addr + 2, &val16, 2) == 0) {
                  return 0;
            }
            break;
      case 0x7 << 0:
            fixme();
            break;
      case 0x7 << 1:
            fixme();
            break;
      case 0xf << 0:
            if (cirrus_vga_write(cpssp, addr + 0, &val, 4) == 0) {
                  return 0;
            }
            break;
      }
      return -1;
}

static int
cirrus_vga_map(
      void *_cpssp,
      unsigned long addr,
      unsigned int len,
      char **haddr_mr_p,
      char **haddr_mw_p
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      addr &= ~0xfff;

      /*
       * Legacy VGA access?
       */
      if (cpssp->mode == VGA) {
            if (vga_map(cpssp, addr, len, haddr_mr_p, haddr_mw_p) == 0) {
                  return 0;
            }
      }

      /*
       * Display memory access?
       */
      if ((pci_getconfigb(cpssp->pci_config_space, 0x04) & PCI_COMMAND_MEMORY)
       && DISPLAY_MEMORY_ADDRESS <= addr
       && addr < DISPLAY_MEMORY_ADDRESS + DISPLAY_MEMORY_SIZE) {
#if (1 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                        "Not mapping any apertures directly.\n");
#endif
            *haddr_mr_p = NULL;
            *haddr_mw_p = NULL;
            return 0;
      }

      /*
       * Bank-based addressing (between 0xa0000 and 0xaffff)?
       */
      if (BANK_MEMORY_ADDRESS <= addr
       && addr < BANK_MEMORY_ADDRESS + BANK_MEMORY_SIZE) {
            if (cpssp->mode == CIRRUS) {
#if (2 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         "Not mapping bank-based memory directly.\n");
#endif
                  *haddr_mr_p = NULL;
                  *haddr_mw_p = NULL;

                  return 0;
            }
      }

      /*
       * Register memory-mapped i/o access?
       */
      if ((pci_getconfigb(cpssp->pci_config_space, 0x04) & PCI_COMMAND_MEMORY)
       && REGISTER_MEMORY_ADDRESS <= addr
       && addr < REGISTER_MEMORY_ADDRESS + REGISTER_MEMORY_SIZE) {
#if (2 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "Not mapping mm-i/o directly.\n");
#endif
            *haddr_mr_p = NULL;
            *haddr_mw_p = NULL;

            return 0;
      }

      /*
       * Legacy memory-mapped i/o at 0xb8000?
       */
      if (LEGACY_MMIO_ADDRESS <= addr
       && addr < LEGACY_MMIO_ADDRESS + LEGACY_MMIO_SIZE) {
#if (2 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "Not mapping legacy mm-i/o directly.\n");
#endif
            *haddr_mr_p = NULL;
            *haddr_mw_p = NULL;

            return 0;
      }

      /*
       * VGA BIOS access over PCI30 configured window?
       * Used during POST to copy rom to 0xc000
       */
      if ((pci_getconfigb(cpssp->pci_config_space, 0x30) & 0x01)
       && BIOS_MEMORY_ADDRESS <= addr
       && addr < BIOS_MEMORY_ADDRESS + BIOS_MEMORY_SIZE) {
#if (1 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "Mapping VGA BIOS at PCI30.\n");
#endif
            /* Map the BIOS directly */
            return sig_cs_map(cpssp->port_rom_cs, cpssp,
                        addr - BIOS_MEMORY_ADDRESS,
                        len, haddr_mr_p, haddr_mw_p);
      }

      /* Don't know what to do with addr */
      return 1;
}

/*************************************************************************/
/*                                                                       */
/* PCI configuration space read and write functions                      */
/*                                                                       */
/*************************************************************************/

static void
_cirrus_vga_cwriteb(
      struct cpssp *cpssp,
      unsigned char val,
      unsigned char addr
)
{
      unsigned char cache_byte;
      unsigned long old_memory_address;
      unsigned long new_memory_address;

      switch (addr) {
      case 0x00 ... 0x01:     /* Vendor Id */
      case 0x02 ... 0x03:     /* Device Id */
            /* readonly */
            break;
      case 0x04:              /* Command register low byte */
            pci_setconfigb(cpssp->pci_config_space, addr,
                        val & (PCI_COMMAND_IO /* bit 0 */
                              | PCI_COMMAND_MEMORY /* bit 1 */
                              | PCI_COMMAND_VGA_PALETTE)); /* bit 5 */
            /* all others must be zero */
            break;
      case 0x05:              /* Command register high byte */
      case 0x06:              /* Status register low byte */
            /* must be zero */
            break;
      case 0x07:              /* Status register high byte */
            cache_byte = pci_getconfigb(cpssp->pci_config_space, addr);
            pci_setconfigb(cpssp->pci_config_space, addr,
                        (val & 0xf8) | (cache_byte & 0x07));
            /* bit 0 reserved zero,
               PCI Devsel Timing (bits 1 and 2) read only */
            break;
      case 0x08:              /* Revision Id */
      case 0x09 ... 0x0b:     /* Class Code */
            /* readonly */
            break;
      case 0x0e:              /* PCI Header Type */
            pci_setconfigb(cpssp->pci_config_space, addr, val);
            break;
      case 0x10 ... 0x12:     /* Mem Base Addr Reserved and Readonly */
            /* readonly */
            break;
      case 0x13:              /* Display Mem Base Addr */
            old_memory_address = DISPLAY_MEMORY_ADDRESS;
            /* bit 1-7 claim 32mb addr range, bit 0 reserved zero */
            pci_setconfigb(cpssp->pci_config_space, addr, val & 0xfe);
            if (DISPLAY_MEMORY_ADDRESS != old_memory_address) {
                  sig_pci_bus_unmap(cpssp->port_pci_bus,
                                cpssp,
                                old_memory_address,
                                DISPLAY_MEMORY_SIZE);
                  sig_pci_bus_unmap(cpssp->port_pci_bus,
                                cpssp,
                                DISPLAY_MEMORY_ADDRESS,
                                DISPLAY_MEMORY_SIZE);
            }
            break;
      case 0x14:              /* VGA and BitBLT I/O Base Addr byte 0 */
            /* bits 1-7 reserved zero, readonly 0 in bit 0 indicates that
               memory space is requested */
            break;
      case 0x15:              /* VGA and BitBLT I/O Base Addr byte 1 */
            /* bit 8-11 (0-3) reserved zero */
            pci_setconfigb(cpssp->pci_config_space, addr, val & 0xf0);
            break;
      case 0x16:              /* VGA and BitBLT I/O Base Addr byte 2 */
            pci_setconfigb(cpssp->pci_config_space, addr, val);
            break;
      case 0x17:              /* VGA and BitBLT I/O Base Addr byte 3 */
            old_memory_address = REGISTER_MEMORY_ADDRESS;
            pci_setconfigb(cpssp->pci_config_space, addr, val);
            if (REGISTER_MEMORY_ADDRESS != old_memory_address) {
                  sig_pci_bus_unmap(cpssp->port_pci_bus,
                                cpssp,
                                old_memory_address,
                                REGISTER_MEMORY_SIZE);
                  sig_pci_bus_unmap(cpssp->port_pci_bus,
                                cpssp,
                                REGISTER_MEMORY_ADDRESS,
                                REGISTER_MEMORY_SIZE);
            }
            break;
      case 0x18 ... 0x1b:     /* GP I/O Base Addr */
            /* General purpose i/o is disabled (as if CF4 and CF8 were 1,
               see appendix B11 in trm).
               Bit 0 readonly one, bits 1-4 reserved zero,
               bits 5-31 readonly all zeros. */
            break;
      case 0x2c ... 0x2d:     /* Subsystem Vendor Id */
      case 0x2e ... 0x2f:     /* Subsystem Id */
            /* readonly */
            break;
      case 0x30:              /* Bit 0 is EROM enable, others reserved 0 */
            val &= 0x01;
            goto rom_address;
      case 0x31:              /* BIOS_MEMORY_SIZE currently 64kb */
            val &= 0x00;
      rom_address:;
      case 0x32 ... 0x33:     /* BIOS base address */
            if (pci_getconfigl(cpssp->pci_config_space, 0x30) & 1) {
                  old_memory_address = pci_getconfigl(cpssp->pci_config_space, 0x30) & ~1;
                  sig_pci_bus_unmap(cpssp->port_pci_bus, cpssp,
                              old_memory_address, BIOS_MEMORY_SIZE);
            }
            pci_setconfigb(cpssp->pci_config_space, addr, val);
            if (pci_getconfigl(cpssp->pci_config_space, 0x30) & 1) {
                  new_memory_address = pci_getconfigl(cpssp->pci_config_space, 0x30) & ~1;
                  sig_pci_bus_unmap(cpssp->port_pci_bus, cpssp,
                              new_memory_address, BIOS_MEMORY_SIZE);
            }
            break;
      case 0x3c:              /* Interrupt Line */
            pci_setconfigb(cpssp->pci_config_space, addr, val);
            break;
      case 0x3d:              /* PCI Interrupt Pin */
            /* readonly */
            break;
      default:
#ifdef CIRRUS_DEBUG_CONFIGSPACE
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "Write attempt of "
                   "0x%02hx to unknown PCI register 0x%02hx.\n",
                   val, addr);
#endif
            break;
      }
}

static int
cirrus_vga_c0r(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t *valp
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      assert(! (addr & 3));

      if ((addr >> 8) & 7) {
            /* Only function 0 supported. */
            return 1;
      }
      addr &= 0xff;

      *valp = 0x00000000;
      if ((bs >> 0) & 1) {
            *valp |= pci_getconfigb(cpssp->pci_config_space, addr + 0) << 0;
      }
      if ((bs >> 1) & 1) {
            *valp |= pci_getconfigb(cpssp->pci_config_space, addr + 1) << 8;
      }
      if ((bs >> 2) & 1) {
            *valp |= pci_getconfigb(cpssp->pci_config_space, addr + 2) << 16;
      }
      if ((bs >> 3) & 1) {
            *valp |= pci_getconfigb(cpssp->pci_config_space, addr + 3) << 24;
      }

      return 0;
}

static int
cirrus_vga_c0w(
      void *_cpssp,
      uint32_t addr,
      unsigned int bs,
      uint32_t val
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      assert(! (addr & 3));

      if ((addr >> 8) & 7) {
            /* Only function 0 supported. */
            return 1;
      }
      addr &= 0xff;

      if ((bs >> 0) & 1) {
            _cirrus_vga_cwriteb(cpssp, (val >> 0) & 0xff, addr + 0);
      }
      if ((bs >> 1) & 1) {
            _cirrus_vga_cwriteb(cpssp, (val >> 8) & 0xff, addr + 1);
      }
      if ((bs >> 2) & 1) {
            _cirrus_vga_cwriteb(cpssp, (val >> 16) & 0xff, addr + 2);
      }
      if ((bs >> 3) & 1) {
            _cirrus_vga_cwriteb(cpssp, (val >> 24) & 0xff, addr + 3);
      }

      return 0;
}

/*************************************************************************/
/*                                                                       */
/* The BitBLT engine                                                     */
/*                                                                       */
/*************************************************************************/

/* Needed for the transparent simple forward or backwards rop */
#define CIRRUS_IS_8BPP (  ( ((cpssp->ext_ext_sequencer_mode & 0x0e) >> 1) \
                      == 0) \
                   || ( ((cpssp->ext_ext_sequencer_mode & 0x0e) >> 1) \
                      == 1))
#define CIRRUS_IS_16BPP (((cpssp->ext_ext_sequencer_mode & 0x0e) >> 1) == 3)

static void
_cirrus_bitblt_reset(struct cpssp *cpssp);

/* generate all blitter functions, see trm table 9.11 */

/*
 * s=1 s=1 s=0 s=0
 * d=1 d=0 d=1 d=0
 *
 *  0   0   0   0
 */
#define ROP_NAME zero
#define ROP_CODE(d, s) d = 0
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  0   0   0   1  */
#define ROP_NAME notsrc_and_notdst
#define ROP_CODE(d, s) d = (~(s)) & (~(d))
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  0   0   1   0  */
#define ROP_NAME notsrc_and_dst
#define ROP_CODE(d, s) d = (~(s)) & (d)
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  0   0   1   1  */
#define ROP_NAME notsrc
#define ROP_CODE(d, s) d = (~(s))
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  0   1   0   0  */
#define ROP_NAME src_and_notdst
#define ROP_CODE(d, s) d = (s) & (~(d))
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  0   1   0   1  */
#define ROP_NAME notdst
#define ROP_CODE(d, s) d = ~(d)
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  0   1   1   0  */
#define ROP_NAME src_xor_dst
#define ROP_CODE(d, s) d = (s) ^ (d)
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  0   1   1   1  */
#define ROP_NAME notsrc_or_notdst
#define ROP_CODE(d, s) d = (~(s)) | (~(d))
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  1   0   0   0  */
#define ROP_NAME src_and_dst
#define ROP_CODE(d, s) d = (s) & (d)
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  1   0   0   1  */
#define ROP_NAME src_notxor_dst
#define ROP_CODE(d, s) d = ~((s) ^ (d))
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  1   0   1   0
 * dst would have no effect -> this is the nop
 */

/*  1   0   1   1  */
#define ROP_NAME notsrc_or_dst
#define ROP_CODE(d, s) d = (~(s)) | (d)
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  1   1   0   0  */
#define ROP_NAME src
#define ROP_CODE(d, s) d = s
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  1   1   0   1  */
#define ROP_NAME src_or_notdst
#define ROP_CODE(d, s) d = (s) | (~(d))
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  1   1   1   0  */
#define ROP_NAME src_or_dst
#define ROP_CODE(d, s) d = (s) | (d)
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

/*  1   1   1   1  */
#define ROP_NAME one
#define ROP_CODE(d, s) d = ~0
#include "chip_cirrus_gd5446_rop.c"
#undef ROP_NAME
#undef ROP_CODE

static void
_cirrus_bitblt_nop(struct cpssp *cpssp)
{
}

typedef void (*_cirrus_bitblt_function_type)(struct cpssp *cpssp);

static const _cirrus_bitblt_function_type
_cirrus_bitblt_forward[16] = {
      _cirrus_bitblt_forward_zero,
      _cirrus_bitblt_forward_notsrc_and_notdst,
      _cirrus_bitblt_forward_notsrc_and_dst,
      _cirrus_bitblt_forward_notsrc,
      _cirrus_bitblt_forward_src_and_notdst,
      _cirrus_bitblt_forward_notdst,
      _cirrus_bitblt_forward_src_xor_dst,
      _cirrus_bitblt_forward_notsrc_or_notdst,
      _cirrus_bitblt_forward_src_and_dst,
      _cirrus_bitblt_forward_src_notxor_dst,
      _cirrus_bitblt_nop,
      _cirrus_bitblt_forward_notsrc_or_dst,
      _cirrus_bitblt_forward_src,
      _cirrus_bitblt_forward_src_or_notdst,
      _cirrus_bitblt_forward_src_or_dst,
      _cirrus_bitblt_forward_one
};

static const _cirrus_bitblt_function_type
_cirrus_bitblt_backward[16] = {
      _cirrus_bitblt_backward_zero,
      _cirrus_bitblt_backward_notsrc_and_notdst,
      _cirrus_bitblt_backward_notsrc_and_dst,
      _cirrus_bitblt_backward_notsrc,
      _cirrus_bitblt_backward_src_and_notdst,
      _cirrus_bitblt_backward_notdst,
      _cirrus_bitblt_backward_src_xor_dst,
      _cirrus_bitblt_backward_notsrc_or_notdst,
      _cirrus_bitblt_backward_src_and_dst,
      _cirrus_bitblt_backward_src_notxor_dst,
      _cirrus_bitblt_nop,
      _cirrus_bitblt_backward_notsrc_or_dst,
      _cirrus_bitblt_backward_src,
      _cirrus_bitblt_backward_src_or_notdst,
      _cirrus_bitblt_backward_src_or_dst,
      _cirrus_bitblt_backward_one
};

static const _cirrus_bitblt_function_type
_cirrus_bitblt_transparent_forward[16] = {
      _cirrus_bitblt_transparent_forward_zero,
      _cirrus_bitblt_transparent_forward_notsrc_and_notdst,
      _cirrus_bitblt_transparent_forward_notsrc_and_dst,
      _cirrus_bitblt_transparent_forward_notsrc,
      _cirrus_bitblt_transparent_forward_src_and_notdst,
      _cirrus_bitblt_transparent_forward_notdst,
      _cirrus_bitblt_transparent_forward_src_xor_dst,
      _cirrus_bitblt_transparent_forward_notsrc_or_notdst,
      _cirrus_bitblt_transparent_forward_src_and_dst,
      _cirrus_bitblt_transparent_forward_src_notxor_dst,
      _cirrus_bitblt_nop,
      _cirrus_bitblt_transparent_forward_notsrc_or_dst,
      _cirrus_bitblt_transparent_forward_src,
      _cirrus_bitblt_transparent_forward_src_or_notdst,
      _cirrus_bitblt_transparent_forward_src_or_dst,
      _cirrus_bitblt_transparent_forward_one
};

static const _cirrus_bitblt_function_type
_cirrus_bitblt_transparent_backward[16] = {
      _cirrus_bitblt_transparent_backward_zero,
      _cirrus_bitblt_transparent_backward_notsrc_and_notdst,
      _cirrus_bitblt_transparent_backward_notsrc_and_dst,
      _cirrus_bitblt_transparent_backward_notsrc,
      _cirrus_bitblt_transparent_backward_src_and_notdst,
      _cirrus_bitblt_transparent_backward_notdst,
      _cirrus_bitblt_transparent_backward_src_xor_dst,
      _cirrus_bitblt_transparent_backward_notsrc_or_notdst,
      _cirrus_bitblt_transparent_backward_src_and_dst,
      _cirrus_bitblt_transparent_backward_src_notxor_dst,
      _cirrus_bitblt_nop,
      _cirrus_bitblt_transparent_backward_notsrc_or_dst,
      _cirrus_bitblt_transparent_backward_src,
      _cirrus_bitblt_transparent_backward_src_or_notdst,
      _cirrus_bitblt_transparent_backward_src_or_dst,
      _cirrus_bitblt_transparent_backward_one
};

#define ROP_DEPTHIFY(function) { function ## _8, \
                         function ## _16,\
                         function ## _24,\
                         function ## _32 }

static const _cirrus_bitblt_function_type
_cirrus_bitblt_color_fill[16][4] = {
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_zero),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_notsrc_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_notsrc_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_notsrc),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_src_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_src_xor_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_notsrc_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_src_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_src_notxor_dst),
      {_cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop },
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_notsrc_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_src),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_src_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_src_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_fill_one)
};

static const  _cirrus_bitblt_function_type
_cirrus_bitblt_color_expansion[16][4] = {
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_zero),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_notsrc_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_notsrc_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_notsrc),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_src_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_src_xor_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_notsrc_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_src_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_src_notxor_dst),
      {_cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop },
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_notsrc_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_src),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_src_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_src_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_expansion_one)
};

static const  _cirrus_bitblt_function_type
_cirrus_bitblt_transparent_color_expansion[16][4] = {
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_zero),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_notsrc_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_notsrc_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_notsrc),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_src_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_src_xor_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_notsrc_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_src_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_src_notxor_dst),
      {_cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop },
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_notsrc_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_src),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_src_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_src_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_expansion_one)
};

static const  _cirrus_bitblt_function_type
_cirrus_bitblt_color_pattern_expansion[16][4] = {
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_zero),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_notsrc_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_notsrc_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_notsrc),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_src_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_src_xor_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_notsrc_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_src_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_src_notxor_dst),
      {_cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop },
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_notsrc_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_src),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_src_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_src_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_color_pattern_expansion_one)
};

static const  _cirrus_bitblt_function_type
_cirrus_bitblt_transparent_color_pattern_expansion[16][4] = {
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_zero),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_notsrc_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_notsrc_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_notsrc),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_src_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_src_xor_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_notsrc_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_src_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_src_notxor_dst),
      {_cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop },
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_notsrc_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_src),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_src_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_src_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_transparent_color_pattern_expansion_one)
};

static const  _cirrus_bitblt_function_type
_cirrus_bitblt_pattern_fill[16][4] = {
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_zero),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_notsrc_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_notsrc_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_notsrc),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_src_and_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_src_xor_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_notsrc_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_src_and_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_src_notxor_dst),
      {_cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop,
       _cirrus_bitblt_nop },
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_notsrc_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_src),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_src_or_notdst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_src_or_dst),
      ROP_DEPTHIFY(_cirrus_bitblt_pattern_fill_one)
};

#undef ROP_DEPTHIFY

/*
 * All raster operations assume that source_pointer and destination_pointer
 * point to completly mapped memory areas.
 */

static void
_cirrus_bitblt_reset(struct cpssp *cpssp)
{
#ifdef CIRRUS_DEBUG_BITBLT
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "\n");
#endif
      cpssp->blt_start_status &=
            ~(BITBLT_BUFFERED_REGISTER_STATUS | BITBLT_RESET
              | BITBLT_START | BITBLT_STATUS);

      cpssp->bitblt.line_pointer = BITBLT_LINE_BUFFER_START;
      cpssp->bitblt.line_pointer_end = BITBLT_LINE_BUFFER_START;
      cpssp->bitblt.src_counter = 0;
}

static void
_cirrus_bitblt_screen_to_screen(struct cpssp *cpssp)
{
#ifdef CIRRUS_DEBUG_BITBLT
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "\n");
#endif
      (*cpssp->bitblt.function)(cpssp);
      _cirrus_bitblt_reset(cpssp);
}

static void
_cirrus_bitblt_system_to_screen(struct cpssp *cpssp)
{
#ifdef CIRRUS_DEBUG_BITBLT
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "\n");
#endif
      /* source pitch is a don't care, calculate it to length
         of one src line in bytes */
      if (cpssp->bitblt.mode & BITBLT_COLOR_EXPAND) {

            int bpl; /* source bits needed per line */

            bpl = cpssp->bitblt.width /
                  cpssp->bitblt.color_expand_width;

            if (cpssp->bitblt.mode_extensions & BITBLT_32BIT_GRANULARITY) {

                  /* position of the first byte within the first dword
                     of each destination scanline */
                  bpl += ((cpssp->bitblt.dest_left_side_clipping
                         & BITBLT_SYSTEM_TO_SCREEN_DWORD_POINTER) >> 5)
                        * 8;

                  /* how many bytes needed per scanline? */
                  cpssp->bitblt.src_pitch = ((bpl + 31) >> 5) * 4;

            } else {
                  /* 8bit granularity */
                  cpssp->bitblt.src_pitch = (bpl + 7) >> 3;
            }
      } else {
            /* always align to 32bits */
            cpssp->bitblt.src_pitch =
                  (cpssp->bitblt.width + 3) & ~3;
      }

      if (BITBLT_LINE_BUFFER_SIZE < cpssp->bitblt.src_pitch) {
            /* this should never happen */
#ifdef CIRRUS_DEBUG_BITBLT
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "System-to-screen line buffer too small, required "
                   "are %d bytes\n", cpssp->bitblt.src_pitch);
#endif
            _cirrus_bitblt_reset(cpssp);
            return;
      }

      /* set up bitblt engine for single line execution */
      cpssp->bitblt.src_counter = /* total bytes for rop */
            cpssp->bitblt.src_pitch * cpssp->bitblt.height;

      cpssp->bitblt.line_pointer = BITBLT_LINE_BUFFER_START;
      cpssp->bitblt.line_pointer_end =
            BITBLT_LINE_BUFFER_START + cpssp->bitblt.src_pitch;

      cpssp->bitblt.height = 1;
      cpssp->bitblt.source_pointer = BITBLT_LINE_BUFFER_START;
}

static void
_cirrus_bitblt_system_to_screen_next(struct cpssp *cpssp)
{
#ifdef CIRRUS_DEBUG_BITBLT
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "\n");
#endif

      if (cpssp->bitblt.src_counter <= 0) {
            /* no sys2scr bitblt set up? */
#ifdef CIRRUS_DEBUG_BITBLT
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "No system-to-screen bitblt in progress? Resetting.\n");
#endif
            _cirrus_bitblt_reset(cpssp);
            return;
      }

      /* rop the line */
      (*cpssp->bitblt.function)(cpssp);

      /* update state of bitblt engine */
      cpssp->bitblt.destination_pointer += cpssp->bitblt.dest_pitch;
      cpssp->bitblt.src_counter -= cpssp->bitblt.src_pitch;

      if (cpssp->bitblt.src_counter <= 0) {
            /* finished */
            _cirrus_bitblt_reset(cpssp);
            return;
      }

      if (!(cpssp->bitblt.mode & BITBLT_COLOR_EXPAND)
          || (cpssp->bitblt.mode_extensions & BITBLT_32BIT_GRANULARITY)) {
            /* dword granularity:
             * drop remaining bytes of line buffer */
            cpssp->bitblt.line_pointer = BITBLT_LINE_BUFFER_START;

      } else {
            /* byte granularity:
             * copy remaining bytes of line buffer to front */
            memmove(cpssp->bitblt.line_buffer,
                        &cpssp->bitblt.line_buffer[
                                BITBLT_LINE_BUFFER_START
                              - cpssp->bitblt.line_pointer_end],
                        cpssp->bitblt.line_pointer
                        - cpssp->bitblt.line_pointer_end);

            cpssp->bitblt.line_pointer = BITBLT_LINE_BUFFER_START;
      }
}

static void
_cirrus_bitblt_do_solid_color_fill(struct cpssp *cpssp)
{
#ifdef CIRRUS_DEBUG_BITBLT
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "\n");
#endif
      cpssp->bitblt.function =
            _cirrus_bitblt_color_fill[cpssp->bitblt.rop]
            [cpssp->bitblt.color_expand_width - 1];
      _cirrus_bitblt_screen_to_screen(cpssp);
}

static void
_cirrus_bitblt_do_color_expansion(struct cpssp *cpssp)
{
#ifdef CIRRUS_DEBUG_BITBLT
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "\n");
#endif
      if (cpssp->bitblt.mode & BITBLT_TRANSPARENCY) {
            cpssp->bitblt.function =
                  _cirrus_bitblt_transparent_color_expansion
                  [cpssp->bitblt.rop]
                  [cpssp->bitblt.color_expand_width - 1];
      } else {
            cpssp->bitblt.function =
                  _cirrus_bitblt_color_expansion
                  [cpssp->bitblt.rop]
                  [cpssp->bitblt.color_expand_width - 1];
      }

      if (cpssp->bitblt.mode & BITBLT_SOURCE_SYSTEM_MEMORY) {
            _cirrus_bitblt_system_to_screen(cpssp);
      } else {
            _cirrus_bitblt_screen_to_screen(cpssp);
      }
}

static void
_cirrus_bitblt_do_pattern_fill(struct cpssp *cpssp)
{
#ifdef CIRRUS_DEBUG_BITBLT
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "\n");
#endif
      if (cpssp->bitblt.mode & BITBLT_COLOR_EXPAND) {
            if (cpssp->bitblt.mode & BITBLT_TRANSPARENCY) {
                  cpssp->bitblt.function =
                        _cirrus_bitblt_transparent_color_pattern_expansion
                        [cpssp->bitblt.rop]
                        [cpssp->bitblt.color_expand_width - 1];
            } else {
                  cpssp->bitblt.function =
                        _cirrus_bitblt_color_pattern_expansion
                        [cpssp->bitblt.rop]
                        [cpssp->bitblt.color_expand_width - 1];
            }
      } else {
            cpssp->bitblt.function =
                  _cirrus_bitblt_pattern_fill
                  [cpssp->bitblt.rop]
                  [cpssp->bitblt.color_expand_width - 1];
      }

      if (cpssp->bitblt.mode & BITBLT_SOURCE_SYSTEM_MEMORY) {
            /* system_to_screen is not allowed */
#ifdef CIRRUS_DEBUG_BITBLT
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "System-to-screen pattern fills are not allowed!\n");
#endif
            _cirrus_bitblt_reset(cpssp);
      } else {
            _cirrus_bitblt_screen_to_screen(cpssp);
      }
}

static void
_cirrus_bitblt_do_simple_rop(struct cpssp *cpssp)
{
#ifdef CIRRUS_DEBUG_BITBLT
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "\n");
#endif
      if ((cpssp->bitblt.mode & BITBLT_TRANSPARENCY)
       && (CIRRUS_IS_8BPP || CIRRUS_IS_16BPP)) {
            if (cpssp->bitblt.mode & BITBLT_REVERSE) {
                  cpssp->bitblt.dest_pitch = -cpssp->bitblt.dest_pitch;
                  cpssp->bitblt.src_pitch = -cpssp->bitblt.src_pitch;
                  cpssp->bitblt.function =
                        _cirrus_bitblt_transparent_backward[cpssp->bitblt.rop];
            } else {
                  cpssp->bitblt.function =
                        _cirrus_bitblt_transparent_forward[cpssp->bitblt.rop];
            }
      } else {
            if (cpssp->bitblt.mode & BITBLT_REVERSE) {
                  cpssp->bitblt.dest_pitch = -cpssp->bitblt.dest_pitch;
                  cpssp->bitblt.src_pitch = -cpssp->bitblt.src_pitch;
                  cpssp->bitblt.function =
                        _cirrus_bitblt_backward[cpssp->bitblt.rop];
            } else {
                  cpssp->bitblt.function =
                        _cirrus_bitblt_forward[cpssp->bitblt.rop];
            }
      }

      if (cpssp->bitblt.mode & BITBLT_SOURCE_SYSTEM_MEMORY) {
            _cirrus_bitblt_system_to_screen(cpssp);
      } else {
            _cirrus_bitblt_screen_to_screen(cpssp);
      }
}

static void
_cirrus_bitblt_start(struct cpssp *cpssp)
{
      unsigned long dest_offset, src_offset;

      /* bitblitter busy now... */
      cpssp->blt_start_status |= BITBLT_STATUS;

      /*
       * first initialize struct bitblt
       */

      /* trm 5.2 */
      cpssp->bitblt.width =
            (cpssp->blt_width[0]
             | ((cpssp->blt_width[1] & 0x1f) << 8)) + 1;

      /* trm 5.3 */
      cpssp->bitblt.height =
            (cpssp->blt_height[0]
             | ((cpssp->blt_height[1] & 0x7) << 8)) + 1;

      /* trm 5.4 */
      cpssp->bitblt.dest_pitch = cpssp->blt_dest_pitch[0]
            | ((cpssp->blt_dest_pitch[1] & 0x1f) << 8);

      /* trm 5.5 */
      cpssp->bitblt.src_pitch = cpssp->blt_source_pitch[0]
            | ((cpssp->blt_source_pitch[1] & 0x1f) << 8);

      /* trm 5.6 */
      dest_offset = cpssp->blt_dest_start[0]
            | (cpssp->blt_dest_start[1] << 8)
            | ((cpssp->blt_dest_start[2] & 0x3f) << 16);
      cpssp->bitblt.destination_pointer = dest_offset;

      /* trm 5.7 */
      src_offset = cpssp->blt_source_start[0]
            | (cpssp->blt_source_start[1] << 8)
            | ((cpssp->blt_source_start[2] & 0x3f) << 16);
      if (cpssp->blt_mode & BITBLT_8X8_PATTERN_COPY) {
            /* see trm 9.4.8 */
            src_offset &= ~0x07UL;
      }
      cpssp->bitblt.source_pointer = src_offset;

      /* trm 5.8 */
      cpssp->bitblt.dest_left_side_clipping =
            cpssp->blt_dest_left_side_clipping;

      /* trm 5.9 */
      cpssp->bitblt.mode = cpssp->blt_mode;
      cpssp->bitblt.color_expand_width =
            ((cpssp->blt_mode & BITBLT_COLOR_EXPAND_WIDTH) >> 4) + 1;

      /* trm 5.12 */
      cpssp->bitblt.mode_extensions = cpssp->blt_mode_extensions;

      /* trm 5.13 */
      cpssp->bitblt.key_color[0] =
            cpssp->blt_transp_blt_key_color[0];
      cpssp->bitblt.key_color[1] =
            cpssp->blt_transp_blt_key_color[1];

      /* trm 5.11 */
      switch (cpssp->blt_rop) {
      case 0x00:
            cpssp->bitblt.rop = 0;  /* zero */
            break;
      case 0x90:
            cpssp->bitblt.rop = 1;  /* notsrc_and_notdst */
            break;
      case 0x50:
            cpssp->bitblt.rop = 2;  /* notsrc_and_dst */
            break;
      case 0xd0:
            cpssp->bitblt.rop = 3;  /* notsrc */
            break;
      case 0x09:
            cpssp->bitblt.rop = 4;  /* src_and_notdst */
            break;
      case 0x0b:
            cpssp->bitblt.rop = 5;  /* notdst */
            break;
      case 0x59:
            cpssp->bitblt.rop = 6;  /* src_xor_dst */
            break;
      case 0xda:
            cpssp->bitblt.rop = 7;  /* notsrc_or_notdst */
            break;
      case 0x05:
            cpssp->bitblt.rop = 8;  /* src_and_dst */
            break;
      case 0x95:
            cpssp->bitblt.rop = 9;  /* src_notxor_dst */
            break;
      case 0x06:
            cpssp->bitblt.rop = 10; /* dst (nop) */
            break;
      case 0xd6:
            cpssp->bitblt.rop = 11; /* notsrc_or_dst */
            break;
      case 0x0d:
            cpssp->bitblt.rop = 12; /* src */
            break;
      case 0xad:
            cpssp->bitblt.rop = 13; /* src_or_notdst */
            break;
      case 0x6d:
            cpssp->bitblt.rop = 14; /* src_or_dst */
            break;
      case 0x0e:
            cpssp->bitblt.rop = 15; /* one */
            break;
      default:
#ifdef CIRRUS_DEBUG_BITBLT
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   "Ignoring unknown raster operation 0x%02hx\n",
                   cpssp->blt_rop);
#endif
            _cirrus_bitblt_reset(cpssp);
            return;
      }

      /* trm 5.1 and trm 9.4.7 */
      switch (cpssp->bitblt.color_expand_width) {
      case 1:                 /* 8bpp */
            cpssp->bitblt.fg_color = cpssp->shadow_gr1;
            cpssp->bitblt.bg_color = cpssp->shadow_gr0;
            break;
      case 2:                 /* 16bpp */
            cpssp->bitblt.fg_color = cpssp->shadow_gr1
                  | (cpssp->blt_color_exp_fg_bg[1] << 8);
            cpssp->bitblt.bg_color = cpssp->shadow_gr0
                  | (cpssp->blt_color_exp_fg_bg[0] << 8);
            break;
      case 3:                 /* 24bpp */
            cpssp->bitblt.fg_color = cpssp->shadow_gr1
                  | (cpssp->blt_color_exp_fg_bg[1] << 8)
                  | (cpssp->blt_color_exp_fg_bg[3] << 16);
            /* 24bpp color expansion must use transparency */
            cpssp->bitblt.bg_color = 0;
            break;
      case 4:                 /* 32bpp */
            cpssp->bitblt.fg_color = cpssp->shadow_gr1
                  | (cpssp->blt_color_exp_fg_bg[1] << 8)
                  | (cpssp->blt_color_exp_fg_bg[3] << 16)
                  | (cpssp->blt_color_exp_fg_bg[5] << 24);
            cpssp->bitblt.bg_color = cpssp->shadow_gr0
                  | (cpssp->blt_color_exp_fg_bg[0] << 8)
                  | (cpssp->blt_color_exp_fg_bg[2] << 16)
                  | (cpssp->blt_color_exp_fg_bg[4] << 24);
            break;
      default:
            assert(0); /* impossible */
      }

#ifdef CIRRUS_DEBUG_BITBLT
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
             "Initialized struct bitblt:\n"
             "  fg_color = 0x%08lx\n"
             "  bg_color = 0x%08lx\n"
             "  width    = %d\n"
             "  height   = %d\n"
             "  dest_pitch = %d\n"
             "  src_pitch  = %d\n"
             "  destination_pointer = &vram[%d]\n"
             "  source_pointer      = &vram[%d]\n"
             "  mode = 0x%02hx\n"
             "  color_expand_width = %d (in bytes)\n"
             "  mode_extensions    = 0x%02hx\n"
             "  dest_left_side_clipping = 0x%02hx\n"
             "  rop = %d\n"
             "  key_color[0] = 0x%02hx\n"
             "  key_color[1] = 0x%02hx\n",
             cpssp->bitblt.fg_color,
             cpssp->bitblt.bg_color,
             cpssp->bitblt.width,
             cpssp->bitblt.height,
             cpssp->bitblt.dest_pitch,
             cpssp->bitblt.src_pitch,
             (int) dest_offset,
             (int) src_offset,
             cpssp->bitblt.mode,
             cpssp->bitblt.color_expand_width,
             cpssp->bitblt.mode_extensions,
             cpssp->bitblt.dest_left_side_clipping,
             cpssp->bitblt.rop,
             cpssp->bitblt.key_color[0],
             cpssp->bitblt.key_color[1]
            );
#endif

      /*
       * second: what are we going to do now?
       */

      if ((cpssp->bitblt.mode_extensions & BITBLT_SOLID_COLOR_FILL)
       && ((cpssp->bitblt.mode & (BITBLT_COLOR_EXPAND
                              | BITBLT_8X8_PATTERN_COPY
                              | BITBLT_TRANSPARENCY))
                        == (BITBLT_COLOR_EXPAND | BITBLT_8X8_PATTERN_COPY))) {
            _cirrus_bitblt_do_solid_color_fill(cpssp);

      } else if ((cpssp->bitblt.mode & (BITBLT_COLOR_EXPAND
                              | BITBLT_8X8_PATTERN_COPY))
                        == BITBLT_COLOR_EXPAND) {
            _cirrus_bitblt_do_color_expansion(cpssp);

      } else if (cpssp->bitblt.mode & BITBLT_8X8_PATTERN_COPY) {
            _cirrus_bitblt_do_pattern_fill(cpssp);

      } else {
            _cirrus_bitblt_do_simple_rop(cpssp);
      }
}

#undef CIRRUS_IS_8BPP
#undef CIRRUS_IS_16BPP

/*************************************************************************/
/*                                                                       */
/* Functions for some special registers                                  */
/*                                                                       */
/*************************************************************************/

static void
_cirrus_write_blt_start(struct cpssp *cpssp, unsigned char val)
{
      unsigned char old_val;

      old_val = cpssp->blt_start_status;
      cpssp->blt_start_status = val;

      if (((old_val & BITBLT_RESET) == 0)
       && (val & BITBLT_RESET)) {
            _cirrus_bitblt_reset(cpssp);
      } else if (((old_val & BITBLT_START) == 0)
               && (val & BITBLT_START)) {
            _cirrus_bitblt_start(cpssp);
      }
}

static unsigned char
_cirrus_read_hidden_dac(struct cpssp *cpssp)
{
      unsigned char value;

      if (cpssp->ext_hidden_dac_counter == 4) {
            value = cpssp->ext_hidden_dac;
            cpssp->ext_hidden_dac_counter = 0;
#if (1 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   " 0x%02hx\n", value);
#endif
      } else {
            value = cpssp->vga.col_pel_mask;
            cpssp->ext_hidden_dac_counter++;
      }
      return value;
}

static void
_cirrus_write_hidden_dac(struct cpssp *cpssp, unsigned char val)
{
      if (cpssp->ext_hidden_dac_counter == 4) {
            cpssp->ext_hidden_dac = val;
#if (1 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                   " 0x%02hx\n", val);
#endif
      } else {
            cpssp->vga.col_pel_mask = val;
      }
      cpssp->ext_hidden_dac_counter = 0;
}

/*
 * Bit 1 of SR12 is
 * 1: Three extra DAC LUT entries available
 *    (x0 cursor background, xf cursor foreground, x2 overscan border color)
 * 0: Above not available -> standard vga
 */
#define ALLOW_ACCESS_TO_DAC_EXTENDED_COLORS 0x02
#define DAC_EXTENDED_COLOR_MASK             0x0f

static unsigned char
_cirrus_read_palette_data(struct cpssp *cpssp)
{
      unsigned char value;

      if (cpssp->ext_graph_curs_attr & ALLOW_ACCESS_TO_DAC_EXTENDED_COLORS) {
            switch (cpssp->vga.col_reg_03c7 & DAC_EXTENDED_COLOR_MASK) {
            case 0x00:      /* cursor background */
                  value = video_col_get(cpssp, 256, cpssp->vga.col_reg_03c7_rgb)
                        >> 2;
                  break;
            case 0x0f:      /* cursor foreground */
                  value = video_col_get(cpssp, 257, cpssp->vga.col_reg_03c7_rgb)
                        >> 2;
                  break;
            case 0x02:      /* overscan color */
                  value = video_col_get(cpssp, 258, cpssp->vga.col_reg_03c7_rgb)
                        >> 2;
                  break;
            default:
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Read access to dac extended colors but "
                         "col_reg_03c7 is 0x%02hx\n",
                         (int) (cpssp->vga.col_reg_03c7 &
                              DAC_EXTENDED_COLOR_MASK));
                  value = 0x00;
            }
      } else {
            value = video_col_get(cpssp, cpssp->vga.col_reg_03c7,
                              cpssp->vga.col_reg_03c7_rgb) >> 2;
      }

      cpssp->vga.col_reg_03c7_rgb++;
      if (cpssp->vga.col_reg_03c7_rgb == 3) {
            cpssp->vga.col_reg_03c7_rgb = 0;
            cpssp->vga.col_reg_03c7++;
      }

      return value;
}

static void
_cirrus_write_palette_data(struct cpssp *cpssp, unsigned char val)
{
      if (cpssp->ext_graph_curs_attr & ALLOW_ACCESS_TO_DAC_EXTENDED_COLORS) {
            switch (cpssp->vga.col_reg_03c8 & DAC_EXTENDED_COLOR_MASK) {
            case 0x00:
                  video_col_set(cpssp, 256, cpssp->vga.col_reg_03c8_rgb, val << 2);
                  break;
            case 0x0f:
                  video_col_set(cpssp, 257, cpssp->vga.col_reg_03c8_rgb, val << 2);
                  break;
            case 0x02:
                  video_col_set(cpssp, 258, cpssp->vga.col_reg_03c8_rgb, val << 2);
                  break;
            default:
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Write access to dac extended colors but "
                         "col_reg_03c8 is 0x%02hx\n",
                         (int) (cpssp->vga.col_reg_03c8 &
                              DAC_EXTENDED_COLOR_MASK));
                  break;
            }
      } else {
            video_col_set(cpssp, cpssp->vga.col_reg_03c8,
                        cpssp->vga.col_reg_03c8_rgb, val << 2);
      }

      cpssp->vga.col_reg_03c8_rgb++;
      if (cpssp->vga.col_reg_03c8_rgb == 3) {
            cpssp->vga.col_reg_03c8_rgb = 0;
            cpssp->vga.col_reg_03c8++;
      }
}

#undef ALLOW_ACCESS_TO_DAC_EXTENDED_COLORS
#undef DAC_EXTENDED_COLOR_MASK

/*
 * The registers SRF and SR17 determine the
 * address mapping from linear frame buffer
 * adresses to memory chip array addresses:
 *
 * SRF[4:3] -  data bus width
 *               00 : reserved
 *               01 : reserved
 *               10 : 32 bit
 *               11 : 64 bit
 * SRF[7]   -  second bank
 *               0 : disabled
 *               1 : enabled
 * SR17[7]  -  bank size
 *               0 : 2MB or mixed
 *               1 : 1MB
 * SR17[1]  -  bank swap
 *               0 : disabled
 *               1 : enabled
 *
 * The theoretically resulting 32 modes can be reduced
 * to 4 effective modes (see also comment at, and
 * implementation of, video_read[bwl]/video_write[bwl]
 * functions).
 */
static void
_cirrus_update_mem_access_mode(struct cpssp *cpssp)
{
      if (cpssp->ext_dram_control & 0x08) {
            /* bus width: 64 bit */
            if (cpssp->ext_config_rb_ext_control & 0x02) {
                  /* bank swap */
                  cpssp->mem_access_mode = B64_SWAP;
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         "B64_SWAP\n");
#endif
            } else {
                  /* no bank swap */
                  if (cpssp->ext_config_rb_ext_control & 0x80) {
                        /* 1 Mbyte bank */
                        if (cpssp->ext_dram_control & 0x80) {
                              /* second bank enabled */
                              cpssp->mem_access_mode = B64_NOSWAP_1MB_2B;
#if (1 < CIRRUS_DEBUG)
                              faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                                          "B64_NOSWAP_1MB_2B\n");
#endif
                        } else {
                              /* second bank disabled */
                              cpssp->mem_access_mode = B64_NOSWAP_1MB_1B;
#if (1 < CIRRUS_DEBUG)
                              faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                                          "B64_NOSWAP_1MB_1B\n");
#endif
                        }
                  } else {
                        /* 2 Mbyte bank */
                        if (cpssp->ext_dram_control & 0x80) {
                              /* second bank enabled */
                              cpssp->mem_access_mode = B64_NOSWAP_2MB_2B;
#if (1 < CIRRUS_DEBUG)
                              faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                                          "B64_NOSWAP_2MB_2B\n");
#endif
                        } else {
                              /* second bank disabled */
                              cpssp->mem_access_mode = B64_NOSWAP_2MB_1B;
#if (1 < CIRRUS_DEBUG)
                              faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                                          "B64_NOSWAP_2MB_1B\n");
#endif
                        }
                  }
            }
      } else {
            /* bus width: 32 bit */
            cpssp->mem_access_mode = B32;
#if (1 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "B32\n");
#endif
      }
}

/*************************************************************************/
/*                                                                       */
/* I/O space access functions                                            */
/*                                                                       */
/*************************************************************************/

/** update the ext_ddc2b_eeprom_control register, if the card is configured
 *  for ddc access.
 *  @param cpssp cirrus instance
 */
static void
cirrus_ddc_update_reg(struct cpssp *cpssp)
{
      unsigned char value = (cpssp->i2c_ddc_bus.clk << 2) 
                      | (cpssp->i2c_ddc_bus.data << 7);

      if (((cpssp->ext_ddc2b_eeprom_control >> 6) & 1) == 0) {
            /* EEPROM mode, don't update the register */
            return;
      }

      /* update values in ddc register */
      cpssp->ext_ddc2b_eeprom_control &= ~0x84;
      cpssp->ext_ddc2b_eeprom_control |= value;

#if 0 /* debug output only */
      faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", 
            "recieved data from bus: clk=%d, data=%d\n",
            cpssp->i2c_ddc_bus.clk, 
            cpssp->i2c_ddc_bus.data);
#endif /* debug output only */
}

static void
cirrus_ddc_data_event(void *_cpssp, bool data)
{
      struct cpssp *cpssp = (struct cpssp*)_cpssp;
      cpssp->i2c_ddc_bus.data = data;

      cirrus_ddc_update_reg(cpssp);
}

static void
cirrus_ddc_clk_event(void *_cpssp, bool clk)
{
      struct cpssp *cpssp = (struct cpssp*)_cpssp;
      cpssp->i2c_ddc_bus.clk = clk;

      cirrus_ddc_update_reg(cpssp);
}

static void
cirrus_sr8_out(struct cpssp *cpssp, unsigned char val)
{
      /* bits: 
       *    0: DDCS Clock out/EECS Output
       *    1: DDC Data Out/EEPROM Data In
       *    2: DDCCLK Readback/SK to EEPROM  (read only)
       *    3: Reserved/Data to EEPROM
       *    4: Reserved/Enable EEPROM Data and SK
       *    5: Reserved/Latch ESYNC and EVIDEO#
       *    6: DDC2B Configuration
       *    7: DDCDAT Readback/EEDI Readback (read only)
       */

      unsigned int mode_change = ((val >> 6) & 1) 
                  ^ ((cpssp->ext_ddc2b_eeprom_control >> 6) & 1);

      /* mask out read only bits and update control register */
      cpssp->ext_ddc2b_eeprom_control = 
            (val & ~(0x84)) 
            | (cpssp->ext_ddc2b_eeprom_control & 0x84);

      if ((val >> 6) & 1) {
            /* ddc mode */
            bool data;
            bool clock;

            data = (val >> 1) & 1;
            clock = (val >> 0) & 1;

            /* propagate to i2c/ddc bus */
            sig_i2c_bus_set_data(cpssp->port_vga->ddc, cpssp, data);
            sig_i2c_bus_set_clk(cpssp->port_vga->ddc, cpssp, clock);

            /* change from eeprom to ddc -> update register with values
             * from line */
            if (mode_change) {
                  cirrus_ddc_update_reg(cpssp);
            }
      } else {
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                  "sr8: eeprom mode not implemented yet.\n");
      }
}

#define CIRRUS_IO_RESPONSIBLE 0
#define CIRRUS_IO_IGNORE      1

static unsigned char
_cirrus_vga_inb(struct cpssp *cpssp, unsigned short port)
{
      unsigned char value;

      switch (port) {
      case 0x3c4:             /* sequencer index */
            /* support readback of cursor positions */
            switch (cpssp->vga.seq_reg_03c4) {
            case 0x10:      /* trm 8.7 */
            case 0x30:
            case 0x50:
            case 0xf0:
                  value = cpssp->hw_cursor_x & 0x07;
                  break;
            case 0x11:      /* trm 8.8 */
            case 0x31:
            case 0x51:
            case 0xf1:
                  value = cpssp->hw_cursor_y & 0x07;
                  break;
            default:
                  value = cpssp->vga.seq_reg_03c4;
                  break;
            }
            break;

      case 0x3c5:             /* sequencer register */
            switch (cpssp->vga.seq_reg_03c4) {
            case 0x06:
                  /* legacy unlock extensions */
                  value = (cpssp->ext_key == 0x12) ? 0x12 : 0x0f;
                  break;
            case 0x07:
                  value = cpssp->ext_ext_sequencer_mode;
                  break;
            case 0x08:
                  value = cpssp->ext_ddc2b_eeprom_control;
                  break;
            case 0x09:
                  value = cpssp->ext_scratch_pad_01[0];
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 0 = 0x%02x\n",
                              value);
#endif
                  break;
            case 0x0a:
                  value = cpssp->ext_scratch_pad_01[1];
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 1 = 0x%02x\n",
                              value);
#endif
                  break;
            case 0x0b:
                  value = cpssp->ext_vclk_numerator[0];
                  break;
            case 0x0c:
                  value = cpssp->ext_vclk_numerator[1];
                  break;
            case 0x0d:
                  value = cpssp->ext_vclk_numerator[2];
                  break;
            case 0x0e:
                  value = cpssp->ext_vclk_numerator[3];
                  break;
            case 0x0f:
                  value = cpssp->ext_dram_control;
                  break;
            case 0x10:
            case 0x30:
            case 0x50:
            case 0x70:
            case 0x90:
            case 0xb0:
            case 0xd0:
            case 0xf0:
                  value = cpssp->ext_graph_curs_x_pos;
                  break;
            case 0x11:
            case 0x31:
            case 0x51:
            case 0x71:
            case 0x91:
            case 0xb1:
            case 0xd1:
            case 0xf1:
                  value = cpssp->ext_graph_curs_y_pos;
                  break;
            case 0x12:
                  value = cpssp->ext_graph_curs_attr;
                  break;
            case 0x13:
                  value = cpssp->ext_graph_curs_pattern_addr_offset;
                  break;
            case 0x14:
                  value = cpssp->ext_scratch_pad_23[0];
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 2 = 0x%02x\n",
                              value);
#endif
                  break;
            case 0x15:
                  value = cpssp->ext_scratch_pad_23[1];
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 3 = 0x%02x\n",
                              value);
#endif
                  break;
            case 0x16:
                  value = cpssp->ext_display_fifo_threshold_control;
                  break;
            case 0x17:
                  value = cpssp->ext_config_rb_ext_control;
                  break;
            case 0x18:
                  value = cpssp->ext_sig_gen_control;
                  break;
            case 0x19:
                  value = cpssp->ext_sig_gen_result_lb;
                  break;
            case 0x1a:
                  value = cpssp->ext_sig_gen_result_hb;
                  break;
            case 0x1b:
                  value = cpssp->ext_vclk_denominator[0];
                  break;
            case 0x1c:
                  value = cpssp->ext_vclk_denominator[1];
                  break;
            case 0x1d:
                  value = cpssp->ext_vclk_denominator[2];
                  break;
            case 0x1e:
                  value = cpssp->ext_vclk_denominator[3];
                  break;
            case 0x1f:
                  value = cpssp->ext_mclk_select;
                  break;
            default:
                  goto umvga_handled;
            }
            break;

      case 0x3c6:             /* pixel mask & hidden dac */
            value = _cirrus_read_hidden_dac(cpssp);
            break;

      case 0x3c9:             /* palette data */
            value = _cirrus_read_palette_data(cpssp);
            break;

      case 0x3cf:             /* graphics register */
            switch (cpssp->vga.gr_reg_03ce) {
            case 0x00:
                  value = cpssp->shadow_gr0;
                  break;
            case 0x01:
                  value = cpssp->shadow_gr1;
                  break;
            case 0x09:
                  value = cpssp->ext_offset_reg_0;
                  break;
            case 0x0a:
                  value = cpssp->ext_offset_reg_1;
                  break;
            case 0x0b:
                  value = cpssp->ext_graph_contr_mode_ext;
                  break;
            case 0x0c:
                  value = cpssp->ext_color_chroma_key_comp;
                  break;
            case 0x0d:
                  value = cpssp->ext_color_mask_chroma_key;
                  break;
            case 0x0e:
                  value = cpssp->ext_power_management;
                  break;
            case 0x10:
                  value = cpssp->blt_color_exp_fg_bg[0];
                  break;
            case 0x11:
                  value = cpssp->blt_color_exp_fg_bg[1];
                  break;
            case 0x12:
                  value = cpssp->blt_color_exp_fg_bg[2];
                  break;
            case 0x13:
                  value = cpssp->blt_color_exp_fg_bg[3];
                  break;
            case 0x14:
                  value = cpssp->blt_color_exp_fg_bg[4];
                  break;
            case 0x15:
                  value = cpssp->blt_color_exp_fg_bg[5];
                  break;
            case 0x16:
                  value = cpssp->scanline & 0x00ff;
                  break;
            case 0x17:
                  value = cpssp->ext_act_disp_line_rb_1
                        | ((cpssp->scanline & 0x0300) >> 8);
                  break;
            case 0x18:
                  value = cpssp->ext_ext_dram_controls;
                  break;
            case 0x19:
                  value = cpssp->ext_gpio_port_config;
                  break;
            case 0x1a:
                  value = cpssp->ext_scratch_pad_45[0];
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 4 = 0x%02x\n",
                              value);
#endif
                  break;
            case 0x1b:
                  value = cpssp->ext_scratch_pad_45[1];
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 5 = 0x%02x\n",
                              value);
#endif
                  break;
            case 0x20:
                  value = cpssp->blt_width[0];
                  break;
            case 0x21:
                  value = cpssp->blt_width[1];
                  break;
            case 0x22:
                  value = cpssp->blt_height[0];
                  break;
            case 0x23:
                  value = cpssp->blt_height[1];
                  break;
            case 0x24:
                  value = cpssp->blt_dest_pitch[0];
                  break;
            case 0x25:
                  value = cpssp->blt_dest_pitch[1];
                  break;
            case 0x26:
                  value = cpssp->blt_source_pitch[0];
                  break;
            case 0x27:
                  value = cpssp->blt_source_pitch[1];
                  break;
            case 0x28:
                  value = cpssp->blt_dest_start[0];
                  break;
            case 0x29:
                  value = cpssp->blt_dest_start[1];
                  break;
            case 0x2a:
                  value = cpssp->blt_dest_start[2];
                  break;
            case 0x2c:
                  value = cpssp->blt_source_start[0];
                  break;
            case 0x2d:
                  value = cpssp->blt_source_start[1];
                  break;
            case 0x2e:
                  value = cpssp->blt_source_start[2];
                  break;
            case 0x2f:
                  value = cpssp->blt_dest_left_side_clipping;
                  break;
            case 0x30:
                  value = cpssp->blt_mode;
                  break;
            case 0x31:
                  value = cpssp->blt_start_status;
                  break;
            case 0x32:
                  value = cpssp->blt_rop;
                  break;
            case 0x33:
                  value = cpssp->blt_mode_extensions;
                  break;
            case 0x34:
                  value = cpssp->blt_transp_blt_key_color[0];
                  break;
            case 0x35:
                  value = cpssp->blt_transp_blt_key_color[1];
                  break;
            default:
                  goto umvga_handled;
            }
            break;

      case 0x3d5:             /* crtc register */
            switch (cpssp->vga.crt_reg_03d4) {
            case 0x19:
                  value = cpssp->ext_interlace_end;
                  break;
            case 0x1a:
                  value = cpssp->ext_misc_control;
                  break;
            case 0x1b:
                  value = cpssp->ext_ext_disp_controls;
                  break;
            case 0x1c:
                  value = cpssp->ext_sync_adjust_genlock;
                  break;
            case 0x1d:
                  value = cpssp->ext_overlay_ext_control;
                  break;
            case 0x22:
                  /* FIXME graphics data latches readback */
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "CR22 read unimplemented :-(\n");
                  value = 0xff;
                  break;
            case 0x24:
                  /* state=0 -> index write; state=1 -> data write */
                  value = cpssp->vga.attr_reg_03c0_state << 7;
                  break;
            case 0x25:
                  value = cpssp->ext_part_status;
                  break;
            case 0x26:
                  /* attribute controller index readback */
                  value = cpssp->vga.attr_reg_03c0 & 0x1f;
                  break;
            case 0x27:
                  value = cpssp->ext_id;
                  break;
            default:
                  goto umvga_handled;
            }
            break;

      default:
      umvga_handled:
            /* umvga wants the portrange offset */
            value = vga__inb(cpssp, port - CIRRUS_IOPORT);
      }

#if (1 < CIRRUS_DEBUG)
      /* i'm not interested in... */
      if (! ((port == 0x3c0 || port == 0x3c1 || port == 0x3c2
           || port == 0x3c6 || port == 0x3c7 || port == 0x3c8
           || port == 0x3c9 || port == 0x3ca || port == 0x3cc
           || port == 0x3da)
           || (port == 0x3d4)
           || (port == 0x3d5 && cpssp->vga.crt_reg_03d4 < 0x19)
           || (port == 0x3c4) /* ignore cursor pos. readback */
           || (port == 0x3c5 && cpssp->vga.seq_reg_03c4 < 0x06)
           || (port == 0x3ce)
           || (port == 0x3cf && cpssp->vga.gr_reg_03ce < 0x09)))  {
            if (port == 0x3cf) {
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         " GR%-2hX  -> 0x%02hx\n",
                         cpssp->vga.gr_reg_03ce, value);
            } else if (port == 0x3c5) {
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         " SR%-2hX  -> 0x%02hx\n",
                         cpssp->vga.seq_reg_03c4, value);
            } else if (port == 0x3d5) {
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         " CR%-2hX  -> 0x%02hx\n",
                         cpssp->vga.crt_reg_03d4, value);
            } else {
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         " %3X -> 0x%02hx\n",
                         port, value);
            }
      }
#endif
      return value;
}

static int
cirrus_vga_ior(
      void *_cpssp,
      uint32_t port,
      unsigned int bs,
      uint32_t *valp
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      if (port < CIRRUS_IOPORT
       || CIRRUS_IOPORT + 0x20 <= port
       || (pci_getconfigb(cpssp->pci_config_space, 0x04) & PCI_COMMAND_IO) == 0) {
            return 1;
      }

      *valp = 0x00000000;
      if ((bs >> 0) & 1) {
            *valp |= _cirrus_vga_inb(cpssp, port + 0) << 0;
      }
      if ((bs >> 1) & 1) {
            *valp |= _cirrus_vga_inb(cpssp, port + 1) << 8;
      }
      if ((bs >> 2) & 1) {
            *valp |= _cirrus_vga_inb(cpssp, port + 2) << 16;
      }
      if ((bs >> 3) & 1) {
            *valp |= _cirrus_vga_inb(cpssp, port + 3) << 24;
      }

      return 0;
}

static void
_cirrus_vga_outb(
      struct cpssp *cpssp,
      unsigned char val,
      unsigned short port
)
{
#if (1 < CIRRUS_DEBUG)
      /* i'm not interested in... */
      if (! ((port == 0x3c0 || port == 0x3c1 || port == 0x3c2
           || port == 0x3c6 || port == 0x3c7 || port == 0x3c8
           || port == 0x3c9 || port == 0x3ca || port == 0x3cc
           || port == 0x3da)
           || (port == 0x3d4)
           || (port == 0x3d5 && cpssp->vga.crt_reg_03d4 < 0x19)
           || (port == 0x3c4)
           || (port == 0x3c5 && cpssp->vga.seq_reg_03c4 < 0x06)
           || (port == 0x3ce)
           || (port == 0x3cf && cpssp->vga.gr_reg_03ce < 0x09))) {
            if (port == 0x3cf) {
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         "GR%-2hX  <- 0x%02hx\n",
                         cpssp->vga.gr_reg_03ce, val);
            } else if (port == 0x3c5) {
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         "SR%-2hX  <- 0x%02hx\n",
                         cpssp->vga.seq_reg_03c4, val);
            } else if (port == 0x3d5) {
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         "CR%-2hX  <- 0x%02hx\n",
                         cpssp->vga.crt_reg_03d4, val);
            } else {
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                         "%3X <- 0x%02hx\n",
                         port, val);
            }
      }
#endif
      switch (port) {
      case 0x3c5:             /* sequencer register */
            switch (cpssp->vga.seq_reg_03c4) {
            case 0x06:
                  cpssp->ext_key = val;
                  break;
            case 0x07:
                  if (val & 0x01) {
#if (0 < CIRRUS_DEBUG)
                        if (cpssp->mode == VGA)
                              faum_log(FAUM_LOG_INFO, __FUNCTION__,
                                    "", "Switching to CIRRUS "
                                    "mode: SR7 <- 0x%02hx\n", val);
#endif
                        cpssp->mode = CIRRUS;
                  } else {
#if (0 < CIRRUS_DEBUG)
                        if (cpssp->mode == CIRRUS)
                              faum_log(FAUM_LOG_INFO, __FUNCTION__,
                                    "", "Switching to VGA "
                                    "mode: SR7 <- 0x%02hx\n", val);
#endif
                        cpssp->mode = VGA;
                  }
                  if ((val & 0x0f) == 0x03) {
                        /* this is a hack to make switching back to
                           console for X's cirrus driver work */
                        /* clock doubled 8bpp, see 9.2 in trm */
                        if (cpssp->mode == CIRRUS)
                              faum_log(FAUM_LOG_WARNING, __FUNCTION__,
                                    "",
                                    "Clock-doubled 8bpp, switching "
                                    " back to VGA legacy mode.\n");
                        cpssp->mode = VGA;
                  }
                  cpssp->ext_ext_sequencer_mode = val;
                  break;
            case 0x08:
                  cirrus_sr8_out(cpssp, val);
                  break;
            case 0x09:
                  cpssp->ext_scratch_pad_01[0] = val;
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 0 = 0x%02x\n",
                              val);
#endif
                  break;
            case 0x0a:
                  cpssp->ext_scratch_pad_01[1] = val;
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 1 = 0x%02x\n",
                              val);
#endif
                  break;
            case 0x0b:
                  cpssp->ext_vclk_numerator[0] = val;
                  break;
            case 0x0c:
                  cpssp->ext_vclk_numerator[1] = val;
                  break;
            case 0x0d:
                  cpssp->ext_vclk_numerator[2] = val;
                  break;
            case 0x0e:
                  cpssp->ext_vclk_numerator[3] = val;
                  break;
            case 0x0f:
                  cpssp->ext_dram_control = val;
                  _cirrus_update_mem_access_mode(cpssp);
                  break;
            case 0x10:
            case 0x30:
            case 0x50:
            case 0x70:
            case 0x90:
            case 0xb0:
            case 0xd0:
            case 0xf0:
                  cpssp->ext_graph_curs_x_pos = val;
                  cpssp->hw_cursor_x =
                        (val << 3) | (cpssp->vga.seq_reg_03c4 >> 5);
                  break;
            case 0x11:
            case 0x31:
            case 0x51:
            case 0x71:
            case 0x91:
            case 0xb1:
            case 0xd1:
            case 0xf1:
                  cpssp->ext_graph_curs_y_pos = val;
                  cpssp->hw_cursor_y =
                        (val << 3) | (cpssp->vga.seq_reg_03c4 >> 5);
                  break;
            case 0x12:
                  cpssp->ext_graph_curs_attr = val;
                  break;
            case 0x13:
                  cpssp->ext_graph_curs_pattern_addr_offset = val;
                  break;
            case 0x14:
                  cpssp->ext_scratch_pad_23[0] = val;
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 2 = 0x%02x\n",
                              val);
#endif
                  break;
            case 0x15:
                  cpssp->ext_scratch_pad_23[1] = val;
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 3 = 0x%02x\n",
                              val);
#endif
                  break;
            case 0x16:
                  cpssp->ext_display_fifo_threshold_control = val;
                  break;
            case 0x17:
                  cpssp->ext_config_rb_ext_control =
                        (cpssp->ext_config_rb_ext_control & 0x38)
                        | (val & 0xc7);
                  _cirrus_update_mem_access_mode(cpssp);
                  break;
            case 0x18:
                  cpssp->ext_sig_gen_control = val;
                  break;
            case 0x19:
                  cpssp->ext_sig_gen_result_lb = val;
                  break;
            case 0x1a:
                  cpssp->ext_sig_gen_result_hb = val;
                  break;
            case 0x1b:
                  cpssp->ext_vclk_denominator[0] = val;
                  break;
            case 0x1c:
                  cpssp->ext_vclk_denominator[1] = val;
                  break;
            case 0x1d:
                  cpssp->ext_vclk_denominator[2] = val;
                  break;
            case 0x1e:
                  cpssp->ext_vclk_denominator[3] = val;
                  break;
            case 0x1f:
                  cpssp->ext_mclk_select = val;
                  break;
            default:
                  goto umvga_handled;
            }
            break;

      case 0x3c6:             /* pixel mask & hidden dac */
            _cirrus_write_hidden_dac(cpssp, val);
            break;

      case 0x3c9:             /* palette data */
            _cirrus_write_palette_data(cpssp, val);
            break;

      case 0x3cf:             /* graphics register */
            /* If you change the bitblt register writes don't forget
               _cirrus_vga_mmio_bitblt_write(). */
            switch (cpssp->vga.gr_reg_03ce) {
            case 0x00:
                  cpssp->shadow_gr0 = val;
                  goto umvga_handled;
            case 0x01:
                  cpssp->shadow_gr1 = val;
                  goto umvga_handled;
            case 0x05:
                  cpssp->vga.gr_mode = val & 0x7f;
                  break;
            case 0x09:
                  cpssp->ext_offset_reg_0 = val;
                  break;
            case 0x0a:
                  cpssp->ext_offset_reg_1 = val;
                  break;
            case 0x0b:
                  if ((cpssp->ext_graph_contr_mode_ext & 0x04)
                   && (! (val & 0x04))) {
                        /* side-effect see trm 8.21 */
                        cpssp->vga.gr_set_reset &= 0x0f;
                        cpssp->vga.gr_enable_set_reset &= 0x0f;
                  }
                  cpssp->ext_graph_contr_mode_ext = val;
                  break;
            case 0x0c:
                  cpssp->ext_color_chroma_key_comp = val;
                  break;
            case 0x0d:
                  cpssp->ext_color_mask_chroma_key = val;
                  break;
            case 0x0e:
                  cpssp->ext_power_management = val;
                  break;
            case 0x10:
                  cpssp->blt_color_exp_fg_bg[0] = val;
                  break;
            case 0x11:
                  cpssp->blt_color_exp_fg_bg[1] = val;
                  break;
            case 0x12:
                  cpssp->blt_color_exp_fg_bg[2] = val;
                  break;
            case 0x13:
                  cpssp->blt_color_exp_fg_bg[3] = val;
                  break;
            case 0x14:
                  cpssp->blt_color_exp_fg_bg[4] = val;
                  break;
            case 0x15:
                  cpssp->blt_color_exp_fg_bg[5] = val;
                  break;
            case 0x16:
                  /* readonly */
                  break;
            case 0x17:
                  /* all bits except 6,5,2 are readonly */
                  cpssp->ext_act_disp_line_rb_1 = val & 0x64;
                  break;
            case 0x18:
                  cpssp->ext_ext_dram_controls = val;
                  break;
            case 0x19:
                  cpssp->ext_gpio_port_config = val;
                  break;
            case 0x1a:
                  cpssp->ext_scratch_pad_45[0] = val;
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 4 = 0x%02x\n",
                              val);
#endif
                  break;
            case 0x1b:
                  cpssp->ext_scratch_pad_45[1] = val;
#if (1 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                              "scratch pad 5 = 0x%02x\n",
                              val);
#endif
                  break;
            case 0x20:
                  cpssp->blt_width[0] = val;
                  break;
            case 0x21:
                  cpssp->blt_width[1] = val & 0x1f;
                  break;
            case 0x22:
                  cpssp->blt_height[0] = val;
                  break;
            case 0x23:
                  cpssp->blt_height[1] = val & 0x07;
                  break;
            case 0x24:
                  cpssp->blt_dest_pitch[0] = val;
                  break;
            case 0x25:
                  cpssp->blt_dest_pitch[1] = val & 0x1f;
                  break;
            case 0x26:
                  cpssp->blt_source_pitch[0] = val;
                  break;
            case 0x27:
                  cpssp->blt_source_pitch[1] = val & 0x1f;
                  break;
            case 0x28:
                  cpssp->blt_dest_start[0] = val;
                  break;
            case 0x29:
                  cpssp->blt_dest_start[1] = val;
                  break;
            case 0x2a:
                  cpssp->blt_dest_start[2] = val & 0x3f;
                  if (cpssp->blt_start_status & BITBLT_AUTOSTART) {
#ifdef CIRRUS_DEBUG_BITBLT
                        faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "",
                               "Wrote GR2A. Autostarting BitBLT...\n");
#endif

                        /* no need to set GR31[4], since the following,
                           when it returns, already buffered the
                           bitblt registers */
                        _cirrus_bitblt_start(cpssp);
                  }
                  break;
            case 0x2c:
                  cpssp->blt_source_start[0] = val;
                  break;
            case 0x2d:
                  cpssp->blt_source_start[1] = val;
                  break;
            case 0x2e:
                  cpssp->blt_source_start[2] = val & 0x3f;
                  break;
            case 0x2f:
                  cpssp->blt_dest_left_side_clipping = val;
                  break;
            case 0x30:
                  cpssp->blt_mode = val;
                  break;
            case 0x31:
                  _cirrus_write_blt_start(cpssp, val);
                  break;
            case 0x32:
                  cpssp->blt_rop = val;
                  break;
            case 0x33:
                  if ((cpssp->ext_power_management & 0x20)
                   || (cpssp->blt_start_status & 0x80)) {
                        cpssp->blt_mode_extensions = val;
                  } else {
#ifdef CIRRUS_DEBUG_BITBLT
                        faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                               "GR33 is write protected\n");
#endif
                  }
                  break;
            case 0x34:
                  cpssp->blt_transp_blt_key_color[0] = val;
                  break;
            case 0x35:
                  cpssp->blt_transp_blt_key_color[1] = val;
                  break;
            default:
                  goto umvga_handled;
            }
            break;

      case 0x3d5:             /* crtc register */
            switch (cpssp->vga.crt_reg_03d4) {
            case 0x19:
                  cpssp->ext_interlace_end = val;
                  break;
            case 0x1a:
                  cpssp->ext_misc_control = val;
                  break;
            case 0x1b:
                  cpssp->ext_ext_disp_controls = val;
                  break;
            case 0x1c:
                  cpssp->ext_sync_adjust_genlock = val;
                  break;
            case 0x1d:
                  cpssp->ext_overlay_ext_control = val;
                  break;
            case 0x22:
            case 0x24:
            case 0x25:
            case 0x26:
            case 0x27:
                  /* these are all readonly registers */
                  goto outb_readonly;
            default:
                  goto umvga_handled;
            }
            break;
      outb_readonly:
#if (0 < CIRRUS_DEBUG)
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "", "Readonly port "
                   "write attempt (port=0x%04hx val=0x%02hx)\n",
                   port, val);
#endif
            break;

      default:
      umvga_handled:
            /* umvga wants the portrange offset */
            vga__outb(cpssp, val, port - CIRRUS_IOPORT);
      }
}

static int
cirrus_vga_iow(
      void *_cpssp,
      uint32_t port,
      unsigned int bs,
      uint32_t val
)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      if (port < CIRRUS_IOPORT
       || CIRRUS_IOPORT + 0x20 <= port
       || (pci_getconfigb(cpssp->pci_config_space, 0x04) & PCI_COMMAND_IO) == 0) {
            return 1;
      }

      if (port == 0x3dc
       && bs == 0xc) {
            /*
             * This is a hack.
             * If you kill f.e. Xvesa you'd be stuck in an
             * extended Cirrus mode. There are no VESA
             * Function 02 calls or so to get you back to a VGA
             * mode. Instead you seem to get:
             * outb(0x03, 0x3de) seems to be legacy mode
             * outb(0x00, 0x3df) ???
             */
            val >>= 16;

            if (val < 0x100) {
#if (0 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_INFO, __FUNCTION__, "",
                         "Switching (back) to VGA mode "
                         "(val=0x%04hx).\n", val);
#endif
                  cpssp->mode                     = VGA;
                  cpssp->ext_ext_sequencer_mode   = val;
                  cpssp->ext_offset_reg_0         = 0;
                  cpssp->ext_offset_reg_1         = 0;
                  cpssp->ext_graph_contr_mode_ext = 0;
                  cpssp->ext_misc_control         = 0;
                  cpssp->ext_ext_disp_controls    = 0;
                  cpssp->ext_overlay_ext_control  = 0;
            } else {
#if (0 < CIRRUS_DEBUG)
                  faum_log(FAUM_LOG_INFO, __FUNCTION__, "",
                         "NOT switching (back) to VGA mode "
                         "(val=0x%04hx).\n", val);
#endif
            }
            return 0;
      }

      if ((bs >> 0) & 1) {
            _cirrus_vga_outb(cpssp, (val >> 0) & 0xff, port + 0);
      }
      if ((bs >> 1) & 1) {
            _cirrus_vga_outb(cpssp, (val >> 8) & 0xff, port + 1);
      }
      if ((bs >> 2) & 1) {
            _cirrus_vga_outb(cpssp, (val >> 16) & 0xff, port + 2);
      }
      if ((bs >> 3) & 1) {
            _cirrus_vga_outb(cpssp, (val >> 24) & 0xff, port + 3);
      }

      return 0;
}

#define PCI_VENDOR_ID_CIRRUS         0x1013
#define PCI_DEVICE_ID_CIRRUS_GD5446  0x00b8
#define PCI_REVISION_ID_CIRRUS       0x01
#define PCI_CLASS_PROG_CIRRUS        0x00
#define PCI_SUBSYSTEM_VENDOR_DEFAULT 0x0000

static void
chip_cirrus_gd5446_power_set(void *_cpssp, unsigned int val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      cpssp->state_power = val;
}

static void
chip_cirrus_gd5446_n_reset_set(void *_cpssp, unsigned int n_val)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      /* When loaded with 0x12 reading returns 0x12, else reading
         returns 0xff. Reset to 0x0f. */
      cpssp->ext_key = 0x0f;                    /* SR6 */
      cpssp->ext_mclk_select = 0x2d;                  /* SR1F */
      cpssp->ext_ext_dram_controls = 0x0f;            /* GR18 */

      /* FIXME: default to 4MB (8 x 256Kx16) */
      cpssp->ext_dram_control = 0x98;                 /* SRF */
      cpssp->ext_config_rb_ext_control = 0x20;  /* SR17 */
      _cirrus_update_mem_access_mode(cpssp);

      cpssp->ext_part_status = 0x40;                  /* CR25 */
      /* device id of gd5446 */
      cpssp->ext_id = PCI_DEVICE_ID_CIRRUS_GD5446;    /* CR27 */

      /*
       * Initialize config space
       */
      /* for pci register configuration see 7.1-7.10 of trm */
      memset(cpssp->pci_config_space, 0, sizeof(uint32_t) * 16);

      pci_setconfigw(cpssp->pci_config_space, /* 0x00 */
                  PCI_VENDOR_ID, PCI_VENDOR_ID_CIRRUS);
      pci_setconfigw(cpssp->pci_config_space, /* 0x02 */
                  PCI_DEVICE_ID, PCI_DEVICE_ID_CIRRUS_GD5446);
      /* PCI_COMMAND_IO will be enabled by the PC BIOS */
      pci_setconfigw(cpssp->pci_config_space, /* 0x06 */
                  PCI_STATUS, PCI_STATUS_DEVSEL_MEDIUM);
      pci_setconfigb(cpssp->pci_config_space, /* 0x08 */
                  PCI_REVISION_ID, PCI_REVISION_ID_CIRRUS);
      pci_setconfigb(cpssp->pci_config_space, /* 0x09 */
                  PCI_CLASS_PROG, PCI_CLASS_PROG_CIRRUS);
      pci_setconfigw(cpssp->pci_config_space, /* 0x0a */
                  PCI_CLASS_DEVICE, PCI_CLASS_DISPLAY_VGA);
      pci_setconfigb(cpssp->pci_config_space, /* 0x0e */
                  PCI_HEADER_TYPE, PCI_HEADER_TYPE_NORMAL);
      /* NOTE: in original cirrus cards/bioses, the subsystem id and
       * subsystem vendor id are taken from the addresses 0x7ffc to 0x7ffe
       * of the bios rom to make it customizable by vendors */
      pci_setconfigw(cpssp->pci_config_space, /* 0x2c */
                  PCI_SUBSYSTEM_VENDOR_ID, PCI_SUBSYSTEM_VENDOR_DEFAULT);

      /*
       * Initialize rest of struct cpssp
       */
      /* Hidden DAC only accessed every fourth read - counting necessary */
      cpssp->ext_hidden_dac = 0;
      cpssp->ext_hidden_dac_counter = 0;

      /* Cache for byte-swapping memory reads and writes */
      cpssp->byte_swap_cache_fill = 0;

      /* Current mode */
      cpssp->mode = VGA;
}

/*************************************************************************/
/*                                                                       */
/* Screen update functions                                               */
/*                                                                       */
/*************************************************************************/

unsigned long
_cirrus_get_display_start_offset(struct cpssp *cpssp)
{
      unsigned long addr;

      addr = cpssp->vga.crt_top_pos
            | ((cpssp->ext_ext_disp_controls   & 0x01) << 16)
            | ((cpssp->ext_ext_disp_controls   & 0x0c) << 15)
            | ((cpssp->ext_overlay_ext_control & 0x80) << 12);

      assert(addr < CIRRUS_MAX_VRAM_SIZE);
      return addr;
}


unsigned int
_cirrus_get_display_pitch(struct cpssp *cpssp)
{
      unsigned int pitch;

      pitch = cpssp->vga.crt_offset
            | ((cpssp->ext_ext_disp_controls & 0x10) << 4);
      pitch <<= 3;

      return pitch;
}

enum pixel_formats {
      UNKNOWN,
      VGA_COMPATIBILITY,
      EIGHT_BPP_GRAYSCALE,
      EIGHT_BPP_DIRECT_COLOR,
      FIFTEEN_BPP_MODE,
      FIFTEEN_BPP_MIX_MODE,
      XGA,
      TWENTYFOUR_BPP_MODE,
      THIRTYTWO_BPP_ALPHA_MODE,
      POWER_OFF
};

int
_cirrus_get_pixel_format(struct cpssp *cpssp)
{
      /* trm table 9-8, SR7[3:1] 8.2, HDR 8.37 */
      int bpp;

      bpp = UNKNOWN;

      switch ((cpssp->ext_ext_sequencer_mode & 0x0e) >> 1) {

      case 0x0:               /* 8bpp */

            if (!(cpssp->ext_hidden_dac & 0x80)) {
                  /* VGA compatibility or Palette mode > 85 MHz */
                  bpp = VGA_COMPATIBILITY;

            } else {
                  switch (cpssp->ext_hidden_dac & 0xcf) {
                  case 0xc8:
                        /* 8-bpp grayscale */
                        bpp = EIGHT_BPP_GRAYSCALE;
                        break;
                  case 0xc9:
                        /* 8-bpp direct color */
                        bpp = EIGHT_BPP_DIRECT_COLOR;
                        break;
                  case 0xc6:
                  case 0xc7:
                        /* Power-down DAC */
                        bpp = POWER_OFF;
                        break;
                  default:
                        faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                               "Unknown hidden-dac-register value "
                               "0x%02hx (SR7 = 0x%02hx)\n",
                               cpssp->ext_hidden_dac,
                               cpssp->ext_ext_sequencer_mode);
                        break;
                  }
            }
            break;

      case 0x1:         /* clock doubled 8bpp, see 9.2 in gd544xtrm.pdf */
            /* must never get here, only vgacore can draw this mode */
            assert(0);
            break;

      case 0x3:         /* 16bpp */

            if (!(cpssp->ext_hidden_dac & 0x80)) {
                  /* VGA compatibility or Palette mode > 85 MHz */
                  bpp = VGA_COMPATIBILITY;

            } else if ((cpssp->ext_hidden_dac & 0xc0) == 0x80) {
                  /* 5:5:5 mode with 32K colors (Sierra)  */
                  bpp = FIFTEEN_BPP_MODE;

            } else if ((cpssp->ext_hidden_dac & 0xd0) == 0x90
                     || (cpssp->ext_hidden_dac & 0xdf) == 0xd0) {
                  /* 5:5:5 with 256-Color Mix mode (Sierra) */
                  bpp = FIFTEEN_BPP_MIX_MODE;

            } else {
                  switch (cpssp->ext_hidden_dac & 0xcf) {
                  case 0xc0:
                        /* 5:5:5 mode with 32K colors (Sierra) */
                        bpp = FIFTEEN_BPP_MODE;
                        break;
                  case 0xc1:
                        /* 5:6:5 mode with 64K colors (XGA) */
                        bpp = XGA;
                        break;
                  case 0xc6:
                  case 0xc7:
                        /* Power-down DAC */
                        bpp = POWER_OFF;
                        break;
                  default:
                        faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                               "Unknown hidden-dac-register value "
                               "0x%02hx (SR7 = 0x%02hx)\n",
                               cpssp->ext_hidden_dac,
                               cpssp->ext_ext_sequencer_mode);
                        break;
                  }
            }
            break;

      case 0x2:              /* 24bpp */

            switch (cpssp->ext_hidden_dac & 0xcf) {
            case 0xc5:
                  /* 8:8:8 mode with 16.8M colors */
                  bpp = TWENTYFOUR_BPP_MODE;
                  break;
            case 0xc6:
            case 0xc7:
                  /* Power-down DAC */
                  bpp = POWER_OFF;
                  break;
            default:
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Unknown hidden-dac-register value "
                         "0x%02hx (SR7 = 0x%02hx)\n",
                         cpssp->ext_hidden_dac,
                         cpssp->ext_ext_sequencer_mode);
                  break;
            }
            break;

      case 0x4:              /* 32bpp */

            switch (cpssp->ext_hidden_dac & 0xcf) {
            case 0xc5:
                  /* 8:8:8:8 mode with 16.8M colors and alpha */
                  bpp = THIRTYTWO_BPP_ALPHA_MODE;
                  break;
            case 0xc6:
            case 0xc7:
                  /* Power-down DAC */
                  bpp = POWER_OFF;
                  break;
            default:
                  faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                         "Unknown hidden-dac-register value "
                         "0x%02hx (SR7 = 0x%02hx)\n",
                         cpssp->ext_hidden_dac,
                         cpssp->ext_ext_sequencer_mode);
                  break;
            }
            break;

      default:
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                   "Unknown sequencer-mode-register[3:1] "
                   "value 0x%1hx\n",
                   (cpssp->ext_ext_sequencer_mode & 0x0e) >> 1);
            break;
      }

      return bpp;
}

void
_cirrus_get_resolution(
      struct cpssp *cpssp,
      unsigned int *width,
      unsigned int *height
)
{
      unsigned int w, h;

      w = (cpssp->vga.crt_horizontal_display_end + 1) * 8;
      /* FIXME clock doubling? trm 4.22 */

      h = cpssp->vga.crt_vertical_display_end
            | ((cpssp->vga.crt_overflow & 0x02) << 7)
            | ((cpssp->vga.crt_overflow & 0x40) << 3);
      h += 1;
      if (cpssp->ext_misc_control & 0x01) {
            /* interlaced mode */
            h *= 2;
      }

      *width = w;
      *height = h;
}

/* GEN_RGB will be run once per pixel and assumes a variable
 * x, which holds the current column, and cpssp->offset to
 * point to the beginning of the current scan line data
 * within the frame buffer; the resulting pixel color data
 * will be assigned to the variables r, g, b.
 */

/* trm 9.3.2.1 */
#define GEN_NAME vga_compatibility
#define GEN_RGB uint8_t col = video_readb(cpssp, cpssp->offset + x); \
            r = video_col_get(cpssp, col, 0); \
            g = video_col_get(cpssp, col, 1); \
            b = video_col_get(cpssp, col, 2)
#include "chip_cirrus_gd5446_gen.c"
#undef GEN_RGB
#undef GEN_NAME

/* trm 9.3.2.2 */
#define GEN_NAME 8bpp_grayscale
#define GEN_RGB r = g = b = video_readb(cpssp, cpssp->offset + x)
#include "chip_cirrus_gd5446_gen.c"
#undef GEN_RGB
#undef GEN_NAME

/* trm 9.3.2.3 */
#define GEN_NAME 8bpp_direct_color
#define GEN_RGB uint8_t col = video_readb(cpssp, cpssp->offset + x);\
            r = (col & 0xe0) << 0; \
            g = (col & 0x1c) << 3; \
            b = (col & 0x03) << 6
#include "chip_cirrus_gd5446_gen.c"
#undef GEN_RGB
#undef GEN_NAME

/* trm 9.3.2.5 */
#define GEN_NAME 555_mode
#define GEN_RGB uint16_t col = video_readw(cpssp, cpssp->offset + 2*x);\
            r = ((col >> 10) & 0x1f) << 3; \
            g = ((col >>  5) & 0x1f) << 3; \
            b = ((col >>  0) & 0x1f) << 3
#include "chip_cirrus_gd5446_gen.c"
#undef GEN_RGB
#undef GEN_NAME

/* trm 9.3.2.6 */
#define GEN_NAME 555_mix_mode
#define GEN_RGB uint16_t col = video_readw(cpssp, cpssp->offset + 2*x); \
            if (col & 0x8000) {                       \
                  col &= 0x00ff;                \
                  r = video_col_get(cpssp, col, 0);   \
                  g = video_col_get(cpssp, col, 1);   \
                  b = video_col_get(cpssp, col, 2);   \
            } else {                            \
                  r = ((col >> 10) & 0x1f) << 3;            \
                  g = ((col >>  5) & 0x1f) << 3;            \
                  b = ((col >>  0) & 0x1f) << 3;            \
            }
#include "chip_cirrus_gd5446_gen.c"
#undef GEN_RGB
#undef GEN_NAME

/* trm 9.3.2.7 */
#define GEN_NAME xga
#define GEN_RGB uint16_t col = video_readw(cpssp, cpssp->offset + 2*x); \
            r = ((col >> 11) & 0x1f) << 3; \
              g = ((col >>  5) & 0x3f) << 2; \
            b = ((col >>  0) & 0x1f) << 3
#include "chip_cirrus_gd5446_gen.c"
#undef GEN_RGB
#undef GEN_NAME

/* trm 9.3.2.10 */
#define GEN_NAME 888_mode
#define GEN_RGB b = video_readb(cpssp, cpssp->offset + 3*x + 0); \
              g = video_readb(cpssp, cpssp->offset + 3*x + 1); \
            r = video_readb(cpssp, cpssp->offset + 3*x + 2)
#include "chip_cirrus_gd5446_gen.c"
#undef GEN_RGB
#undef GEN_NAME

/* trm 9.3.2.11 */
#define GEN_NAME 8888_alpha_mode
#define GEN_RGB uint32_t col = video_readl(cpssp, cpssp->offset + 4*x); \
            b = (uint8_t) ((col >>  0) & 0xff); \
              g = (uint8_t) ((col >>  8) & 0xff); \
              r = (uint8_t) ((col >> 16) & 0xff)
#include "chip_cirrus_gd5446_gen.c"
#undef GEN_RGB
#undef GEN_NAME


void
cirrus_gen(struct cpssp *cpssp, struct sig_vga *video)
{
      unsigned int width, height;
      int pixel_format;
      unsigned long start_offset;
      unsigned int pitch;
      static int standingby = 0;

      _cirrus_get_resolution(cpssp, &width, &height);

      if ((cpssp->ext_power_management & 0x18) == 0x18) {
            /* STAND BY state */
#ifdef CIRRUS_DEBUG_GEN
            faum_log(FAUM_LOG_DEBUG, __FUNCTION__, "", "STAND BY\n");
#endif
            if (!standingby) {
                  sig_video_no_sync(cpssp->port_vga->video, cpssp);
                  standingby = 1;
            }
            return;
      } else {
            standingby = 0;
      }

      pixel_format = _cirrus_get_pixel_format(cpssp);
      start_offset = _cirrus_get_display_start_offset(cpssp);
      pitch = _cirrus_get_display_pitch(cpssp);

      switch (pixel_format) {
      case VGA_COMPATIBILITY:
            _cirrus_gen_vga_compatibility(cpssp, video, width, height,
                                    start_offset, pitch);
            break;
      case EIGHT_BPP_GRAYSCALE:
            _cirrus_gen_8bpp_grayscale(cpssp, video, width, height,
                                    start_offset, pitch);
            break;
      case EIGHT_BPP_DIRECT_COLOR:
            _cirrus_gen_8bpp_direct_color(cpssp, video, width, height,
                                    start_offset, pitch);
            break;
      case FIFTEEN_BPP_MODE:
            _cirrus_gen_555_mode(cpssp, video, width, height,
                              start_offset, pitch);
            break;
      case FIFTEEN_BPP_MIX_MODE:
            _cirrus_gen_555_mix_mode(cpssp, video, width, height,
                              start_offset, pitch);
            break;
      case XGA:
            _cirrus_gen_xga(cpssp, video, width, height,
                              start_offset, pitch);
            break;
      case TWENTYFOUR_BPP_MODE:
            _cirrus_gen_888_mode(cpssp, video, width, height,
                              start_offset, pitch);
            break;
      case THIRTYTWO_BPP_ALPHA_MODE:
            _cirrus_gen_8888_alpha_mode(cpssp, video, width, height,
                              start_offset, pitch);
            break;
      default:
            faum_log(FAUM_LOG_WARNING, __FUNCTION__, "",
                   "Unknown pixel format, cannot do video.\n");
            break;
      }
}

static void
do_video(void *_cpssp)
{
      struct cpssp *cpssp = (struct cpssp *) _cpssp;

      if (cpssp->state_power) {
            switch (cpssp->mode) {
            case CIRRUS:
                  /* CIRRUS Mode */
                  cirrus_gen(cpssp, cpssp->port_vga);
                  break;
            case VGA:
                  /* VGA Mode */
                  vga_gen(cpssp, cpssp->port_vga->video);
                  break;
            default:
                  assert(0); /* Mustn't happen. */
            }
      } else {
            /* Power Off */
            sig_video_no_sync(cpssp->port_vga->video, cpssp);
      }

      time_call_after(TIME_HZ / ((REFRESH_CYCLES + 1) * VSYNC), do_video, cpssp);
}

/*************************************************************************/
/*                                                                       */
/* Map ressources and connect to busses                                  */
/*                                                                       */
/*************************************************************************/

void
chip_cirrus_gd5446_init(
      unsigned int nr,
      struct sig_boolean *port_power,
      struct sig_boolean *port_reset_hash_,
      struct sig_pci_bus_idsel *port_idsel,
      struct sig_pci_bus_main *port_pci_bus,
      struct sig_vga *port_vga,
      struct sig_cs *port_mem0_cs,
      struct sig_cs *port_mem1_cs,
      struct sig_cs *port_mem2_cs,
      struct sig_cs *port_mem3_cs,
      struct sig_cs *port_mem4_cs,
      struct sig_cs *port_mem5_cs,
      struct sig_cs *port_mem6_cs,
      struct sig_cs *port_mem7_cs,
      struct sig_cs *port_rom_cs
)
{
      static const struct sig_boolean_funcs power_funcs = {
            .set = chip_cirrus_gd5446_power_set,
      };
      static const struct sig_boolean_funcs reset_hash__funcs = {
            .set = chip_cirrus_gd5446_n_reset_set,
      };
      static const struct sig_pci_bus_idsel_funcs idsel_funcs = {
            .c0r =            cirrus_vga_c0r,
            .c0w =            cirrus_vga_c0w,
      };
      static const struct sig_pci_bus_main_funcs pci_bus_funcs = {
            .ior =            cirrus_vga_ior,
            .iow =            cirrus_vga_iow,

            .mr =       cirrus_vga_mr,
            .mw =       cirrus_vga_mw,
            .map =            cirrus_vga_map,
      };
      static const struct sig_i2c_bus_funcs ddc_funcs = {
            .data_event = cirrus_ddc_data_event,
            .clk_event = cirrus_ddc_clk_event,
      };
      struct cpssp *cpssp;

      cpssp = shm_map(COMP, nr, sizeof(*cpssp), 0);

      /* initialize vga legacy core */
      vga_init(cpssp);

      /* initialize ddc/i2c bus state (drawn high by bus) */
      cpssp->i2c_ddc_bus.clk = true;
      cpssp->i2c_ddc_bus.data = true;

      /* Out */
      /* Call */
      sig_pci_bus_idsel_connect(port_idsel, cpssp, &idsel_funcs);

      cpssp->port_pci_bus = port_pci_bus;
      sig_pci_bus_main_connect(port_pci_bus, cpssp, &pci_bus_funcs);

      cpssp->port_vga = port_vga;
      sig_i2c_bus_connect_raw(port_vga->ddc, cpssp, &ddc_funcs);

      /* memory chip connections */
      /* (swap memory lines within bank to
       *  ease mapping of linear adresses to
       *  chips)
       */
      /* first bank */
      cpssp->port_mem_cs[0] = port_mem3_cs;
      cpssp->port_mem_cs[1] = port_mem2_cs;
      cpssp->port_mem_cs[2] = port_mem1_cs;
      cpssp->port_mem_cs[3] = port_mem0_cs;
      /* second bank */
      cpssp->port_mem_cs[4] = port_mem7_cs;
      cpssp->port_mem_cs[5] = port_mem6_cs;
      cpssp->port_mem_cs[6] = port_mem5_cs;
      cpssp->port_mem_cs[7] = port_mem4_cs;

      cpssp->port_rom_cs = port_rom_cs;

      /* In */
      cpssp->state_power = 0;
      sig_boolean_connect_in(port_power, cpssp, &power_funcs);

      sig_boolean_connect_in(port_reset_hash_, cpssp, &reset_hash__funcs);

      time_call_after(TIME_HZ / ((REFRESH_CYCLES + 1) * VSYNC), do_video, cpssp);
}

unsigned int
chip_cirrus_gd5446_create(void)
{
      static unsigned int nr = 0;
      struct cpssp *cpssp;

      shm_create(COMP, nr, sizeof(*cpssp));
      cpssp = shm_map(COMP, nr, sizeof(*cpssp), 0);

      vga_create("vga_core", nr); /* only does shm_create("vga_core") */

      shm_unmap(cpssp, sizeof(*cpssp));

      return nr++;
}

void
chip_cirrus_gd5446_destroy(unsigned int nr)
{
      struct cpssp *cpssp;

      cpssp = shm_map(COMP, nr, sizeof(*cpssp), 0);

      vga_destroy("vga_core", nr); /* does unmap and destroy "vga_core" */

      shm_unmap(cpssp, sizeof(*cpssp));
      shm_destroy(COMP, nr);
}

Generated by  Doxygen 1.6.0   Back to index