/*
* UAE - The Un*x Amiga Emulator
*
* Cirrus Logic based graphics board emulation
*
* Copyright 2013 Toni Wilen
*
*/

#define VRAMLOG 0
#define MEMLOGR 0
#define MEMLOGW 0
#define MEMLOGINDIRECT 0
#define MEMDEBUG 0
#define MEMDEBUGMASK 0x7fffff
#define MEMDEBUGTEST 0x1ff000
#define PICASSOIV_DEBUG_IO 0

#if MEMLOGR
static bool memlogr = false;
static bool memlogw = false;
#endif

#define BYTESWAP_WORD -1
#define BYTESWAP_LONG 1

#include "sysconfig.h"
#include "sysdeps.h"

#include "options.h"
#include "uae.h"
#include "uae/memory.h"
#include "debug.h"
#include "custom.h"
#include "newcpu.h"
#include "picasso96.h"
#include "statusline.h"
#include "rommgr.h"
#include "zfile.h"
#include "gfxboard.h"
#include "rommgr.h"

#include "qemuvga/qemuuaeglue.h"
#include "qemuvga/vga.h"

#define MONITOR_SWITCH_DELAY 25

#define GFXBOARD_AUTOCONFIG_SIZE 131072

#define BOARD_REGISTERS_SIZE 0x00010000

#define BOARD_MANUFACTURER_PICASSO 2167
#define BOARD_MODEL_MEMORY_PICASSOII 11
#define BOARD_MODEL_REGISTERS_PICASSOII 12

#define BOARD_MODEL_MEMORY_PICASSOIV 24
#define BOARD_MODEL_REGISTERS_PICASSOIV 23
#define PICASSOIV_REG  0x00600000
#define PICASSOIV_IO   0x00200000
#define PICASSOIV_VRAM1 0x01000000
#define PICASSOIV_VRAM2 0x00800000
#define PICASSOIV_ROM_OFFSET 0x0200
#define PICASSOIV_FLASH_OFFSET 0x8000
#define PICASSOIV_FLASH_BANK 0x8000
#define PICASSOIV_MAX_FLASH (GFXBOARD_AUTOCONFIG_SIZE - 32768)

#define PICASSOIV_BANK_UNMAPFLASH 2
#define PICASSOIV_BANK_MAPRAM 4
#define PICASSOIV_BANK_FLASHBANK 128

#define PICASSOIV_INT_VBLANK 128

#define BOARD_MANUFACTURER_PICCOLO 2195
#define BOARD_MODEL_MEMORY_PICCOLO 5
#define BOARD_MODEL_REGISTERS_PICCOLO 6
#define BOARD_MODEL_MEMORY_PICCOLO64 10
#define BOARD_MODEL_REGISTERS_PICCOLO64 11

#define BOARD_MANUFACTURER_SPECTRUM 2193
#define BOARD_MODEL_MEMORY_SPECTRUM 1
#define BOARD_MODEL_REGISTERS_SPECTRUM 2

struct gfxboard
{
	const TCHAR *name;
	int manufacturer;
	int model_memory;
	int model_registers;
	int serial;
	int vrammin;
	int vrammax;
	int banksize;
	int chiptype;
	bool z3;
	int irq;
	bool swap;
};

#define PICASSOIV_Z2 10
#define PICASSOIV_Z3 11

#define ISP4() (currprefs.rtgmem_type == PICASSOIV_Z2 || currprefs.rtgmem_type == PICASSOIV_Z3)

// Picasso II: 8* 4x256 (1M) or 16* 4x256 (2M)
// Piccolo: 8* 4x256 + 2* 16x256 (2M)

static struct gfxboard boards[] =
{
	{
		_T("Picasso II"),
		BOARD_MANUFACTURER_PICASSO, BOARD_MODEL_MEMORY_PICASSOII, BOARD_MODEL_REGISTERS_PICASSOII,
		0x00020000, 0x00100000, 0x00200000, 0x00200000, CIRRUS_ID_CLGD5426, false, 0, false
	},
	{
		_T("Picasso II+"),
		BOARD_MANUFACTURER_PICASSO, BOARD_MODEL_MEMORY_PICASSOII, BOARD_MODEL_REGISTERS_PICASSOII,
		0x00100000, 0x00100000, 0x00200000, 0x00200000, CIRRUS_ID_CLGD5428, false, 2, false
	},
	{
		_T("Piccolo Zorro II"),
		BOARD_MANUFACTURER_PICCOLO, BOARD_MODEL_MEMORY_PICCOLO, BOARD_MODEL_REGISTERS_PICCOLO,
		0x00000000, 0x00100000, 0x00200000, 0x00200000, CIRRUS_ID_CLGD5426, false, 6, true
	},
	{
		_T("Piccolo Zorro III"),
		BOARD_MANUFACTURER_PICCOLO, BOARD_MODEL_MEMORY_PICCOLO, BOARD_MODEL_REGISTERS_PICCOLO,
		0x00000000, 0x00100000, 0x00200000, 0x00200000, CIRRUS_ID_CLGD5426, true, 6, true
	},
	{
		_T("Piccolo SD64 Zorro II"),
		BOARD_MANUFACTURER_PICCOLO, BOARD_MODEL_MEMORY_PICCOLO64, BOARD_MODEL_REGISTERS_PICCOLO64,
		0x00000000, 0x00200000, 0x00400000, 0x00400000, CIRRUS_ID_CLGD5434, false, 6, true
	},
	{
		_T("Piccolo SD64 Zorro III"),
		BOARD_MANUFACTURER_PICCOLO, BOARD_MODEL_MEMORY_PICCOLO64, BOARD_MODEL_REGISTERS_PICCOLO64,
		0x00000000, 0x00200000, 0x00400000, 0x04000000, CIRRUS_ID_CLGD5434, true, 6, true
	},
	{
		_T("Spectrum 28/24 Zorro II"),
		BOARD_MANUFACTURER_SPECTRUM, BOARD_MODEL_MEMORY_SPECTRUM, BOARD_MODEL_REGISTERS_SPECTRUM,
		0x00000000, 0x00100000, 0x00200000, 0x00200000, CIRRUS_ID_CLGD5428, false, 6, true
	},
	{
		_T("Spectrum 28/24 Zorro III"),
		BOARD_MANUFACTURER_SPECTRUM, BOARD_MODEL_MEMORY_SPECTRUM, BOARD_MODEL_REGISTERS_SPECTRUM,
		0x00000000, 0x00100000, 0x00200000, 0x00200000, CIRRUS_ID_CLGD5428, true, 6, true
	},
	{
		_T("Picasso IV Zorro II"),
		BOARD_MANUFACTURER_PICASSO, BOARD_MODEL_MEMORY_PICASSOIV, BOARD_MODEL_REGISTERS_PICASSOIV,
		0x00000000, 0x00400000, 0x00400000, 0x00400000, CIRRUS_ID_CLGD5446, false, 2, false
	},
	{
		// REG:00600000 IO:00200000 VRAM:01000000
		_T("Picasso IV Zorro III"),
		BOARD_MANUFACTURER_PICASSO, BOARD_MODEL_MEMORY_PICASSOIV, 0,
		0x00000000, 0x00400000, 0x00400000, 0x04000000, CIRRUS_ID_CLGD5446, true, 2, false
	}
};

static TCHAR memorybankname[40];
static TCHAR wbsmemorybankname[40];
static TCHAR lbsmemorybankname[40];
static TCHAR regbankname[40];

static int configured_mem, configured_regs;
static struct gfxboard *board;
static uae_u8 expamem_lo;
static uae_u8 *automemory;
static uae_u32 banksize_mask;

static uae_u8 picassoiv_bank, picassoiv_flifi;
static uae_u8 p4autoconfig[256];
static struct zfile *p4rom;
static bool p4z2;
static uae_u32 p4_mmiobase;
static uae_u32 p4_special_mask;
static uae_u32 p4_vram_bank[2];

static CirrusVGAState vga;
static uae_u8 *vram, *vramrealstart;
static int vram_start_offset;
static uae_u32 gfxboardmem_start;
static bool monswitch_current, monswitch_new;
static bool monswitch_reset;
static int monswitch_delay;
static int fullrefresh;
static bool modechanged;
static uae_u8 *gfxboard_surface, *fakesurface_surface;
static bool gfxboard_vblank;
static bool gfxboard_intena;
static bool vram_enabled, vram_offset_enabled;
static hwaddr vram_offset[2];
static uae_u8 cirrus_pci[0x44];
static uae_u8 p4_pci[0x44];

static uae_u32 vgaioregionptr, vgavramregionptr, vgabank0regionptr, vgabank1regionptr;

static const MemoryRegionOps *vgaio, *vgaram, *vgalowram, *vgammio;
static MemoryRegion vgaioregion, vgavramregion;

DECLARE_MEMORY_FUNCTIONS(gfxboard);
DECLARE_MEMORY_FUNCTIONS_WITH_SUFFIX(gfxboard, mem);
DECLARE_MEMORY_FUNCTIONS_WITH_SUFFIX(gfxboard, mem_nojit);
DECLARE_MEMORY_FUNCTIONS_WITH_SUFFIX(gfxboard, bsmem);
DECLARE_MEMORY_FUNCTIONS_WITH_SUFFIX(gfxboard, wbsmem);
DECLARE_MEMORY_FUNCTIONS_WITH_SUFFIX(gfxboard, lbsmem);
DECLARE_MEMORY_FUNCTIONS_WITH_SUFFIX(gfxboard, nbsmem);
DECLARE_MEMORY_FUNCTIONS_WITH_SUFFIX(gfxboard, regs);
DECLARE_MEMORY_FUNCTIONS_WITH_SUFFIX(gfxboards, regs);

static addrbank gfxboard_bank_memory = {
	gfxboard_lget_mem, gfxboard_wget_mem, gfxboard_bget_mem,
	gfxboard_lput_mem, gfxboard_wput_mem, gfxboard_bput_mem,
	gfxboard_xlate, gfxboard_check, NULL, NULL, NULL,
	gfxboard_lget_mem, gfxboard_wget_mem, ABFLAG_RAM | ABFLAG_THREADSAFE
};

static addrbank gfxboard_bank_memory_nojit = {
	gfxboard_lget_mem_nojit, gfxboard_wget_mem_nojit, gfxboard_bget_mem_nojit,
	gfxboard_lput_mem_nojit, gfxboard_wput_mem_nojit, gfxboard_bput_mem_nojit,
	gfxboard_xlate, gfxboard_check, NULL, NULL, NULL,
	gfxboard_lget_mem_nojit, gfxboard_wget_mem_nojit, ABFLAG_RAM | ABFLAG_THREADSAFE
};

static addrbank gfxboard_bank_wbsmemory = {
	gfxboard_lget_wbsmem, gfxboard_wget_wbsmem, gfxboard_bget_wbsmem,
	gfxboard_lput_wbsmem, gfxboard_wput_wbsmem, gfxboard_bput_wbsmem,
	gfxboard_xlate, gfxboard_check, NULL, NULL, NULL,
	gfxboard_lget_wbsmem, gfxboard_wget_wbsmem, ABFLAG_RAM | ABFLAG_THREADSAFE
};

static addrbank gfxboard_bank_lbsmemory = {
	gfxboard_lget_lbsmem, gfxboard_wget_lbsmem, gfxboard_bget_lbsmem,
	gfxboard_lput_lbsmem, gfxboard_wput_lbsmem, gfxboard_bput_lbsmem,
	gfxboard_xlate, gfxboard_check, NULL, NULL, NULL,
	gfxboard_lget_lbsmem, gfxboard_wget_lbsmem, ABFLAG_RAM | ABFLAG_THREADSAFE
};

static addrbank gfxboard_bank_nbsmemory = {
	gfxboard_lget_nbsmem, gfxboard_wget_nbsmem, gfxboard_bget_bsmem,
	gfxboard_lput_nbsmem, gfxboard_wput_nbsmem, gfxboard_bput_bsmem,
	gfxboard_xlate, gfxboard_check, NULL, NULL, _T("Picasso IV banked VRAM"),
	gfxboard_lget_nbsmem, gfxboard_wget_nbsmem, ABFLAG_RAM | ABFLAG_THREADSAFE
};

static addrbank gfxboard_bank_registers = {
	gfxboard_lget_regs, gfxboard_wget_regs, gfxboard_bget_regs,
	gfxboard_lput_regs, gfxboard_wput_regs, gfxboard_bput_regs,
	default_xlate, default_check, NULL, NULL, NULL,
	dummy_lgeti, dummy_wgeti, ABFLAG_IO | ABFLAG_SAFE
};

static addrbank gfxboard_bank_special = {
	gfxboards_lget_regs, gfxboards_wget_regs, gfxboards_bget_regs,
	gfxboards_lput_regs, gfxboards_wput_regs, gfxboards_bput_regs,
	default_xlate, default_check, NULL, NULL, _T("Picasso IV MISC"),
	dummy_lgeti, dummy_wgeti, ABFLAG_IO | ABFLAG_SAFE
};

static void init_board (void)
{
	int vramsize = board->vrammax;

	mapped_free(&gfxmem_bank);
	vram_start_offset = 0;
	if (ISP4() && !p4z2) // JIT direct compatibility hack
		vram_start_offset = 0x01000000;
	vramsize += vram_start_offset;
	xfree (fakesurface_surface);
	fakesurface_surface = xmalloc (uae_u8, 4 * 10000);
	vram_offset[0] = vram_offset[1] = 0;
	vram_enabled = true;
	vram_offset_enabled = false;
	gfxmem_bank.label = board->z3 ? _T("z3_gfx") : _T("z2_gfx");
	gfxmem_bank.allocated = vramsize;
	mapped_malloc (&gfxmem_bank);
	vram = gfxmem_bank.baseaddr;
	vramrealstart = vram;
	vram += vram_start_offset;
	gfxmem_bank.baseaddr = vram;
	gfxmem_bank.allocated = currprefs.rtgmem_size;
	vga.vga.vram_size_mb = currprefs.rtgmem_size >> 20;
	vgaioregion.opaque = &vgaioregionptr;
	vgavramregion.opaque = &vgavramregionptr;
	vga.vga.vram.opaque = &vgavramregionptr;
	vga_common_init(&vga.vga);
	cirrus_init_common(&vga, board->chiptype, 0,  NULL, NULL);
	picasso_allocatewritewatch (currprefs.rtgmem_size);
}


bool gfxboard_toggle (int mode)
{
	if (vram == NULL)
		return false;
	if (monswitch_current) {
		monswitch_new = false;
		monswitch_delay = 1;
		picasso_requested_on = 0;
		return true;
	} else {
		int width, height;
		vga.vga.get_resolution (&vga.vga, &width, &height);
		if (width > 16 && height > 16) {
			monswitch_new = true;
			monswitch_delay = 1;
			picasso_requested_on = 1;
			return true;
		}
	}
	return false;
}

static bool gfxboard_setmode (void)
{
	int bpp, width, height;

	bpp = vga.vga.get_bpp (&vga.vga);
	vga.vga.get_resolution (&vga.vga, &width, &height);
	if (bpp == 0)
		bpp = 8;
	if (width <= 16 || height <= 16)
		return false;
	picasso96_state.Width = width;
	picasso96_state.Height = height;
	picasso96_state.BytesPerPixel = bpp / 8;
	picasso96_state.RGBFormat = RGBFB_CLUT;
	write_log (_T("GFXBOARD %dx%dx%d\n"), width, height, bpp);
	gfx_set_picasso_modeinfo (width, height, bpp, RGBFB_NONE);
	fullrefresh = 2;
	return true;
}

static bool gfxboard_checkchanged (void)
{
	int bpp, width, height;
	bpp = vga.vga.get_bpp (&vga.vga);
	vga.vga.get_resolution (&vga.vga, &width, &height);
	if (bpp == 0)
		bpp = 8;
	if (width <= 16 || height <= 16)
		return false;
	if (picasso96_state.Width != width ||
		picasso96_state.Height != height ||
		picasso96_state.BytesPerPixel != bpp / 8)
		return true;
	return false;
}

static DisplaySurface gfxsurface, fakesurface;
DisplaySurface *qemu_console_surface(QemuConsole *con)
{
	if (picasso_on)
		return &gfxsurface;
	modechanged = true;
	return &fakesurface;
}

void qemu_console_resize(QemuConsole *con, int width, int height)
{
}

#if 0
static uae_u8 pal64 (uae_u8 v)
{
	v = (v << 2) | ((v >> 2) & 3);
	return v;
}
#endif

DisplaySurface* qemu_create_displaysurface_from(int width, int height, int bpp,
                                                int linesize, uint8_t *data,
                                                bool byteswap)
{
	modechanged = true;
	return &fakesurface;
}

int surface_bits_per_pixel(DisplaySurface *s)
{
	if (s == &fakesurface)
		return 32;
	return picasso_vidinfo.pixbytes * 8;
}
int surface_bytes_per_pixel(DisplaySurface *s)
{
	if (s == &fakesurface)
		return 4;
	return picasso_vidinfo.pixbytes;
}

int surface_stride(DisplaySurface *s)
{
	if (s == &fakesurface)
		return 0;
	if (gfxboard_surface == NULL)
		gfxboard_surface = gfx_lock_picasso (false, false);
	return picasso_vidinfo.rowbytes;
}
uint8_t *surface_data(DisplaySurface *s)
{
	if (s == &fakesurface)
		return fakesurface_surface;
	if (gfxboard_surface == NULL)
		gfxboard_surface = gfx_lock_picasso (false, false);
	return gfxboard_surface;
}

void gfxboard_refresh (void)
{
	fullrefresh = 2;
}

void gfxboard_vsync_handler (void)
{
#ifdef FSUAE
#ifdef DEBUG_SHOW_SCREEN
	printf("gfxboard_vsync_handler\n");
#endif
#endif
	if (!configured_mem || !configured_regs)
		return;

	if (monswitch_current && (modechanged || gfxboard_checkchanged ())) {
		if (!gfxboard_setmode ()) {
			picasso_requested_on = 0;
			return;
		}
		init_hz_p96 ();
		modechanged = false;
		picasso_requested_on = 1;
		return;
	}

	if (monswitch_new != monswitch_current) {
		if (monswitch_delay > 0)
			monswitch_delay--;
		if (monswitch_delay == 0) {
			if (!monswitch_new)
				picasso_requested_on = 0;
			monswitch_current = monswitch_new;
			write_log (_T("GFXBOARD ACTIVE=%d\n"), monswitch_current);
		}
	} else {
		monswitch_delay = 0;
	}

	if (monswitch_current) {
		picasso_getwritewatch (vram_start_offset);
		if (fullrefresh)
			vga.vga.graphic_mode = -1;
		vga.vga.hw_ops->gfx_update(&vga);
	}

	if (picasso_on) {
		if (currprefs.leds_on_screen & STATUSLINE_RTG) {
			if (gfxboard_surface == NULL) {
				gfxboard_surface = gfx_lock_picasso (false, false);
			}
			if (gfxboard_surface) {
				if (!(currprefs.leds_on_screen & STATUSLINE_TARGET))
					picasso_statusline (gfxboard_surface);
			}
		}
		if (fullrefresh > 0)
			fullrefresh--;
	}

	if (gfxboard_surface)
		gfx_unlock_picasso (true);
	gfxboard_surface = NULL;

	// Vertical Sync End Register, 0x20 = Disable Vertical Interrupt, 0x10 = Clear Vertical Interrupt.
	if (board->irq) {
		if ((!(vga.vga.cr[0x11] & 0x20) && (vga.vga.cr[0x11] & 0x10) && !(vga.vga.gr[0x17] & 4))) {
			if (gfxboard_intena) {
				gfxboard_vblank = true;
				if (board->irq == 2)
					INTREQ (0x8000 | 0x0008);
				else
					INTREQ (0x8000 | 0x2000);
			}
		}
	}

}

double gfxboard_get_vsync (void)
{
	return vblank_hz; // FIXME
}

void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
{
	picasso_invalidate (x, y, w, h);
}

void memory_region_init_alias(MemoryRegion *mr,
                              const char *name,
                              MemoryRegion *orig,
                              hwaddr offset,
                              uint64_t size)
{
	if (!stricmp(name, "vga.bank0")) {
		mr->opaque = &vgabank0regionptr;
	} else if (!stricmp(name, "vga.bank1")) {
		mr->opaque = &vgabank1regionptr;
	}
}

static void jit_reset (void)
{
#ifdef JIT
	if (currprefs.cachesize && (!currprefs.comptrustbyte || !currprefs.comptrustword || !currprefs.comptrustlong)) {
		flush_icache (0, 3);
	}
#endif
}

static void remap_vram (hwaddr offset0, hwaddr offset1, bool enabled)
{
	jit_reset ();
	vram_offset[0] = offset0;
	vram_offset[1] = offset1;
#if VRAMLOG
	if (vram_enabled != enabled)
		write_log (_T("VRAM state=%d\n"), enabled);
	bool was_vram_offset_enabled = vram_offset_enabled;
#endif
	vram_enabled = enabled && (vga.vga.sr[0x07] & 0x01);
#if 0
	vram_enabled = false;
#endif
	// offset==0 and offset1==0x8000: linear vram mapping
	vram_offset_enabled = offset0 != 0 || offset1 != 0x8000;
#if VRAMLOG
	if (vram_offset_enabled || was_vram_offset_enabled)
		write_log (_T("VRAM offset %08x and %08x\n"), offset0, offset1);
#endif
}

void memory_region_set_alias_offset(MemoryRegion *mr,
                                    hwaddr offset)
{
	if (mr->opaque == &vgabank0regionptr) {
		if (offset != vram_offset[0]) {
			//write_log (_T("vgavramregion0 %08x\n"), offset);
			remap_vram (offset, vram_offset[1], vram_enabled);
		}
	} else if (mr->opaque == &vgabank1regionptr) {
		if (offset != vram_offset[1]) {
			//write_log (_T("vgavramregion1 %08x\n"), offset);
			remap_vram (vram_offset[0], offset, vram_enabled);
		}
	} else if (mr->opaque == &vgaioregionptr) {
		write_log (_T("vgaioregion %d\n"), offset);
	} else {
		write_log (_T("unknown region %d\n"), offset);
	}

}
void memory_region_set_enabled(MemoryRegion *mr, bool enabled)
{
	if (mr->opaque == &vgabank0regionptr || mr->opaque == &vgabank1regionptr) {
		if (enabled != vram_enabled)  {
			//write_log (_T("enable vgavramregion %d\n"), enabled);
			remap_vram (vram_offset[0], vram_offset[1], enabled);
		}
	} else if (mr->opaque == &vgaioregionptr) {
		write_log (_T("enable vgaioregion %d\n"), enabled);
	} else {
		write_log (_T("enable unknown region %d\n"), enabled);
	}
}
void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr,
                               hwaddr size, unsigned client)
{
	//write_log (_T("memory_region_reset_dirty %08x %08x\n"), addr, size);
}
bool memory_region_get_dirty(MemoryRegion *mr, hwaddr addr,
                             hwaddr size, unsigned client)
{
	if (mr->opaque != &vgavramregionptr)
		return false;
	//write_log (_T("memory_region_get_dirty %08x %08x\n"), addr, size);
	if (fullrefresh)
		return true;
	return picasso_is_vram_dirty (addr + gfxmem_bank.start, size);
}

static QEMUResetHandler *reset_func;
static void *reset_parm;
void qemu_register_reset(QEMUResetHandler *func, void *opaque)
{
	reset_func = func;
	reset_parm = opaque;
	reset_func (reset_parm);
}

static void p4_pci_check (void)
{
	uaecptr b0, b1;

	b0 = p4_pci[0x10 + 2] << 16;
	b1 = p4_pci[0x14 + 2] << 16;

	p4_vram_bank[0] = b0;
	p4_vram_bank[1] = b1;
#if PICASSOIV_DEBUG_IO
	write_log (_T("%08X %08X\n"), p4_vram_bank[0], p4_vram_bank[1]);
#endif
}

static void reset_pci (void)
{
	cirrus_pci[0] = 0x00;
	cirrus_pci[1] = 0xb8;
	cirrus_pci[2] = 0x10;
	cirrus_pci[3] = 0x13;

	cirrus_pci[4] = 2;
	cirrus_pci[5] = 0;
	cirrus_pci[6] = 0;
	cirrus_pci[7] &= ~(1 | 2 | 32);

	cirrus_pci[8] = 3;
	cirrus_pci[9] = 0;
	cirrus_pci[10] = 0;
	cirrus_pci[11] = 68;

	cirrus_pci[0x10] &= ~1; // B revision
	cirrus_pci[0x13] &= ~1; // memory
}

static void set_monswitch(bool newval)
{
	if (monswitch_new == newval)
		return;
	monswitch_new = newval; 
	monswitch_delay = MONITOR_SWITCH_DELAY;
}

static void picassoiv_checkswitch (void)
{
	if (ISP4()) {
		bool rtg_active = (picassoiv_flifi & 1) == 0 || (vga.vga.cr[0x51] & 8) == 0;
		// do not switch to P4 RTG until monitor switch is set to native at least
		// once after reset.
		if (monswitch_reset && rtg_active)
			return;
		monswitch_reset = false;
		set_monswitch(rtg_active);
	}
}

static void bput_regtest (uaecptr addr, uae_u8 v)
{
	addr += 0x3b0;
	if (addr == 0x3d5) { // CRxx
		if (vga.vga.cr_index == 0x11) {
			if (!(vga.vga.cr[0x11] & 0x10)) {
				gfxboard_vblank = false;
			}
		}
	}
	if (!(vga.vga.sr[0x07] & 0x01) && vram_enabled) {
		remap_vram (vram_offset[0], vram_offset[1], false);
	}
	picassoiv_checkswitch ();
}

static uae_u8 bget_regtest (uaecptr addr, uae_u8 v)
{
	addr += 0x3b0;
	// Input Status 0
	if (addr == 0x3c2) {
		if (gfxboard_vblank) {
			// Disable Vertical Interrupt == 0?
			// Clear Vertical Interrupt == 1
			// GR17 bit 2 = INTR disable
			if (!(vga.vga.cr[0x11] & 0x20) && (vga.vga.cr[0x11] & 0x10) && !(vga.vga.gr[0x17] & 4)) {
				v |= 0x80; // VGA Interrupt Pending
			}
		}
		v |= 0x10; // DAC sensing
	}
	if (addr == 0x3c5) {
		if (vga.vga.sr_index == 8) {
			// TODO: DDC
		}
	}
	return v;
}

void *memory_region_get_ram_ptr(MemoryRegion *mr)
{
	if (mr->opaque == &vgavramregionptr)
		return vram;
	return NULL;
}

void memory_region_init_ram(MemoryRegion *mr,
                            const char *name,
                            uint64_t size)
{
	if (!stricmp (name, "vga.vram")) {
		vgavramregion.opaque = mr->opaque;
	}
}

void memory_region_init_io(MemoryRegion *mr,
                           const MemoryRegionOps *ops,
                           void *opaque,
                           const char *name,
                           uint64_t size)
{
	if (!stricmp (name, "cirrus-io")) {
		vgaio = ops;
		mr->opaque = vgaioregion.opaque;
	} else if (!stricmp (name, "cirrus-linear-io")) {
		vgaram = ops;
	} else if (!stricmp (name, "cirrus-low-memory")) {
		vgalowram = ops;
	} else if (!stricmp (name, "cirrus-mmio")) {
		vgammio = ops;
	}
}

int is_surface_bgr(DisplaySurface *surface)
{
	return board->swap;
}

static uaecptr fixaddr_bs (uaecptr addr, int mask, int *bs)
{
	bool swapped = false;
	addr &= gfxmem_bank.mask;
	if (p4z2) {
		if (addr < 0x200000) {
			addr |= p4_vram_bank[0];
			if (addr >= 0x400000 && addr < 0x600000) {
				*bs = BYTESWAP_WORD;
				swapped = true;
			} else if (addr >= 0x800000 && addr < 0xa00000) {
				*bs = BYTESWAP_LONG;
				swapped = true;
			}
		} else {
			addr |= p4_vram_bank[1];
			if (addr >= 0x600000 && addr < 0x800000) {
				*bs = BYTESWAP_WORD;
				swapped = true;
			} else if (addr >= 0xa00000 && addr < 0xc00000) {
				*bs = BYTESWAP_LONG;
				swapped = true;
			}
		}
	}
#ifdef JIT
	if (mask && (vram_offset_enabled || !vram_enabled || swapped || p4z2))
		special_mem |= mask;
#endif
	if (vram_offset_enabled) {
		if (addr & 0x8000) {
			addr += vram_offset[1] & ~0x8000;
		} else {
			addr += vram_offset[0];
		}
	}
	addr &= gfxmem_bank.mask;
	return addr;
}

STATIC_INLINE uaecptr fixaddr (uaecptr addr, int mask)
{
#ifdef JIT
	if (mask && (vram_offset_enabled || !vram_enabled))
		special_mem |= mask;
#endif
	if (vram_offset_enabled) {
		if (addr & 0x8000) {
			addr += vram_offset[1] & ~0x8000;
		} else {
			addr += vram_offset[0];
		}
	}
	addr &= gfxmem_bank.mask;
	return addr;
}

STATIC_INLINE uaecptr fixaddr (uaecptr addr)
{
	if (vram_offset_enabled) {
		if (addr & 0x8000) {
			addr += vram_offset[1] & ~0x8000;
		} else {
			addr += vram_offset[0];
		}
	}
	addr &= gfxmem_bank.mask;
	return addr;
}

STATIC_INLINE const MemoryRegionOps *getvgabank (uaecptr *paddr)
{
	uaecptr addr = *paddr;
	addr &= gfxmem_bank.mask;
	*paddr = addr;
	return vgaram;
}

static uae_u32 gfxboard_lget_vram (uaecptr addr, int bs)
{
	uae_u32 v;
	if (!vram_enabled) {
		const MemoryRegionOps *bank = getvgabank (&addr);
		addr &= gfxmem_bank.mask;
		if (bs < 0) { // WORD
			v  = bank->read (&vga, addr + 1, 1) << 24;
			v |= bank->read (&vga, addr + 0, 1) << 16;
			v |= bank->read (&vga, addr + 3, 1) <<  8;
			v |= bank->read (&vga, addr + 2, 1) <<  0;
		} else if (bs > 0) { // LONG
			v  = bank->read (&vga, addr + 3, 1) << 24;
			v |= bank->read (&vga, addr + 2, 1) << 16;
			v |= bank->read (&vga, addr + 1, 1) <<  8;
			v |= bank->read (&vga, addr + 0, 1) <<  0;
		} else {
			v  = bank->read (&vga, addr + 0, 1) << 24;
			v |= bank->read (&vga, addr + 1, 1) << 16;
			v |= bank->read (&vga, addr + 2, 1) <<  8;
			v |= bank->read (&vga, addr + 3, 1) <<  0;
		}
	} else {
		uae_u8 *m = vram + addr;
		if (bs < 0) {
			v  = (*((uae_u16*)(m + 0))) << 16;
			v |= (*((uae_u16*)(m + 2))) << 0;
		} else if (bs > 0) {
			v = *((uae_u32*)m);
		} else {
			v = do_get_mem_long ((uae_u32*)m);
		}
	}
#if MEMLOGR
#if MEMLOGINDIRECT
	if (!vram_enabled || vram_offset_enabled)
#endif
	if (memlogr)
	write_log (_T("R %08X L %08X BS=%d EN=%d\n"), addr, v, bs, vram_enabled);
#endif
	return v;
}
static uae_u16 gfxboard_wget_vram (uaecptr addr, int bs)
{
	uae_u32 v;
	if (!vram_enabled) {
		const MemoryRegionOps *bank = getvgabank (&addr);
		if (bs) {
			v  = bank->read (&vga, addr + 0, 1) <<  0;
			v |= bank->read (&vga, addr + 1, 1) <<  8;
		} else {
			v  = bank->read (&vga, addr + 0, 1) <<  8;
			v |= bank->read (&vga, addr + 1, 1) <<  0;
		}
	} else {
		uae_u8 *m = vram + addr;
		if (bs)
			v = *((uae_u16*)m);
		else
			v = do_get_mem_word ((uae_u16*)m);
	}
#if MEMLOGR
#if MEMLOGINDIRECT
	if (!vram_enabled || vram_offset_enabled)
#endif
	if (memlogr)
	write_log (_T("R %08X W %08X BS=%d EN=%d\n"), addr, v, bs, vram_enabled);
#endif
	return v;
}
static uae_u8 gfxboard_bget_vram (uaecptr addr, int bs)
{
	uae_u32 v;
	if (!vram_enabled) {
		const MemoryRegionOps *bank = getvgabank (&addr);
		if (bs)
			v = bank->read (&vga, addr ^ 1, 1);
		else
			v = bank->read (&vga, addr + 0, 1);
	} else {
		if (bs)
			v = vram[addr ^ 1];
		else
			v = vram[addr];
	}
#if MEMLOGR
#if MEMLOGINDIRECT
	if (!vram_enabled || vram_offset_enabled)
#endif
	if (memlogr)
	write_log (_T("R %08X B %08X BS=0 EN=%d\n"), addr, v, vram_enabled);
#endif
	return v;
}

static void gfxboard_lput_vram (uaecptr addr, uae_u32 l, int bs)
{
#if MEMDEBUG
	if ((addr & MEMDEBUGMASK) >= MEMDEBUGTEST && l)
		write_log (_T("%08X L %08X\n"), addr, l);
#endif
#if MEMLOGW
#if MEMLOGINDIRECT
	if (!vram_enabled || vram_offset_enabled)
#endif
	if (memlogw)
	write_log (_T("W %08X L %08X\n"), addr, l);
#endif
	if (!vram_enabled) {
		const MemoryRegionOps *bank = getvgabank (&addr);
		if (bs < 0) { // WORD
			bank->write (&vga, addr + 1, l >> 24, 1);
			bank->write (&vga, addr + 0, (l >> 16) & 0xff, 1);
			bank->write (&vga, addr + 3, (l >> 8) & 0xff, 1);
			bank->write (&vga, addr + 2, (l >> 0) & 0xff, 1);
		} else if (bs > 0) { // LONG
			bank->write (&vga, addr + 3, l >> 24, 1);
			bank->write (&vga, addr + 2, (l >> 16) & 0xff, 1);
			bank->write (&vga, addr + 1, (l >> 8) & 0xff, 1);
			bank->write (&vga, addr + 0, (l >> 0) & 0xff, 1);
		} else {
			bank->write (&vga, addr + 0, l >> 24, 1);
			bank->write (&vga, addr + 1, (l >> 16) & 0xff, 1);
			bank->write (&vga, addr + 2, (l >> 8) & 0xff, 1);
			bank->write (&vga, addr + 3, (l >> 0) & 0xff, 1);
		}
	} else {
		uae_u8 *m = vram + addr;
		if (bs < 0) {
			*((uae_u16*)(m + 0)) = l >> 16;
			*((uae_u16*)(m + 2)) = l >>  0;
		} else if (bs > 0) {
			*((uae_u32*)m) = l;
		} else {
			do_put_mem_long ((uae_u32*) m, l);
		}
	}
}
static void gfxboard_wput_vram (uaecptr addr, uae_u16 w, int bs)
{
#if MEMDEBUG
	if ((addr & MEMDEBUGMASK) >= MEMDEBUGTEST && w)
		write_log (_T("%08X W %04X\n"), addr, w & 0xffff);
#endif
#if MEMLOGW
#if MEMLOGINDIRECT
	if (!vram_enabled || vram_offset_enabled)
#endif
	if (memlogw)
	write_log (_T("W %08X W %04X\n"), addr, w & 0xffff);
#endif
	if (!vram_enabled) {
		const MemoryRegionOps *bank = getvgabank (&addr);
		if (bs) {
			bank->write (&vga, addr + 0, (w >> 0) & 0xff, 1);
			bank->write (&vga, addr + 1, w >> 8, 1);
		} else {
			bank->write (&vga, addr + 0, w >> 8, 1);
			bank->write (&vga, addr + 1, (w >> 0) & 0xff, 1);
		}
	} else {
		uae_u8 *m = vram + addr;
		if (bs)
			*((uae_u16*)m) = w;
		else
			do_put_mem_word ((uae_u16*)m, w);
	}
}
static void gfxboard_bput_vram (uaecptr addr, uae_u8 b, int bs)
{
#if MEMDEBUG
	if ((addr & MEMDEBUGMASK) >= MEMDEBUGTEST && b)
		write_log (_T("%08X B %02X\n"), addr, b & 0xff);
#endif
#if MEMLOGW
#if MEMLOGINDIRECT
	if (!vram_enabled || vram_offset_enabled)
#endif
	if (memlogw)
	write_log (_T("W %08X B %02X\n"), addr, b & 0xff);
#endif
	if (!vram_enabled) {
		const MemoryRegionOps *bank = getvgabank (&addr);
#ifdef JIT
		special_mem |= S_WRITE;
#endif
		if (bs)
			bank->write (&vga, addr ^ 1, b, 1);
		else
			bank->write (&vga, addr, b, 1);
	} else {
		if (bs)
			vram[addr ^ 1] = b;
		else
			vram[addr] = b;
	}
}

// LONG byteswapped VRAM
static uae_u32 REGPARAM2 gfxboard_lget_lbsmem (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return 0;
	return gfxboard_lget_vram (addr, BYTESWAP_LONG);
}
static uae_u32 REGPARAM2 gfxboard_wget_lbsmem (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return 0;
	return gfxboard_wget_vram (addr, BYTESWAP_LONG);
}
static uae_u32 REGPARAM2 gfxboard_bget_lbsmem (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return 0;
	return gfxboard_bget_vram (addr, BYTESWAP_LONG);
}

static void REGPARAM2 gfxboard_lput_lbsmem (uaecptr addr, uae_u32 l)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return;
	gfxboard_lput_vram (addr, l, BYTESWAP_LONG);
}
static void REGPARAM2 gfxboard_wput_lbsmem (uaecptr addr, uae_u32 w)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return;
	gfxboard_wput_vram (addr, w, BYTESWAP_LONG);
}
static void REGPARAM2 gfxboard_bput_lbsmem (uaecptr addr, uae_u32 w)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return;
	gfxboard_bput_vram (addr, w, BYTESWAP_LONG);
}

// WORD byteswapped VRAM
static uae_u32 REGPARAM2 gfxboard_lget_wbsmem (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return 0;
	return gfxboard_lget_vram (addr, BYTESWAP_WORD);
}
static uae_u32 REGPARAM2 gfxboard_wget_wbsmem (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return 0;
	return gfxboard_wget_vram (addr, BYTESWAP_WORD);
}
static uae_u32 REGPARAM2 gfxboard_bget_wbsmem (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return 0;
	return gfxboard_bget_vram (addr, BYTESWAP_WORD);
}

static void REGPARAM2 gfxboard_lput_wbsmem (uaecptr addr, uae_u32 l)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return;
	gfxboard_lput_vram (addr, l, BYTESWAP_WORD);
}
static void REGPARAM2 gfxboard_wput_wbsmem (uaecptr addr, uae_u32 w)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return;
	gfxboard_wput_vram (addr, w, BYTESWAP_WORD);
}
static void REGPARAM2 gfxboard_bput_wbsmem (uaecptr addr, uae_u32 w)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return;
	gfxboard_bput_vram (addr, w, BYTESWAP_WORD);
}

// normal or byteswapped (banked) vram
static uae_u32 REGPARAM2 gfxboard_lget_nbsmem (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	int bs = 0;
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr_bs (addr, 0, &bs);
	if (addr == -1)
		return 0;
//	activate_debugger();
	return gfxboard_lget_vram (addr, bs);
}
static uae_u32 REGPARAM2 gfxboard_wget_nbsmem (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	int bs = 0;
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr_bs (addr, 0, &bs);
	if (addr == -1)
		return 0;
	return gfxboard_wget_vram (addr, bs);
}

static void REGPARAM2 gfxboard_lput_nbsmem (uaecptr addr, uae_u32 l)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	int bs = 0;
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr_bs (addr, 0, &bs);
	if (addr == -1)
		return;
	gfxboard_lput_vram (addr, l, bs);
}
static void REGPARAM2 gfxboard_wput_nbsmem (uaecptr addr, uae_u32 w)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	int bs = 0;
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr_bs (addr, 0, &bs);
	if (addr == -1)
		return;
	gfxboard_wput_vram (addr, w, bs);
}

static uae_u32 REGPARAM2 gfxboard_bget_bsmem (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	int bs = 0;
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, 0);
	if (addr == -1)
		return 0;
	return gfxboard_bget_vram (addr, bs);
}
static void REGPARAM2 gfxboard_bput_bsmem (uaecptr addr, uae_u32 b)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	int bs = 0;
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr_bs (addr, 0, &bs);
	if (addr == -1)
		return;
	gfxboard_bput_vram (addr, b, bs);
}

// normal vram
static uae_u32 REGPARAM2 gfxboard_lget_mem (uaecptr addr)
{
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, S_READ);
	if (addr == -1)
		return 0;
	return gfxboard_lget_vram (addr, 0);
}
static uae_u32 REGPARAM2 gfxboard_wget_mem (uaecptr addr)
{
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, S_READ);
	if (addr == -1)
		return 0;
	return gfxboard_wget_vram (addr, 0);
}
static uae_u32 REGPARAM2 gfxboard_bget_mem (uaecptr addr)
{
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, S_READ);
	if (addr == -1)
		return 0;
	return gfxboard_bget_vram (addr, 0);
}
static void REGPARAM2 gfxboard_lput_mem (uaecptr addr, uae_u32 l)
{
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, S_WRITE);
	if (addr == -1)
		return;
	gfxboard_lput_vram (addr, l, 0);
}
static void REGPARAM2 gfxboard_wput_mem (uaecptr addr, uae_u32 w)
{
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, S_WRITE);
	if (addr == -1)
		return;
	gfxboard_wput_vram (addr, w, 0);
}
static void REGPARAM2 gfxboard_bput_mem (uaecptr addr, uae_u32 b)
{
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr, S_WRITE);
	if (addr == -1)
		return;
	gfxboard_bput_vram (addr, b, 0);
}

// normal vram, no jit direct
static uae_u32 REGPARAM2 gfxboard_lget_mem_nojit (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr);
	if (addr == -1)
		return 0;
	return gfxboard_lget_vram (addr, 0);
}
static uae_u32 REGPARAM2 gfxboard_wget_mem_nojit (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr);
	if (addr == -1)
		return 0;
	return gfxboard_wget_vram (addr, 0);
}
static uae_u32 REGPARAM2 gfxboard_bget_mem_nojit (uaecptr addr)
{
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr);
	if (addr == -1)
		return 0;
	return gfxboard_bget_vram (addr, 0);
}
static void REGPARAM2 gfxboard_lput_mem_nojit (uaecptr addr, uae_u32 l)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr);
	if (addr == -1)
		return;
	gfxboard_lput_vram (addr, l, 0);
}
static void REGPARAM2 gfxboard_wput_mem_nojit (uaecptr addr, uae_u32 w)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr);
	if (addr == -1)
		return;
	gfxboard_wput_vram (addr, w, 0);
}
static void REGPARAM2 gfxboard_bput_mem_nojit (uaecptr addr, uae_u32 b)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr = fixaddr (addr);
	if (addr == -1)
		return;
	gfxboard_bput_vram (addr, b, 0);
}

static int REGPARAM2 gfxboard_check (uaecptr addr, uae_u32 size)
{
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr &= gfxmem_bank.mask;
	return (addr + size) <= currprefs.rtgmem_size;
}
static uae_u8 *REGPARAM2 gfxboard_xlate (uaecptr addr)
{
	addr -= gfxboardmem_start & gfxmem_bank.mask;
	addr &= gfxmem_bank.mask;
	return vram + addr;
}

static uae_u32 REGPARAM2 gfxboard_bget_mem_autoconfig (uaecptr addr)
{
	uae_u32 v = 0;
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr &= 65535;
	if (addr < GFXBOARD_AUTOCONFIG_SIZE)
		v = automemory[addr];
	return v;
}

static void REGPARAM2 gfxboard_wput_mem_autoconfig (uaecptr addr, uae_u32 b)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	if (!board->z3)
		return;
	b &= 0xffff;
	addr &= 65535;
	if (addr == 0x44) {
		uae_u32 start;
		if (!expamem_z3hack(&currprefs)) {
			start = (b & 0xff00) | expamem_lo;
			gfxmem_bank.start = start << 16;
		}
		gfxboard_bank_memory.bget = gfxboard_bget_mem;
		gfxboard_bank_memory.bput = gfxboard_bput_mem;
		gfxboard_bank_memory.wput = gfxboard_wput_mem;
		init_board ();
		if (ISP4()) {
			// main vram
			map_banks (&gfxboard_bank_memory, (gfxmem_bank.start + PICASSOIV_VRAM1) >> 16, 0x400000 >> 16, currprefs.rtgmem_size);
			map_banks (&gfxboard_bank_wbsmemory, (gfxmem_bank.start + PICASSOIV_VRAM1 + 0x400000) >> 16, 0x400000 >> 16, currprefs.rtgmem_size);
			// secondary
			map_banks (&gfxboard_bank_memory_nojit, (gfxmem_bank.start + PICASSOIV_VRAM2) >> 16, 0x400000 >> 16, currprefs.rtgmem_size);
			map_banks (&gfxboard_bank_wbsmemory, (gfxmem_bank.start + PICASSOIV_VRAM2 + 0x400000) >> 16, 0x400000 >> 16, currprefs.rtgmem_size);
			// regs
			map_banks (&gfxboard_bank_registers, (gfxmem_bank.start + PICASSOIV_REG) >> 16, 0x200000 >> 16, BOARD_REGISTERS_SIZE);
			map_banks (&gfxboard_bank_special, gfxmem_bank.start >> 16, PICASSOIV_REG >> 16, PICASSOIV_REG);
			picassoiv_bank = 0;
			picassoiv_flifi = 1;
			configured_regs = gfxmem_bank.start >> 16;
		} else {
			map_banks (&gfxboard_bank_memory, gfxmem_bank.start >> 16, board->banksize >> 16, currprefs.rtgmem_size);
		}
		configured_mem = gfxmem_bank.start >> 16;
		gfxboardmem_start = gfxmem_bank.start;
		expamem_next (&gfxboard_bank_memory, NULL);
		return;
	}
	if (addr == 0x4c) {
		configured_mem = 0xff;
		expamem_shutup(&gfxboard_bank_memory);
		return;
	}
}

static void REGPARAM2 gfxboard_bput_mem_autoconfig (uaecptr addr, uae_u32 b)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	b &= 0xff;
	addr &= 65535;
	if (addr == 0x48) {
		if (!board->z3) {
			addrbank *ab;
			if (ISP4()) {
				ab = &gfxboard_bank_nbsmemory;
				if (configured_mem == 0)
					init_board ();
				map_banks (ab, b, 0x00200000 >> 16, 0x00200000);
				if (configured_mem == 0) {
					configured_mem = b;
					gfxboardmem_start = b << 16;
				} else {
					gfxboard_bank_memory.bget = gfxboard_bget_mem;
					gfxboard_bank_memory.bput = gfxboard_bput_mem;
				}
			} else {
				ab = &gfxboard_bank_memory;
				gfxboard_bank_memory.bget = gfxboard_bget_mem;
				gfxboard_bank_memory.bput = gfxboard_bput_mem;
				init_board ();
				map_banks (ab, b, board->banksize >> 16, currprefs.rtgmem_size);
				configured_mem = b;
				gfxboardmem_start = b << 16;
			}
			expamem_next (ab, NULL);
		} else {
			expamem_lo = b & 0xff;
		}
		return;
	}
	if (addr == 0x4c) {
		configured_mem = 0xff;
		expamem_shutup(&gfxboard_bank_memory);
		return;
	}
}

static uaecptr mungeaddr (uaecptr addr, bool write)
{
	addr &= 65535;
	if (addr >= 0x2000) {
		if (addr == 0x46e8) {
			// wakeup register
			return 0;
		}
		write_log (_T("GFXBOARD: %c unknown IO address %x\n"), write ? 'W' : 'R', addr);
		return 0;
	}
	if (addr >= 0x1000) {
		if (board->manufacturer == BOARD_MANUFACTURER_PICASSO) {
			if (addr == 0x1001) {
				gfxboard_intena = true;
				return 0;
			}
			if (addr == 0x1000) {
				gfxboard_intena = false;
				return 0;
			}
		}
		if ((addr & 0xfff) < 0x3b0) {
			write_log (_T("GFXBOARD: %c unknown IO address %x\n"), write ? 'W' : 'R', addr);
			return 0;
		}
		addr++;
	}
	addr &= 0x0fff;
	if (addr == 0x102) {
		// POS102
		return 0;
	}
	if (addr < 0x3b0) {
		write_log (_T("GFXBOARD: %c unknown IO address %x\n"), write ? 'W' : 'R', addr);
		return 0;
	}
	addr -= 0x3b0;
	return addr;
}

static uae_u32 REGPARAM2 gfxboard_lget_regs (uaecptr addr)
{
	uae_u32 v = 0xffffffff;
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr = mungeaddr (addr, false);
	if (addr)
		v = vgaio->read (&vga, addr, 4);
	return v;
}
static uae_u32 REGPARAM2 gfxboard_wget_regs (uaecptr addr)
{
	uae_u16 v = 0xffff;
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr = mungeaddr (addr, false);
	if (addr) {
		uae_u8 v1, v2;
		v1  = vgaio->read (&vga, addr + 0, 1);
		v1 = bget_regtest (addr + 0, v1);
		v2 = vgaio->read (&vga, addr + 1, 1);
		v2 = bget_regtest (addr + 1, v2);
		v = (v1 << 8) | v2;
	}
	return v;
}
static uae_u32 REGPARAM2 gfxboard_bget_regs (uaecptr addr)
{
	uae_u8 v = 0xff;
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr &= 65535;
	if (addr >= 0x8000) {
		write_log (_T("GFX SPECIAL BGET IO %08X\n"), addr);
		return 0;
	}
	addr = mungeaddr (addr, false);
	if (addr) {
		v = vgaio->read (&vga, addr, 1);
		v = bget_regtest (addr, v);
	}
	return v;
}

static void REGPARAM2 gfxboard_lput_regs (uaecptr addr, uae_u32 l)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	//write_log (_T("GFX LONG PUT IO %04X = %04X\n"), addr & 65535, l);
	addr = mungeaddr (addr, true);
	if (addr) {
		vgaio->write (&vga, addr + 0, l >> 24, 1);
		bput_regtest (addr + 0, (l >> 24));
		vgaio->write (&vga, addr + 1, (l >> 16) & 0xff, 1);
		bput_regtest (addr + 0, (l >> 16));
		vgaio->write (&vga, addr + 2, (l >>  8) & 0xff, 1);
		bput_regtest (addr + 0, (l >>  8));
		vgaio->write (&vga, addr + 3, (l >>  0) & 0xff, 1);
		bput_regtest (addr + 0, (l >>  0));
	}
}
static void REGPARAM2 gfxboard_wput_regs (uaecptr addr, uae_u32 w)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	//write_log (_T("GFX WORD PUT IO %04X = %04X\n"), addr & 65535, w & 0xffff);
	addr = mungeaddr (addr, true);
	if (addr) {
		vgaio->write (&vga, addr + 0, (w >> 8) & 0xff, 1);
		bput_regtest (addr + 0, (w >> 8));
		vgaio->write (&vga, addr + 1, (w >> 0) & 0xff, 1);
		bput_regtest (addr + 1, (w >> 0));
	}
}
static void REGPARAM2 gfxboard_bput_regs (uaecptr addr, uae_u32 b)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	//write_log (_T("GFX BYTE PUT IO %04X = %02X\n"), addr & 65535, b & 0xff);
	addr &= 65535;
	if (addr >= 0x8000) {
		write_log (_T("GFX SPECIAL BPUT IO %08X = %02X\n"), addr, b & 0xff);
		switch (board->manufacturer)
		{
		case BOARD_MANUFACTURER_PICASSO:
			{
				if ((addr & 1) == 0) {
					int idx = addr >> 12;
					if (idx == 0x0b || idx == 0x09) {
						set_monswitch(false);
					} else if (idx == 0x0a || idx == 0x08) {
						set_monswitch(true);
					}
				}
			}
		break;
		case BOARD_MANUFACTURER_PICCOLO:
		case BOARD_MANUFACTURER_SPECTRUM:
			set_monswitch((b & 0x20) != 0);
			gfxboard_intena = (b & 0x40) != 0;
		break;
		}
		return;
	}
	addr = mungeaddr (addr, true);
	if (addr) {
		vgaio->write (&vga, addr, b & 0xff, 1);
		bput_regtest (addr, b);
	}
}

static uae_u32 REGPARAM2 gfxboard_bget_regs_autoconfig (uaecptr addr)
{
	uae_u32 v = 0;
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr &= 65535;
	if (addr < GFXBOARD_AUTOCONFIG_SIZE)
		v = automemory[addr];
	return v;
}

static void REGPARAM2 gfxboard_bput_regs_autoconfig (uaecptr addr, uae_u32 b)
{
	addrbank *ab;
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	b &= 0xff;
	addr &= 65535;
	if (addr == 0x48) {
		gfxboard_bank_registers.bget = gfxboard_bget_regs;
		gfxboard_bank_registers.bput = gfxboard_bput_regs;
		if (p4z2) {
			ab = &gfxboard_bank_special;
			map_banks (ab, b, gfxboard_bank_special.allocated >> 16, gfxboard_bank_special.allocated);
		} else {
			ab = &gfxboard_bank_registers;
			map_banks (ab, b, gfxboard_bank_registers.allocated >> 16, gfxboard_bank_registers.allocated);
		}
		configured_regs = b;
		expamem_next (ab, NULL);
		return;
	}
	if (addr == 0x4c) {
		configured_regs = 0xff;
		expamem_next (NULL, NULL);
		return;
	}
}

void gfxboard_free(void)
{
	if (vram) {
		gfxmem_bank.baseaddr = vramrealstart;
		mapped_free (&gfxmem_bank);
	}
	vram = NULL;
	vramrealstart = NULL;
	xfree (fakesurface_surface);
	fakesurface_surface = NULL;
	configured_mem = 0;
	configured_regs = 0;
	monswitch_new = false;
	monswitch_current = false;
	monswitch_delay = -1;
	monswitch_reset = true;
	modechanged = false;
	gfxboard_vblank = false;
	gfxboard_intena = false;
	picassoiv_bank = 0;
}

void gfxboard_reset (void)
{
	if (currprefs.rtgmem_type >= GFXBOARD_HARDWARE) {
		board = &boards[currprefs.rtgmem_type - GFXBOARD_HARDWARE];
		gfxmem_bank.mask = currprefs.rtgmem_size - 1;
	}
	gfxboard_free();
	if (board) {
		if (board->z3)
			gfxboard_bank_memory.wput = gfxboard_wput_mem_autoconfig;
		if (reset_func) 
			reset_func (reset_parm);
	}
}

static uae_u32 REGPARAM2 gfxboards_lget_regs (uaecptr addr)
{
	uae_u32 v = 0;
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr &= p4_special_mask;
	// pci config
	if (addr >= 0x400000 || (p4z2 && !(picassoiv_bank & PICASSOIV_BANK_MAPRAM) && (picassoiv_bank & PICASSOIV_BANK_UNMAPFLASH) && ((addr >= 0x800 && addr < 0xc00) || (addr >= 0x1000 && addr < 0x2000)))) {
		uae_u32 addr2 = addr & 0xffff;
		if (addr2 >= 0x0800 && addr2 < 0x840) {
			addr2 -= 0x800;
			v =  p4_pci[addr2 + 0] << 24;
			v |= p4_pci[addr2 + 1] << 16;
			v |= p4_pci[addr2 + 2] <<  8;
			v |= p4_pci[addr2 + 3] <<  0;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV PCI LGET %08x %08x\n"), addr, v);
#endif
		} else if (addr2 >= 0x1000 && addr2 < 0x1040) {
			addr2 -= 0x1000;
			v =  cirrus_pci[addr2 + 0] << 24;
			v |= cirrus_pci[addr2 + 1] << 16;
			v |= cirrus_pci[addr2 + 2] <<  8;
			v |= cirrus_pci[addr2 + 3] <<  0;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV CL PCI LGET %08x %08x\n"), addr, v);
#endif
		}
		return v;
	}
	if (picassoiv_bank & PICASSOIV_BANK_MAPRAM) {
		// memory mapped io
		if (addr >= p4_mmiobase && addr < p4_mmiobase + 0x8000) {
			uae_u32 addr2 = addr - p4_mmiobase;
			v  = vgammio->read(&vga, addr2 + 0, 1) << 24;
			v |= vgammio->read(&vga, addr2 + 1, 1) << 16;
			v |= vgammio->read(&vga, addr2 + 2, 1) <<  8;
			v |= vgammio->read(&vga, addr2 + 3, 1) <<  0;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV MMIO LGET %08x %08x\n"), addr, v);
#endif
			return v;
		}
	}
#if PICASSOIV_DEBUG_IO
	write_log (_T("PicassoIV LGET %08x %08x\n"), addr, v);
#endif
	return v;
}
static uae_u32 REGPARAM2 gfxboards_wget_regs (uaecptr addr)
{
	uae_u16 v = 0;
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr &= p4_special_mask;
	// pci config
	if (addr >= 0x400000 || (p4z2 && !(picassoiv_bank & PICASSOIV_BANK_MAPRAM) && (picassoiv_bank & PICASSOIV_BANK_UNMAPFLASH) && ((addr >= 0x800 && addr < 0xc00) || (addr >= 0x1000 && addr < 0x2000)))) {
		uae_u32 addr2 = addr & 0xffff;
		if (addr2 >= 0x0800 && addr2 < 0x840) {
			addr2 -= 0x800;
			v =  p4_pci[addr2 + 0] << 8;
			v |= p4_pci[addr2 + 1] << 0;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV PCI WGET %08x %04x\n"), addr, v);
#endif
		} else if (addr2 >= 0x1000 && addr2 < 0x1040) {
			addr2 -= 0x1000;
			v =  cirrus_pci[addr2 + 0] << 8;
			v |= cirrus_pci[addr2 + 1] << 0;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV CL PCI WGET %08x %04x\n"), addr, v);
#endif
		}
		return v;
	}
	if (picassoiv_bank & PICASSOIV_BANK_MAPRAM) {
		// memory mapped io
		if (addr >= p4_mmiobase && addr < p4_mmiobase + 0x8000) {
			uae_u32 addr2 = addr - p4_mmiobase;
			v  = vgammio->read(&vga, addr2 + 0, 1) << 8;
			v |= vgammio->read(&vga, addr2 + 1, 1) << 0;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV MMIO WGET %08x %04x\n"), addr, v & 0xffff);
#endif
			return v;
		}
	}
#if PICASSOIV_DEBUG_IO
	write_log (_T("PicassoIV WGET %04x %04x\n"), addr, v);
#endif
	return v;
}
static uae_u32 REGPARAM2 gfxboards_bget_regs (uaecptr addr)
{
	uae_u8 v = 0xff;
#ifdef JIT
	special_mem |= S_READ;
#endif
	addr &= p4_special_mask;

	// pci config
	if (addr >= 0x400000 || (p4z2 && !(picassoiv_bank & PICASSOIV_BANK_MAPRAM) && (picassoiv_bank & PICASSOIV_BANK_UNMAPFLASH) && ((addr >= 0x800 && addr < 0xc00) || (addr >= 0x1000 && addr < 0x2000)))) {
		uae_u32 addr2 = addr & 0xffff;
		v = 0;
		if (addr2 >= 0x0800 && addr2 < 0x840) {
			addr2 -= 0x800;
			v =  p4_pci[addr2];
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV PCI BGET %08x %02x\n"), addr, v);
#endif
		} else if (addr2 >= 0x800 && addr2 <= 0x1000) {
			if (addr2 == 0x802)
				v = 2; // ???
			if (addr2 == 0x808)
				v = 4; // bridge revision
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV PCI BGET %08x %02x\n"), addr, v);
#endif
		} else if (addr2 >= 0x1000 && addr2 <= 0x1040) {
			addr2 -= 0x1000;
			v = cirrus_pci[addr2];
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV CL PCI BGET %08x %02x\n"), addr, v);
#endif
		}
		return v;
	}

	if (picassoiv_bank & PICASSOIV_BANK_MAPRAM) {
		// memory mapped io
		if (addr >= p4_mmiobase && addr < p4_mmiobase + 0x8000) {
			uae_u32 addr2 = addr - p4_mmiobase;
			v = vgammio->read(&vga, addr2, 1);
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV MMIO BGET %08x %02x\n"), addr, v & 0xff);
#endif
			return v;
		}
	}
	if (addr == 0) {
		v = picassoiv_bank;
		return v;
	}
	if (picassoiv_bank & PICASSOIV_BANK_UNMAPFLASH) {
		v = 0;
		if (addr == 0x404) {
			v = 0x70; // FLIFI revision
			// FLIFI type in use
			if (currprefs.chipset_mask & CSMASK_AGA)
				v |= 4 | 8;
			else
				v |= 8;
		} else if (addr == 0x408) {
			v = gfxboard_vblank ? 0x80 : 0;
		} else if (p4z2 && addr >= 0x10000) {
			addr -= 0x10000;
			uaecptr addr2 = mungeaddr (addr, true);
			if (addr2) {
				v = vgaio->read (&vga, addr2, 1);
				v = bget_regtest (addr2, v);
			}
			//write_log (_T("PicassoIV IO %08x %02x\n"), addr, v);
			return v;
		}
#if PICASSOIV_DEBUG_IO
		if (addr != 0x408)
			write_log (_T("PicassoIV BGET %08x %02x\n"), addr, v);
#endif
	} else {
		if (addr < PICASSOIV_FLASH_OFFSET) {
			v = automemory[addr];
			return v;
		}
		addr -= PICASSOIV_FLASH_OFFSET;
		addr /= 2;
		v = automemory[addr + PICASSOIV_FLASH_OFFSET + ((picassoiv_bank & PICASSOIV_BANK_FLASHBANK) ? 0x8000 : 0)];
	}
	return v;
}
static void REGPARAM2 gfxboards_lput_regs (uaecptr addr, uae_u32 l)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr &= p4_special_mask;
	if (addr >= 0x400000 || (p4z2 && !(picassoiv_bank & PICASSOIV_BANK_MAPRAM) && (picassoiv_bank & PICASSOIV_BANK_UNMAPFLASH) && ((addr >= 0x800 && addr < 0xc00) || (addr >= 0x1000 && addr < 0x2000)))) {
		uae_u32 addr2 = addr & 0xffff;
		if (addr2 >= 0x0800 && addr2 < 0x840) {
			addr2 -= 0x800;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV PCI LPUT %08x %08x\n"), addr, l);
#endif
			p4_pci[addr2 + 0] = l >> 24;
			p4_pci[addr2 + 1] = l >> 16;
			p4_pci[addr2 + 2] = l >>  8;
			p4_pci[addr2 + 3] = l >>  0;
			p4_pci_check ();
		} else if (addr2 >= 0x1000 && addr2 < 0x1040) {
			addr2 -= 0x1000;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV CL PCI LPUT %08x %08x\n"), addr, l);
#endif
			cirrus_pci[addr2 + 0] = l >> 24;
			cirrus_pci[addr2 + 1] = l >> 16;
			cirrus_pci[addr2 + 2] = l >>  8;
			cirrus_pci[addr2 + 3] = l >>  0;
			reset_pci ();
		}
		return;
	}
	if (picassoiv_bank & PICASSOIV_BANK_MAPRAM) {
		// memory mapped io
		if (addr >= p4_mmiobase && addr < p4_mmiobase + 0x8000) {
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV MMIO LPUT %08x %08x\n"), addr, l);
#endif
			uae_u32 addr2 = addr - p4_mmiobase;
			vgammio->write(&vga, addr2 + 0, l >> 24, 1);
			vgammio->write(&vga, addr2 + 1, l >> 16, 1);
			vgammio->write(&vga, addr2 + 2, l >>  8, 1);
			vgammio->write(&vga, addr2 + 3, l >>  0, 1);
			return;
		}
	}
#if PICASSOIV_DEBUG_IO
	write_log (_T("PicassoIV LPUT %08x %08x\n"), addr, l);
#endif
}
static void REGPARAM2 gfxboards_wput_regs (uaecptr addr, uae_u32 v)
{
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	uae_u16 w = (uae_u16)v;
	addr &= p4_special_mask;
	if (addr >= 0x400000 || (p4z2 && !(picassoiv_bank & PICASSOIV_BANK_MAPRAM) && (picassoiv_bank & PICASSOIV_BANK_UNMAPFLASH) && ((addr >= 0x800 && addr < 0xc00) || (addr >= 0x1000 && addr < 0x2000)))) {
		uae_u32 addr2 = addr & 0xffff;
		if (addr2 >= 0x0800 && addr2 < 0x840) {
			addr2 -= 0x800;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV PCI WPUT %08x %04x\n"), addr, w & 0xffff);
#endif
			p4_pci[addr2 + 0] = w >> 8;
			p4_pci[addr2 + 1] = w >> 0;
			p4_pci_check ();
		} else if (addr2 >= 0x1000 && addr2 < 0x1040) {
			addr2 -= 0x1000;
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV CL PCI WPUT %08x %04x\n"), addr, w & 0xffff);
#endif
			cirrus_pci[addr2 + 0] = w >> 8;
			cirrus_pci[addr2 + 1] = w >> 0;
			reset_pci ();
		}
		return;
	}
	if (picassoiv_bank & PICASSOIV_BANK_MAPRAM) {
		// memory mapped io
		if (addr >= p4_mmiobase && addr < p4_mmiobase + 0x8000) {
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV MMIO LPUT %08x %08x\n"), addr, w & 0xffff);
#endif
			uae_u32 addr2 = addr - p4_mmiobase;
			vgammio->write(&vga, addr2 + 0, w >> 8, 1);
			vgammio->write(&vga, addr2 + 1, (w >> 0) & 0xff, 1);
			return;
		}
	}
	if (p4z2 && addr >= 0x10000) {
		addr -= 0x10000;
		addr = mungeaddr (addr, true);
		if (addr) {
			vgaio->write (&vga, addr + 0, w >> 8, 1);
			bput_regtest (addr + 0, w >> 8);
			vgaio->write (&vga, addr + 1, (w >> 0) & 0xff, 1);
			bput_regtest (addr + 1, w >> 0);
		}
		return;
	}
#if PICASSOIV_DEBUG_IO
	write_log (_T("PicassoIV WPUT %08x %04x\n"), addr, w & 0xffff);
#endif
}

static void REGPARAM2 gfxboards_bput_regs (uaecptr addr, uae_u32 v)
{
	uae_u8 b = (uae_u8)v;
#ifdef JIT
	special_mem |= S_WRITE;
#endif
	addr &= p4_special_mask;
	if (addr >= 0x400000 || (p4z2 && !(picassoiv_bank & PICASSOIV_BANK_MAPRAM) && (picassoiv_bank & PICASSOIV_BANK_UNMAPFLASH) && ((addr >= 0x800 && addr < 0xc00) || (addr >= 0x1000 && addr < 0x2000)))) {
		uae_u32 addr2 = addr & 0xffff;
		if (addr2 >= 0x0800 && addr2 < 0x840) {
			addr2 -= 0x800;
			p4_pci[addr2] = b;
			p4_pci_check ();
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV PCI BPUT %08x %02x\n"), addr, b & 0xff);
#endif
		} else if (addr2 >= 0x1000 && addr2 < 0x1040) {
			addr2 -= 0x1000;
			cirrus_pci[addr2] = b;
			reset_pci ();
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV CL PCI BPUT %08x %02x\n"), addr, b & 0xff);
#endif
		}
		return;
	}
	if (picassoiv_bank & PICASSOIV_BANK_UNMAPFLASH) {
		if (addr == 0x404) {
			picassoiv_flifi = b;
			picassoiv_checkswitch ();
		}
	}
	if (picassoiv_bank & PICASSOIV_BANK_MAPRAM) {
		// memory mapped io
		if (addr >= p4_mmiobase && addr < p4_mmiobase + 0x8000) {
#if PICASSOIV_DEBUG_IO
			write_log (_T("PicassoIV MMIO BPUT %08x %08x\n"), addr, b & 0xff);
#endif
			uae_u32 addr2 = addr - p4_mmiobase;
			vgammio->write(&vga, addr2, b, 1);
			return;
		}
	}
	if (p4z2 && addr >= 0x10000) {
		addr -= 0x10000;
		addr = mungeaddr (addr, true);
		if (addr) {
			vgaio->write (&vga, addr, b & 0xff, 1);
			bput_regtest (addr, b);
		}
		return;
	}
#if PICASSOIV_DEBUG_IO
	write_log (_T("PicassoIV BPUT %08x %02X\n"), addr, b & 0xff);
#endif
	if (addr == 0) {
		picassoiv_bank = b;
	}
}

bool gfxboard_is_z3 (int type)
{
	if (type == GFXBOARD_UAE_Z2)
		return false;
	if (type == GFXBOARD_UAE_Z3)
		return true;
	board = &boards[type - GFXBOARD_HARDWARE];
	return board->z3;
}

bool gfxboard_need_byteswap (int type)
{
	if (type < GFXBOARD_HARDWARE)
		return false;
	board = &boards[type - GFXBOARD_HARDWARE];
	return board->swap;
}

int gfxboard_get_autoconfig_size(int type)
{
	if (type == PICASSOIV_Z3)
		return 32 * 1024 * 1024;
	return -1;
}

int gfxboard_get_vram_min (int type)
{
	if (type < GFXBOARD_HARDWARE)
		return -1;
	board = &boards[type - GFXBOARD_HARDWARE];
	//return board->vrammax;
	return board->vrammin;
}

int gfxboard_get_vram_max (int type)
{
	if (type < GFXBOARD_HARDWARE)
		return -1;
	board = &boards[type - GFXBOARD_HARDWARE];
	return board->vrammax;
}

bool gfxboard_is_registers (int type)
{
	if (type < 2)
		return false;
	board = &boards[type - 2];
	return board->model_registers != 0;
}

int gfxboard_num_boards (int type)
{
	if (type < 2)
		return 1;
	board = &boards[type - 2];
	if (type == PICASSOIV_Z2)
		return 3;
	if (board->model_registers == 0)
		return 1;
	return 2;
}


static void gfxboard_init (void)
{
	if (!automemory)
		automemory = xmalloc (uae_u8, GFXBOARD_AUTOCONFIG_SIZE);
	memset (automemory, 0xff, GFXBOARD_AUTOCONFIG_SIZE);
	p4z2 = false;
	zfile_fclose (p4rom);
	p4rom = NULL;
	banksize_mask = board->banksize - 1;
	memset (cirrus_pci, 0, sizeof cirrus_pci);
	reset_pci ();
}

static void copyp4autoconfig (int startoffset)
{
	int size = 0;
	int offset = 0;
	memset (automemory, 0xff, 64);
	while (size < 32) {
		uae_u8 b = p4autoconfig[size + startoffset];
		automemory[offset] = b;
		offset += 2;
		size++;
	}
}

static void loadp4rom (void)
{
	int size, offset;
	uae_u8 b;
	// rom loader code
	zfile_fseek (p4rom, 256, SEEK_SET);
	offset = PICASSOIV_ROM_OFFSET;
	size = 0;
	while (size < 4096 - 256) {
		if (!zfile_fread (&b, 1, 1, p4rom))
			break;
		automemory[offset] = b;
		offset += 2;
		size++;
	}
	// main flash code
	zfile_fseek (p4rom, 16384, SEEK_SET);
	zfile_fread (&automemory[PICASSOIV_FLASH_OFFSET], 1, PICASSOIV_MAX_FLASH, p4rom);
	zfile_fclose (p4rom);
	p4rom = NULL;
	write_log (_T("PICASSOIV: flash rom loaded\n"));
}

static void ew (int addr, uae_u32 value)
{
	addr &= 0xffff;
	if (addr == 00 || addr == 02 || addr == 0x40 || addr == 0x42) {
		automemory[addr] = (value & 0xf0);
		automemory[addr + 2] = (value & 0x0f) << 4;
	} else {
		automemory[addr] = ~(value & 0xf0);
		automemory[addr + 2] = ~((value & 0x0f) << 4);
	}
}

addrbank *gfxboard_init_memory (int devnum)
{
	int bank;
	uae_u8 z2_flags, z3_flags, type;

	gfxboard_init ();

	memset (automemory, 0xff, GFXBOARD_AUTOCONFIG_SIZE);
	
	z2_flags = 0x05; // 1M
	z3_flags = 0x06; // 1M
	bank = board->banksize;
	bank /= 0x00100000;
	while (bank > 1) {
		z2_flags++;
		z3_flags++;
		bank >>= 1;
	}
	if (board->z3) {
		type = 0x00 | 0x08 | 0x80; // 16M Z3
		ew (0x08, z3_flags | 0x10 | 0x20);
	} else {
		type = z2_flags | 0x08 | 0xc0;
	}
	ew (0x04, board->model_memory);
	ew (0x10, board->manufacturer >> 8);
	ew (0x14, board->manufacturer);

	uae_u32 ser = board->serial;
	ew (0x18, ser >> 24); /* ser.no. Byte 0 */
	ew (0x1c, ser >> 16); /* ser.no. Byte 1 */
	ew (0x20, ser >>  8); /* ser.no. Byte 2 */
	ew (0x24, ser >>  0); /* ser.no. Byte 3 */

	ew (0x00, type);

	if (ISP4()) {
		int roms[] = { 91, -1 };
		struct romlist *rl = getromlistbyids (roms, NULL);
		TCHAR path[MAX_DPATH];
		fetch_rompath (path, sizeof path / sizeof (TCHAR));

		p4rom = NULL;
		if (currprefs.picassoivromfile[0])
			p4rom = read_rom_name(currprefs.picassoivromfile);

		if (!p4rom && rl) {
			p4rom = read_rom (rl->rd);
		}
		if (!p4rom) {
			_tcscat (path, _T("picasso_iv_flash.rom"));
			p4rom = read_rom_name (path);
			if (!p4rom)
				p4rom = read_rom_name (_T("picasso_iv_flash.rom"));
		}
		if (p4rom) {
			zfile_fread (p4autoconfig, sizeof p4autoconfig, 1, p4rom);
			copyp4autoconfig (board->z3 ? 192 : 0);
			if (board->z3) {
				loadp4rom ();
				p4_mmiobase = 0x200000;
				p4_special_mask = 0x7fffff;
			} else {
				p4z2 = true;
				p4_mmiobase = 0x8000;
				p4_special_mask = 0x1ffff;
			}
			gfxboard_intena = true;
		} else {
			error_log (_T("Picasso IV: '%s' flash rom image not found!\nAvailable from http://www.sophisticated-development.de/\nPIV_FlashImageXX -> picasso_iv_flash.rom"), path);
			gui_message (_T("Picasso IV: '%s' flash rom image not found!\nAvailable from http://www.sophisticated-development.de/\nPIV_FlashImageXX -> picasso_iv_flash.rom"), path);
		}
	}

	_stprintf (memorybankname, _T("%s VRAM"), board->name);
	_stprintf (wbsmemorybankname, _T("%s VRAM WORDSWAP"), board->name);
	_stprintf (lbsmemorybankname, _T("%s VRAM LONGSWAP"), board->name);
	_stprintf (regbankname, _T("%s REG"), board->name);

	gfxboard_bank_memory.name = memorybankname;
	gfxboard_bank_memory_nojit.name = memorybankname;
	gfxboard_bank_wbsmemory.name = wbsmemorybankname;
	gfxboard_bank_lbsmemory.name = lbsmemorybankname;
	gfxboard_bank_registers.name = regbankname;

	gfxboard_bank_memory.bget = gfxboard_bget_mem_autoconfig;
	gfxboard_bank_memory.bput = gfxboard_bput_mem_autoconfig;

	return &gfxboard_bank_memory;
}

addrbank *gfxboard_init_memory_p4_z2 (int devnum)
{
	if (board->z3)
		return &expamem_null;

	copyp4autoconfig (64);
	return &gfxboard_bank_memory;
}

addrbank *gfxboard_init_registers (int devnum)
{
	if (!board->model_registers)
		return &expamem_null;

	memset (automemory, 0xff, GFXBOARD_AUTOCONFIG_SIZE);
	ew (0x00, 0xc0 | 0x01); // 64k Z2
	ew (0x04, board->model_registers);
	ew (0x10, board->manufacturer >> 8);
	ew (0x14, board->manufacturer);

	uae_u32 ser = board->serial;
	ew (0x18, ser >> 24); /* ser.no. Byte 0 */
	ew (0x1c, ser >> 16); /* ser.no. Byte 1 */
	ew (0x20, ser >>  8); /* ser.no. Byte 2 */
	ew (0x24, ser >>  0); /* ser.no. Byte 3 */

	gfxboard_bank_registers.allocated = BOARD_REGISTERS_SIZE;

	if (ISP4()) {
		uae_u8 v;
		copyp4autoconfig (128);
		loadp4rom ();
		v = (((automemory[0] & 0xf0) | (automemory[2] >> 4)) & 3) - 1;
		gfxboard_bank_special.allocated = 0x10000 << v;
	}

	gfxboard_bank_registers.bget = gfxboard_bget_regs_autoconfig;
	gfxboard_bank_registers.bput = gfxboard_bput_regs_autoconfig;

	return &gfxboard_bank_registers;
}
