/*
 * This file is part of SIS.
 * 
 * SIS, SPARC instruction simulator V2.5 Copyright (C) 1995 Jiri Gaisler,
 * European Space Agency
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 675
 * Mass Ave, Cambridge, MA 02139, USA.
 * 
 */

/* The control space devices */

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <unistd.h>
#include "sis.h"
#include "end.h"
#include "sim-config.h"

extern int      ctrl_c;
extern int32    sis_verbose;
extern int32    sparclite, sparclite_board;
extern int      rom8,wrp,uben;
extern char     uart_dev1[], uart_dev2[];

int dumbio = 0; /* normal, smart, terminal oriented IO by default */

/* MEC registers */
#define MEC_START 	0x01f80000
#define MEC_END 	0x01f80100

/* Memory exception waitstates */
#define MEM_EX_WS 	1

/* ERC32 always adds one waitstate during RAM std */
#define STD_WS 1

#ifdef ERRINJ
extern int errmec;
#endif

/* The target's byte order is big-endian by default until we load a
   little-endian program.  */

int	current_target_byte_order = BIG_ENDIAN;

#define MEC_WS	0		/* Waitstates per MEC access (0 ws) */
#define MOK	0

/* MEC register addresses */

#define MEC_MCR		0x000
#define MEC_SFR  	0x004
#define MEC_PWDR  	0x008
#define MEC_MEMCFG	0x010
#define MEC_IOCR	0x014
#define MEC_WCR		0x018

#define MEC_MAR0  	0x020
#define MEC_MAR1  	0x024

#define MEC_SSA1 	0x020
#define MEC_SEA1 	0x024
#define MEC_SSA2 	0x028
#define MEC_SEA2 	0x02C
#define MEC_ISR		0x044
#define MEC_IPR		0x048
#define MEC_IMR 	0x04C
#define MEC_ICR 	0x050
#define MEC_IFR 	0x054
#define MEC_WDOG  	0x060
#define MEC_TRAPD  	0x064
#define MEC_RTC_COUNTER	0x080
#define MEC_RTC_RELOAD	0x080
#define MEC_RTC_SCALER	0x084
#define MEC_GPT_COUNTER	0x088
#define MEC_GPT_RELOAD	0x088
#define MEC_GPT_SCALER	0x08C
#define MEC_TIMER_CTRL	0x098
#define MEC_SFSR	0x0A0
#define MEC_FFAR	0x0A4
#define MEC_ERSR	0x0B0
#define MEC_DBG		0x0C0
#define MEC_TCR		0x0D0

#define MEC_BRK		0x0C4
#define MEC_WPR		0x0C8

#define MEC_UARTA	0x0E0
#define MEC_UARTB	0x0E4
#define MEC_UART_CTRL	0x0E8
#define SIM_LOAD	0x0F0

/* Memory exception causes */
#define PROT_EXC	0x3
#define UIMP_ACC	0x4
#define MEC_ACC		0x6
#define WATCH_EXC	0xa
#define BREAK_EXC	0xb

/* Size of UART buffers (bytes) */
#define UARTBUF	1024

/* Number of simulator ticks between flushing the UARTS. 	 */
/* For good performance, keep above 1000			 */
#define UART_FLUSH_TIME	  3000

/* MEC timer control register bits */
#define TCR_GACR 1
#define TCR_GACL 2
#define TCR_GASE 4
#define TCR_GASL 8
#define TCR_TCRCR 0x100
#define TCR_TCRCL 0x200
#define TCR_TCRSE 0x400
#define TCR_TCRSL 0x800

/* New uart defines */
#define UART_TX_TIME	1000
#define UART_RX_TIME	1000
#define UARTA_DR	0x1
#define UARTA_SRE	0x2
#define UARTA_HRE	0x4
#define UARTA_OR	0x40
#define UARTA_CLR	0x80
#define UARTB_DR	0x10000
#define UARTB_SRE	0x20000
#define UARTB_HRE	0x40000
#define UARTB_OR	0x400000
#define UARTB_CLR	0x800000

#define UART_DR		0x100
#define UART_TSE	0x200
#define UART_THE	0x400

/* MEC registers */

static char     fname[256];
static int32    find = 0;
static uint32   mec_ssa[2];	/* Write protection start address */
static uint32   mec_sea[2];	/* Write protection end address */
static uint32   mec_wpr[2];	/* Write protection control fields */
static uint32   mec_sfsr;
static uint32   mec_ffar;
static uint32   mec_ipr;
static uint32   mec_imr;
static uint32   mec_isr;
static uint32   mec_icr;
static uint32   mec_ifr;
static uint32   mec_mcr;	/* MEC control register */
static uint32   mec_memcfg;	/* Memory control register */
static uint32   mec_wcr;	/* MEC waitstate register */
static uint32   mec_iocr;	/* MEC IO control register */
static uint32   posted_irq;
static uint32   mec_ersr;	/* MEC error and status register */
static uint32   mec_tcr;	/* MEC test comtrol register */

static uint32   rtc_counter;
static uint32   rtc_reload;
static uint32   rtc_scaler;
static uint32   rtc_scaler_start;
static uint32   rtc_enabled;
static uint32   rtc_cr;
static uint32   rtc_se;

static uint32   gpt_counter;
static uint32   gpt_reload;
static uint32   gpt_scaler;
static uint32   gpt_scaler_start;
static uint32   gpt_enabled;
static uint32   gpt_cr;
static uint32   gpt_se;

static uint32   wdog_scaler;
static uint32   wdog_counter;
static uint32   wdog_rst_delay;
static uint32   wdog_rston;

enum wdog_type {
    init, disabled, enabled, stopped
};

static enum wdog_type wdog_status;


/* ROM size 1024 Kbyte */
#define ROM_SZ	 	0x100000
#define ROM_MASK 	0x0fffff

/* RAM size 4 Mbyte */
#define RAM_START 	0x02000000
#define RAM_END 	0x02400000
#define RAM_MASK 	0x003fffff

/* SPARClite boards all seem to have RAM at the same place. */
#define RAM_START_SLITE	0x40000000
#define RAM_END_SLITE 	0x40400000
#define RAM_MASK_SLITE 	0x003fffff

/* Memory support variables */

static uint32   mem_ramr_ws;	/* RAM read waitstates */
static uint32   mem_ramw_ws;	/* RAM write waitstates */
static uint32   mem_romr_ws;	/* ROM read waitstates */
static uint32   mem_romw_ws;	/* ROM write waitstates */
static uint32	mem_ramstart;	/* RAM start */
static uint32	mem_ramend;	/* RAM end */
static uint32	mem_rammask;	/* RAM address mask */
static uint32   mem_ramsz;	/* RAM size */
static uint32   mem_romsz;	/* ROM size */
static uint32   mem_accprot;	/* RAM write protection enabled */
static uint32   mem_blockprot;	/* RAM block write protection enabled */

static unsigned char	romb[ROM_SZ];
static unsigned char	ramb[RAM_END - RAM_START];


/* UART support variables */

static int32    fd1, fd2;	/* file descriptor for input file */
static int32    Ucontrol;	/* UART status register */
static unsigned char aq[UARTBUF], bq[UARTBUF];
static int32    anum, aind = 0;
static int32    bnum, bind = 0;
static char     wbufa[UARTBUF], wbufb[UARTBUF];
static unsigned wnuma;
static unsigned wnumb;
static FILE    *f1in, *f1out, *f2in, *f2out;
static struct termios ioc1, ioc2, iocold1, iocold2;
static int      f1open = 0, f2open = 0;

static char     uarta_sreg, uarta_hreg, uartb_sreg, uartb_hreg;
static uint32   uart_stat_reg;
static uint32   uarta_data, uartb_data;

#ifdef ERA
int era = 0;
int erareg;
#endif

/* Forward declarations */

static void	decode_ersr PARAMS ((void));
#ifdef ERRINJ
static void	iucomperr PARAMS ((void));
#endif
static void	mecparerror PARAMS ((void));
static void	decode_memcfg PARAMS ((void));
static void	decode_wcr PARAMS ((void));
static void	decode_mcr PARAMS ((void));
static void	close_port PARAMS ((void));
static void	mec_reset PARAMS ((void));
static void	mec_intack PARAMS ((int32 level));
static void	chk_irq PARAMS ((void));
static void	mec_irq PARAMS ((int32 level));
static void	set_sfsr PARAMS ((uint32 fault, uint32 addr,
				  uint32 asi, uint32 read));
static int32	mec_read PARAMS ((uint32 addr, uint32 asi, uint32 *data));
static int	mec_write PARAMS ((uint32 addr, uint32 data));
static void	port_init PARAMS ((void));
static uint32	read_uart PARAMS ((uint32 addr));
static void	write_uart PARAMS ((uint32 addr, uint32 data));
static void	flush_uart PARAMS ((void));
static void	uarta_tx PARAMS ((void));
static void	uartb_tx PARAMS ((void));
static void	uart_rx PARAMS ((caddr_t arg));
static void	uart_intr PARAMS ((caddr_t arg));
static void	uart_irq_start PARAMS ((void));
static void	wdog_intr PARAMS ((caddr_t arg));
static void	wdog_start PARAMS ((void));
static void	rtc_intr PARAMS ((caddr_t arg));
static void	rtc_start PARAMS ((void));
static uint32	rtc_counter_read PARAMS ((void));
static void	rtc_scaler_set PARAMS ((uint32 val));
static void	rtc_reload_set PARAMS ((uint32 val));
static void	gpt_intr PARAMS ((caddr_t arg));
static void	gpt_start PARAMS ((void));
static uint32	gpt_counter_read PARAMS ((void));
static void	gpt_scaler_set PARAMS ((uint32 val));
static void	gpt_reload_set PARAMS ((uint32 val));
static void	timer_ctrl PARAMS ((uint32 val));
static unsigned char *
		get_mem_ptr PARAMS ((uint32 addr, uint32 size));

static void	fetch_bytes PARAMS ((int asi, unsigned char *mem,
				     uint32 *data, int sz));

static void	store_bytes PARAMS ((unsigned char *mem, uint32 *data, int sz));

extern int	ext_irl;


/* One-time init */

void
init_sim()
{
    port_init();
}

/* Power-on reset init */

void
reset()
{
    mec_reset();
    uart_irq_start();
    wdog_start();
}

static void
decode_ersr()
{
    if (mec_ersr & 0x01) {
	if (!(mec_mcr & 0x20)) {
	    if (mec_mcr & 0x40) {
	        sys_reset();
	        mec_ersr = 0x8000;
	        if (sis_verbose)
	            printf("Error manager reset - IU in error mode\n");
	    } else {
	        sys_halt();
	        mec_ersr |= 0x2000;
	        if (sis_verbose)
	            printf("Error manager halt - IU in error mode\n");
	    }
	} else
	    mec_irq(1);
    }
    if (mec_ersr & 0x04) {
	if (!(mec_mcr & 0x200)) {
	    if (mec_mcr & 0x400) {
	        sys_reset();
	        mec_ersr = 0x8000;
	        if (sis_verbose)
	            printf("Error manager reset - IU comparison error\n");
	    } else {
	        sys_halt();
	        mec_ersr |= 0x2000;
	        if (sis_verbose)
	            printf("Error manager halt - IU comparison error\n");
	    }
	} else
	    mec_irq(1);
    }
    if (mec_ersr & 0x20) { 
	if (!(mec_mcr & 0x2000)) {
	    if (mec_mcr & 0x4000) {
	        sys_reset();
	        mec_ersr = 0x8000;
	        if (sis_verbose)
	            printf("Error manager reset - MEC hardware error\n");
	    } else {
	        sys_halt();
	        mec_ersr |= 0x2000;
	        if (sis_verbose)
	            printf("Error manager halt - MEC hardware error\n");
	    }
	} else
	    mec_irq(1);
    }
}

#ifdef ERRINJ
static void
iucomperr()
{
    mec_ersr |= 0x04;
    decode_ersr();
}
#endif

static void
mecparerror()
{
    mec_ersr |= 0x20;
    decode_ersr();
}


/* IU error mode manager */

void
error_mode(pc)
    uint32          pc;
{

    mec_ersr |= 0x1;
    decode_ersr();
}


/* Check memory settings */

static void
decode_memcfg()
{
    if (rom8) mec_memcfg &= ~0x20000;
    else mec_memcfg |= 0x20000;

    mem_ramsz = (256 * 1024) << ((mec_memcfg >> 10) & 7);
    mem_romsz = (128 * 1024) << ((mec_memcfg >> 18) & 7);

    if (sparclite_board) {
	mem_ramstart = RAM_START_SLITE;
	mem_ramend = RAM_END_SLITE;
	mem_rammask = RAM_MASK_SLITE;
    }
    else {
	mem_ramstart = RAM_START;
	mem_ramend = RAM_END;
	mem_rammask = RAM_MASK;
    }
    if (sis_verbose)
	printf("RAM start: 0x%x, RAM size: %d K, ROM size: %d K\n",
	       mem_ramstart, mem_ramsz >> 10, mem_romsz >> 10);
}

static void
decode_wcr()
{
    mem_ramr_ws = mec_wcr & 3;
    mem_ramw_ws = (mec_wcr >> 2) & 3;
    mem_romr_ws = (mec_wcr >> 4) & 0x0f;
    if (rom8) {
    	if (mem_romr_ws > 0 )  mem_romr_ws--;
	mem_romr_ws = 5 + (4*mem_romr_ws);
    }
    mem_romw_ws = (mec_wcr >> 8) & 0x0f;
    if (sis_verbose)
	printf("Waitstates = RAM read: %d, RAM write: %d, ROM read: %d, ROM write: %d\n",
	       mem_ramr_ws, mem_ramw_ws, mem_romr_ws, mem_romw_ws);
}

static void
decode_mcr()
{
    mem_accprot = (mec_wpr[0] | mec_wpr[1]);
    mem_blockprot = (mec_mcr >> 3) & 1;
    if (sis_verbose && mem_accprot)
	printf("Memory block write protection enabled\n");
    if (mec_mcr & 0x08000) {
	mec_ersr |= 0x20;
	decode_ersr();
    }
    if (sis_verbose && (mec_mcr & 2))
	printf("Software reset enabled\n");
    if (sis_verbose && (mec_mcr & 1))
	printf("Power-down mode enabled\n");
}

/* Flush ports when simulator stops */

void
sim_halt()
{
#ifdef FAST_UART
    flush_uart();
#endif
}

int
sim_stop(SIM_DESC sd)
{
  ctrl_c = 1;
  return 1;
}

static void
close_port()
{
    if (f1open && f1in != stdin)
	fclose(f1in);
    if (f2open && f2in != stdin)
	fclose(f2in);
}

void
exit_sim()
{
    close_port();
}

static void
mec_reset()
{
    int             i;

    find = 0;
    for (i = 0; i < 2; i++)
	mec_ssa[i] = mec_sea[i] = mec_wpr[i] = 0;
    mec_mcr = 0x01350014;
    mec_iocr = 0;
    mec_sfsr = 0x078;
    mec_ffar = 0;
    mec_ipr = 0;
    mec_imr = 0x7ffe;
    mec_isr = 0;
    mec_icr = 0;
    mec_ifr = 0;
    mec_memcfg = 0x10000;
    mec_wcr = -1;
    mec_ersr = 0;		/* MEC error and status register */
    mec_tcr = 0;		/* MEC test comtrol register */

    decode_memcfg();
    decode_wcr();
    decode_mcr();

    posted_irq = 0;
    wnuma = wnumb = 0;
    anum = aind = bnum = bind = 0;

    uart_stat_reg = UARTA_SRE | UARTA_HRE | UARTB_SRE | UARTB_HRE;
    uarta_data = uartb_data = UART_THE | UART_TSE;

    rtc_counter = 0xffffffff;
    rtc_reload = 0xffffffff;
    rtc_scaler = 0xff;
    rtc_enabled = 0;
    rtc_cr = 0;
    rtc_se = 0;

    gpt_counter = 0xffffffff;
    gpt_reload = 0xffffffff;
    gpt_scaler = 0xffff;
    gpt_enabled = 0;
    gpt_cr = 0;
    gpt_se = 0;

    wdog_scaler = 255;
    wdog_rst_delay = 255;
    wdog_counter = 0xffff;
    wdog_rston = 0;
    wdog_status = init;

#ifdef ERA
    erareg = 0;
#endif

}



static void
mec_intack(level)
    int32           level;
{
    int             irq_test;

    if (sis_verbose)
	printf("interrupt %d acknowledged\n", level);
    irq_test = mec_tcr & 0x80000;
    if ((irq_test) && (mec_ifr & (1 << level)))
	mec_ifr &= ~(1 << level);
    else
	mec_ipr &= ~(1 << level);
   chk_irq();
}

static void
chk_irq()
{
    int32           i;
    uint32          itmp;
    int		    old_irl;

    old_irl = ext_irl;
    if (mec_tcr & 0x80000) itmp = mec_ifr;
    else itmp  = 0;
    itmp = ((mec_ipr | itmp) & ~mec_imr) & 0x0fffe;
    ext_irl = 0;
    if (itmp != 0) {
	for (i = 15; i > 0; i--) {
	    if (((itmp >> i) & 1) != 0) {
		if ((sis_verbose) && (i > old_irl)) 
		    printf("IU irl: %d\n", i);
		ext_irl = i;
	        set_int(i, mec_intack, i);
		break;
	    }
	}
    }
}

static void
mec_irq(level)
    int32           level;
{
    mec_ipr |= (1 << level);
    chk_irq();
}

static void
set_sfsr(fault, addr, asi, read)
    uint32          fault;
    uint32          addr;
    uint32          asi;
    uint32          read;
{
    if ((asi == 0xa) || (asi == 0xb)) {
	mec_ffar = addr;
	mec_sfsr = (fault << 3) | (!read << 15);
	mec_sfsr |= ((mec_sfsr & 1) ^ 1) | (mec_sfsr & 1);
	switch (asi) {
	case 0xa:
	    mec_sfsr |= 0x0004;
	    break;
	case 0xb:
	    mec_sfsr |= 0x1004;
	    break;
	}
    }
}

static int32
mec_read(addr, asi, data)
    uint32          addr;
    uint32          asi;
    uint32         *data;
{

    switch (addr & 0x0ff) {

    case MEC_MCR:		/* 0x00 */
	*data = mec_mcr;
	break;

    case MEC_MEMCFG:		/* 0x10 */
	*data = mec_memcfg;
	break;

    case MEC_IOCR:
	*data = mec_iocr;	/* 0x14 */
	break;

    case MEC_SSA1:		/* 0x20 */
	*data = mec_ssa[0] | (mec_wpr[0] << 23);
	break;
    case MEC_SEA1:		/* 0x24 */
	*data = mec_sea[0];
	break;
    case MEC_SSA2:		/* 0x28 */
	*data = mec_ssa[1] | (mec_wpr[1] << 23);
	break;
    case MEC_SEA2:		/* 0x2c */
	*data = mec_sea[1];
	break;

    case MEC_ISR:		/* 0x44 */
	*data = mec_isr;
	break;

    case MEC_IPR:		/* 0x48 */
	*data = mec_ipr;
	break;

    case MEC_IMR:		/* 0x4c */
	*data = mec_imr;
	break;

    case MEC_IFR:		/* 0x54 */
	*data = mec_ifr;
	break;

    case MEC_RTC_COUNTER:	/* 0x80 */
	*data = rtc_counter_read();
	break;
    case MEC_RTC_SCALER:	/* 0x84 */
	if (rtc_enabled)
	    *data = rtc_scaler - (now() - rtc_scaler_start);
	else
	    *data = rtc_scaler;
	break;

    case MEC_GPT_COUNTER:	/* 0x88 */
	*data = gpt_counter_read();
	break;

    case MEC_GPT_SCALER:	/* 0x8c */
	if (rtc_enabled)
	    *data = gpt_scaler - (now() - gpt_scaler_start);
	else
	    *data = gpt_scaler;
	break;


    case MEC_SFSR:		/* 0xA0 */
	*data = mec_sfsr;
	break;

    case MEC_FFAR:		/* 0xA4 */
	*data = mec_ffar;
	break;

    case SIM_LOAD:
	fname[find] = 0;
	if (find == 0)
	    strcpy(fname, "simload");
	find = bfd_load(fname);
 	if (find == -1) 
	    *data = 0;
	else
	    *data = 1;
	find = 0;
	break;

    case MEC_ERSR:		/* 0xB0 */
	*data = mec_ersr;
	break;

    case MEC_TCR:		/* 0xD0 */
	*data = mec_tcr;
	break;

    case MEC_UARTA:		/* 0xE0 */
    case MEC_UARTB:		/* 0xE4 */
	if (asi != 0xb) {
	    set_sfsr(MEC_ACC, addr, asi, 1);
	    return (1);
	}
	*data = read_uart(addr);
	break;

    case MEC_UART_CTRL:		/* 0xE8 */

	*data = read_uart(addr);
	break;

    default:
	set_sfsr(MEC_ACC, addr, asi, 1);
	return (1);
	break;
    }
    return (MOK);
}

static int
mec_write(addr, data)
    uint32          addr;
    uint32          data;
{
    if (sis_verbose > 1)
	printf("MEC write a: %08x, d: %08x\n",addr,data);
    switch (addr & 0x0ff) {

    case MEC_MCR:
	mec_mcr = data;
	decode_mcr();
        if (mec_mcr & 0x08000) mecparerror();
	break;

    case MEC_SFR:
	if (mec_mcr & 0x2) {
	    sys_reset();
	    mec_ersr = 0x4000;
    	    if (sis_verbose)
	    	printf(" Software reset issued\n");
	}
	break;

    case MEC_IOCR:
	mec_iocr = data;
        if (mec_iocr & 0xC0C0C0C0) mecparerror();
	break;

    case MEC_SSA1:		/* 0x20 */
        if (data & 0xFE000000) mecparerror();
	mec_ssa[0] = data & 0x7fffff;
	mec_wpr[0] = (data >> 23) & 0x03;
	mem_accprot = mec_wpr[0] || mec_wpr[1];
	if (sis_verbose && mec_wpr[0])
	    printf("Segment 1 memory protection enabled (0x02%06x - 0x02%06x)\n",
		   mec_ssa[0] << 2, mec_sea[0] << 2);
	break;
    case MEC_SEA1:		/* 0x24 */
        if (data & 0xFF800000) mecparerror();
	mec_sea[0] = data & 0x7fffff;
	break;
    case MEC_SSA2:		/* 0x28 */
        if (data & 0xFE000000) mecparerror();
	mec_ssa[1] = data & 0x7fffff;
	mec_wpr[1] = (data >> 23) & 0x03;
	mem_accprot = mec_wpr[0] || mec_wpr[1];
	if (sis_verbose && mec_wpr[1])
	    printf("Segment 2 memory protection enabled (0x02%06x - 0x02%06x)\n",
		   mec_ssa[1] << 2, mec_sea[1] << 2);
	break;
    case MEC_SEA2:		/* 0x2c */
        if (data & 0xFF800000) mecparerror();
	mec_sea[1] = data & 0x7fffff;
	break;

    case MEC_UARTA:
    case MEC_UARTB:
        if (data & 0xFFFFFF00) mecparerror();
    case MEC_UART_CTRL:
        if (data & 0xFF00FF00) mecparerror();
	write_uart(addr, data);
	break;

    case MEC_GPT_RELOAD:
	gpt_reload_set(data);
	break;

    case MEC_GPT_SCALER:
        if (data & 0xFFFF0000) mecparerror();
	gpt_scaler_set(data);
	break;

    case MEC_TIMER_CTRL:
        if (data & 0xFFFFF0F0) mecparerror();
	timer_ctrl(data);
	break;

    case MEC_RTC_RELOAD:
	rtc_reload_set(data);
	break;

    case MEC_RTC_SCALER:
        if (data & 0xFFFFFF00) mecparerror();
	rtc_scaler_set(data);
	break;

    case MEC_SFSR:		/* 0xA0 */
        if (data & 0xFFFF0880) mecparerror();
	mec_sfsr = 0x78;
	break;

    case MEC_ISR:
        if (data & 0xFFFFE000) mecparerror();
	mec_isr = data;
	break;

    case MEC_IMR:		/* 0x4c */

        if (data & 0xFFFF8001) mecparerror();
	mec_imr = data & 0x7ffe;
	chk_irq();
	break;

    case MEC_ICR:		/* 0x50 */

        if (data & 0xFFFF0001) mecparerror();
	mec_ipr &= ~data & 0x0fffe;
	chk_irq();
	break;

    case MEC_IFR:		/* 0x54 */

        if (mec_tcr & 0x080000) {
            if (data & 0xFFFF0001) mecparerror();
	    mec_ifr = data & 0xfffe;
	    chk_irq();
	}
	break;
    case SIM_LOAD:
	fname[find++] = (char) data;
	break;


    case MEC_MEMCFG:		/* 0x10 */
        if (data & 0xC0E08000) mecparerror();
	mec_memcfg = data;
	decode_memcfg();
	if (mec_memcfg & 0xc0e08000)
	    mecparerror();
	break;

    case MEC_WCR:		/* 0x18 */
	mec_wcr = data;
	decode_wcr();
	break;

    case MEC_ERSR:		/* 0xB0 */
	if (mec_tcr & 0x100000)
            if (data & 0xFFFFEFC0) mecparerror();
	    mec_ersr = data & 0x103f;
	break;

    case MEC_TCR:		/* 0xD0 */
        if (data & 0xFFE1FFC0) mecparerror();
	mec_tcr = data & 0x1e003f;
	break;

    case MEC_WDOG:		/* 0x60 */
	wdog_scaler = (data >> 16) & 0x0ff;
	wdog_counter = data & 0x0ffff;
	wdog_rst_delay = data >> 24;
	wdog_rston = 0;
	if (wdog_status == stopped)
	    wdog_start();
	wdog_status = enabled;
	break;

    case MEC_TRAPD:		/* 0x64 */
	if (wdog_status == init) {
	    wdog_status = disabled;
	    if (sis_verbose)
		printf("Watchdog disabled\n");
	}
	break;

    case MEC_PWDR:
	if (mec_mcr & 1)
	    wait_for_irq();
	break;

    default:
	set_sfsr(MEC_ACC, addr, 0xb, 0);
	return (1);
	break;
    }
    return (MOK);
}


/* MEC UARTS */

static int      ifd1 = -1, ifd2 = -1, ofd1 = -1, ofd2 = -1;

void
init_stdio()
{
    if (dumbio)
        return; /* do nothing */
    if (!ifd1)
	tcsetattr(0, TCSANOW, &ioc1);
    if (!ifd2)
	tcsetattr(0, TCSANOW, &ioc2);
}

void
restore_stdio()
{
    if (dumbio)
        return; /* do nothing */
    if (!ifd1)
	tcsetattr(0, TCSANOW, &iocold1);
    if (!ifd2)
	tcsetattr(0, TCSANOW, &iocold2);
}

#define DO_STDIO_READ( _fd_, _buf_, _len_ )          \
             ( dumbio                                \
               ? (0) /* no bytes read, no delay */   \
               : read( _fd_, _buf_, _len_ ) )


static void
port_init()
{

    if (uben) {
    f2in = stdin;
    f1in = NULL;
    f2out = stdout;
    f1out = NULL;
    } else {
    f1in = stdin;
    f2in = NULL;
    f1out = stdout;
    f2out = NULL;
    }
    if (uart_dev1[0] != 0)
	if ((fd1 = open(uart_dev1, O_RDWR | O_NONBLOCK)) < 0) {
	    printf("Warning, couldn't open output device %s\n", uart_dev1);
	} else {
	    if (sis_verbose)
		printf("serial port A on %s\n", uart_dev1);
	    f1in = f1out = fdopen(fd1, "r+");
	    setbuf(f1out, NULL);
	    f1open = 1;
	}
    if (f1in) ifd1 = fileno(f1in);
    if (ifd1 == 0) {
	if (sis_verbose)
	    printf("serial port A on stdin/stdout\n");
        if (!dumbio) {
            tcgetattr(ifd1, &ioc1);
            iocold1 = ioc1;
            ioc1.c_lflag &= ~(ICANON | ECHO);
            ioc1.c_cc[VMIN] = 0;
            ioc1.c_cc[VTIME] = 0;
        }
	f1open = 1;
    }

    if (f1out) {
	ofd1 = fileno(f1out);
    	if (!dumbio && ofd1 == 1) setbuf(f1out, NULL);
    }

    if (uart_dev2[0] != 0)
	if ((fd2 = open(uart_dev2, O_RDWR | O_NONBLOCK)) < 0) {
	    printf("Warning, couldn't open output device %s\n", uart_dev2);
	} else {
	    if (sis_verbose)
		printf("serial port B on %s\n", uart_dev2);
	    f2in = f2out = fdopen(fd2, "r+");
	    setbuf(f2out, NULL);
	    f2open = 1;
	}
    if (f2in)  ifd2 = fileno(f2in);
    if (ifd2 == 0) {
	if (sis_verbose)
	    printf("serial port B on stdin/stdout\n");
        if (!dumbio) {
            tcgetattr(ifd2, &ioc2);
            iocold2 = ioc2;
            ioc2.c_lflag &= ~(ICANON | ECHO);
            ioc2.c_cc[VMIN] = 0;
            ioc2.c_cc[VTIME] = 0;
        }
	f2open = 1;
    }

    if (f2out) {
	ofd2 = fileno(f2out);
        if (!dumbio && ofd2 == 1) setbuf(f2out, NULL);
    }

    wnuma = wnumb = 0;

}

static uint32
read_uart(addr)
    uint32          addr;
{

    unsigned        tmp;

    tmp = 0;
    switch (addr & 0xff) {

    case 0xE0:			/* UART 1 */
#ifndef _WIN32
#ifdef FAST_UART

	if (aind < anum) {
	    if ((aind + 1) < anum)
		mec_irq(4);
	    return (0x700 | (uint32) aq[aind++]);
	} else {
	    if (f1open) {
	        anum = DO_STDIO_READ(ifd1, aq, UARTBUF);
	    }
	    if (anum > 0) {
		aind = 0;
		if ((aind + 1) < anum)
		    mec_irq(4);
		return (0x700 | (uint32) aq[aind++]);
	    } else {
		return (0x600 | (uint32) aq[aind]);
	    }

	}
#else
	tmp = uarta_data;
	uarta_data &= ~UART_DR;
	uart_stat_reg &= ~UARTA_DR;
	return tmp;
#endif
#else
	return(0);
#endif
	break;

    case 0xE4:			/* UART 2 */
#ifndef _WIN32
#ifdef FAST_UART
	if (bind < bnum) {
	    if ((bind + 1) < bnum)
		mec_irq(5);
	    return (0x700 | (uint32) bq[bind++]);
	} else {
	    if (f2open) {
		bnum = DO_STDIO_READ(ifd2, bq, UARTBUF);
	    }
	    if (bnum > 0) {
		bind = 0;
		if ((bind + 1) < bnum)
		    mec_irq(5);
		return (0x700 | (uint32) bq[bind++]);
	    } else {
		return (0x600 | (uint32) bq[bind]);
	    }

	}
#else
	tmp = uartb_data;
	uartb_data &= ~UART_DR;
	uart_stat_reg &= ~UARTB_DR;
	return tmp;
#endif
#else
	return(0);
#endif
	break;

    case 0xE8:			/* UART status register	 */
#ifndef _WIN32
#ifdef FAST_UART

	Ucontrol = 0;
	if (aind < anum) {
	    Ucontrol |= 0x00000001;
	} else {
	    if (f1open) {
	        anum = DO_STDIO_READ(ifd1, aq, UARTBUF);
            }
	    if (anum > 0) {
		Ucontrol |= 0x00000001;
		aind = 0;
		mec_irq(4);
	    }
	}
	if (bind < bnum) {
	    Ucontrol |= 0x00010000;
	} else {
	    if (f2open) {
		bnum = DO_STDIO_READ(ifd2, bq, UARTBUF);
	    }
	    if (bnum > 0) {
		Ucontrol |= 0x00010000;
		bind = 0;
		mec_irq(5);
	    }
	}

	Ucontrol |= 0x00060006;
	return (Ucontrol);
#else
	return (uart_stat_reg);
#endif
#else
	return(0x00060006);
#endif
	break;
    default:
	if (sis_verbose)
	    printf("Read from unimplemented MEC register (%x)\n", addr);

    }
    return (0);
}

static void
write_uart(addr, data)
    uint32          addr;
    uint32          data;
{
    unsigned char   c;

    c = (unsigned char) data;
    switch (addr & 0xff) {

    case 0xE0:			/* UART A */
#ifdef FAST_UART
	if (f1open) {
	    if (wnuma < UARTBUF)
	        wbufa[wnuma++] = c;
	    else {
	        while (wnuma)
		    wnuma -= fwrite(wbufa, 1, wnuma, f1out);
	        wbufa[wnuma++] = c;
	    }
	}
	mec_irq(4);
#else
	if (uart_stat_reg & UARTA_SRE) {
	    uarta_sreg = c;
	    uart_stat_reg &= ~UARTA_SRE;
	    event(uarta_tx, 0, UART_TX_TIME);
	} else {
	    uarta_hreg = c;
	    uart_stat_reg &= ~UARTA_HRE;
	}
#endif
	break;

    case 0xE4:			/* UART B */
#ifdef FAST_UART
	if (f2open) {
	    if (wnumb < UARTBUF)
		wbufb[wnumb++] = c;
	    else {
		while (wnumb)
		    wnumb -= fwrite(wbufb, 1, wnumb, f2out);
		wbufb[wnumb++] = c;
	    }
	}
	mec_irq(5);
#else
	if (uart_stat_reg & UARTB_SRE) {
	    uartb_sreg = c;
	    uart_stat_reg &= ~UARTB_SRE;
	    event(uartb_tx, 0, UART_TX_TIME);
	} else {
	    uartb_hreg = c;
	    uart_stat_reg &= ~UARTB_HRE;
	}
#endif
	break;
    case 0xE8:			/* UART status register */
#ifndef FAST_UART
	if (data & UARTA_CLR) {
	    uart_stat_reg &= 0xFFFF0000;
	    uart_stat_reg |= UARTA_SRE | UARTA_HRE;
	}
	if (data & UARTB_CLR) {
	    uart_stat_reg &= 0x0000FFFF;
	    uart_stat_reg |= UARTB_SRE | UARTB_HRE;
	}
#endif
	break;
    default:
	if (sis_verbose)
	    printf("Write to unimplemented MEC register (%x)\n", addr);

    }
}

static void
flush_uart()
{
    while (wnuma && f1open)
	wnuma -= fwrite(wbufa, 1, wnuma, f1out);
    while (wnumb && f2open)
	wnumb -= fwrite(wbufb, 1, wnumb, f2out);
}



static void
uarta_tx()
{

    while (f1open && fwrite(&uarta_sreg, 1, 1, f1out) != 1);
    if (uart_stat_reg & UARTA_HRE) {
	uart_stat_reg |= UARTA_SRE;
    } else {
	uarta_sreg = uarta_hreg;
	uart_stat_reg |= UARTA_HRE;
	event(uarta_tx, 0, UART_TX_TIME);
    }
    mec_irq(4);
}

static void
uartb_tx()
{
    while (f2open && fwrite(&uartb_sreg, 1, 1, f2out) != 1);
    if (uart_stat_reg & UARTB_HRE) {
	uart_stat_reg |= UARTB_SRE;
    } else {
	uartb_sreg = uartb_hreg;
	uart_stat_reg |= UARTB_HRE;
	event(uartb_tx, 0, UART_TX_TIME);
    }
    mec_irq(5);
}

static void
uart_rx(arg)
    caddr_t         arg;
{
    int32           rsize;
    char            rxd;


    rsize = 0;
    if (f1open)
        rsize = DO_STDIO_READ(ifd1, &rxd, 1);
    if (rsize > 0) {
	uarta_data = UART_DR | rxd;
	if (uart_stat_reg & UARTA_HRE)
	    uarta_data |= UART_THE;
	if (uart_stat_reg & UARTA_SRE)
	    uarta_data |= UART_TSE;
	if (uart_stat_reg & UARTA_DR) {
	    uart_stat_reg |= UARTA_OR;
	    mec_irq(7);		/* UART error interrupt */
	}
	uart_stat_reg |= UARTA_DR;
	mec_irq(4);
    }
    rsize = 0;
    if (f2open)
        rsize = DO_STDIO_READ(ifd2, &rxd, 1);
    if (rsize) {
	uartb_data = UART_DR | rxd;
	if (uart_stat_reg & UARTB_HRE)
	    uartb_data |= UART_THE;
	if (uart_stat_reg & UARTB_SRE)
	    uartb_data |= UART_TSE;
	if (uart_stat_reg & UARTB_DR) {
	    uart_stat_reg |= UARTB_OR;
	    mec_irq(7);		/* UART error interrupt */
	}
	uart_stat_reg |= UARTB_DR;
	mec_irq(5);
    }
    event(uart_rx, 0, UART_RX_TIME);
}

static void
uart_intr(arg)
    caddr_t         arg;
{
    read_uart(0xE8);		/* Check for UART interrupts every 1000 clk */
    flush_uart();		/* Flush UART ports      */
    event(uart_intr, 0, UART_FLUSH_TIME);
}


static void
uart_irq_start()
{
#ifdef FAST_UART
    event(uart_intr, 0, UART_FLUSH_TIME);
#else
#ifndef _WIN32
    event(uart_rx, 0, UART_RX_TIME);
#endif
#endif
}

/* Watch-dog */

static void
wdog_intr(arg)
    caddr_t         arg;
{
    if (wdog_status == disabled) {
	wdog_status = stopped;
    } else {

	if (wdog_counter) {
	    wdog_counter--;
	    event(wdog_intr, 0, wdog_scaler + 1);
	} else {
	    if (wdog_rston) {
		printf("Watchdog reset!\n");
		sys_reset();
		mec_ersr = 0xC000;
	    } else {
		mec_irq(15);
		wdog_rston = 1;
		wdog_counter = wdog_rst_delay;
		event(wdog_intr, 0, wdog_scaler + 1);
	    }
	}
    }
}

static void
wdog_start()
{
    event(wdog_intr, 0, wdog_scaler + 1);
    if (sis_verbose)
	printf("Watchdog started, scaler = %d, counter = %d\n",
	       wdog_scaler, wdog_counter);
}


/* MEC timers */


static void
rtc_intr(arg)
    caddr_t         arg;
{
    if (rtc_counter == 0) {

	mec_irq(13);
	if (rtc_cr)
	    rtc_counter = rtc_reload;
	else
	    rtc_se = 0;
    } else
	rtc_counter -= 1;
    if (rtc_se) {
	event(rtc_intr, 0, rtc_scaler + 1);
	rtc_scaler_start = now();
	rtc_enabled = 1;
    } else {
	if (sis_verbose)
	    printf("RTC stopped\n\r");
	rtc_enabled = 0;
    }
}

static void
rtc_start()
{
    if (sis_verbose)
	printf("RTC started (period %d)\n\r", rtc_scaler + 1);
    event(rtc_intr, 0, rtc_scaler + 1);
    rtc_scaler_start = now();
    rtc_enabled = 1;
}

static uint32
rtc_counter_read()
{
    return (rtc_counter);
}

static void
rtc_scaler_set(val)
    uint32          val;
{
    rtc_scaler = val & 0x0ff;	/* eight-bit scaler only */
}

static void
rtc_reload_set(val)
    uint32          val;
{
    rtc_reload = val;
}

static void
gpt_intr(arg)
    caddr_t         arg;
{
    if (gpt_counter == 0) {
	mec_irq(12);
	if (gpt_cr)
	    gpt_counter = gpt_reload;
	else
	    gpt_se = 0;
    } else
	gpt_counter -= 1;
    if (gpt_se) {
	event(gpt_intr, 0, gpt_scaler + 1);
	gpt_scaler_start = now();
	gpt_enabled = 1;
    } else {
	if (sis_verbose)
	    printf("GPT stopped\n\r");
	gpt_enabled = 0;
    }
}

static void
gpt_start()
{
    if (sis_verbose)
	printf("GPT started (period %d)\n\r", gpt_scaler + 1);
    event(gpt_intr, 0, gpt_scaler + 1);
    gpt_scaler_start = now();
    gpt_enabled = 1;
}

static uint32
gpt_counter_read()
{
    return (gpt_counter);
}

static void
gpt_scaler_set(val)
    uint32          val;
{
    gpt_scaler = val & 0x0ffff;	/* 16-bit scaler */
}

static void
gpt_reload_set(val)
    uint32          val;
{
    gpt_reload = val;
}

static void
timer_ctrl(val)
    uint32          val;
{

    rtc_cr = ((val & TCR_TCRCR) != 0);
    if (val & TCR_TCRCL) {
	rtc_counter = rtc_reload;
    }
    if (val & TCR_TCRSL) {
    }
    rtc_se = ((val & TCR_TCRSE) != 0);
    if (rtc_se && (rtc_enabled == 0))
	rtc_start();

    gpt_cr = (val & TCR_GACR);
    if (val & TCR_GACL) {
	gpt_counter = gpt_reload;
    }
    if (val & TCR_GACL) {
    }
    gpt_se = (val & TCR_GASE) >> 2;
    if (gpt_se && (gpt_enabled == 0))
	gpt_start();
}


/* Retrieve data from target memory.  MEM points to location from which
   to read the data; DATA points to words where retrieved data will be
   stored in host byte order.  SZ contains log(2) of the number of bytes
   to retrieve, and can be 0 (1 byte), 1 (one half-word), 2 (one word),
   or 3 (two words). */

static void
fetch_bytes (asi, mem, data, sz)
    int		    asi;
    unsigned char  *mem;
    uint32	   *data;
    int		    sz;
{
    if (CURRENT_TARGET_BYTE_ORDER == BIG_ENDIAN
	|| asi == 8 || asi == 9) {
	switch (sz) {
	case 3:
	    data[1] =  (((uint32) mem[7]) & 0xff) |
		      ((((uint32) mem[6]) & 0xff) <<  8) |
		      ((((uint32) mem[5]) & 0xff) << 16) |
		      ((((uint32) mem[4]) & 0xff) << 24);
	    /* Fall through to 2 */
	case 2:
	    data[0] =  (((uint32) mem[3]) & 0xff) |
		      ((((uint32) mem[2]) & 0xff) <<  8) |
		      ((((uint32) mem[1]) & 0xff) << 16) |
		      ((((uint32) mem[0]) & 0xff) << 24);
	    break;
	case 1:
	    data[0] =  (((uint32) mem[1]) & 0xff) |
		      ((((uint32) mem[0]) & 0xff) << 8);
	    break;
	case 0:
	    data[0] = mem[0] & 0xff;
	    break;
	    
	}
    } else {
	switch (sz) {
	case 3:
	    data[1] = ((((uint32) mem[7]) & 0xff) << 24) |
		      ((((uint32) mem[6]) & 0xff) << 16) |
		      ((((uint32) mem[5]) & 0xff) <<  8) |
		       (((uint32) mem[4]) & 0xff);
	    /* Fall through to 4 */
	case 2:
	    data[0] = ((((uint32) mem[3]) & 0xff) << 24) |
		      ((((uint32) mem[2]) & 0xff) << 16) |
		      ((((uint32) mem[1]) & 0xff) <<  8) |
		       (((uint32) mem[0]) & 0xff);
	    break;
	case 1:
	    data[0] = ((((uint32) mem[1]) & 0xff) <<  8) |
		       (((uint32) mem[0]) & 0xff);
	    break;
	case 0:
	    data[0] = mem[0] & 0xff;
	    break;
	}
    }
}


/* Store data in target byte order.  MEM points to location to store data;
   DATA points to words in host byte order to be stored.  SZ contains log(2)
   of the number of bytes to retrieve, and can be 0 (1 byte), 1 (one half-word),
   2 (one word), or 3 (two words). */

static void
store_bytes (mem, data, sz)
    unsigned char  *mem;
    uint32	   *data;
    int		    sz;
{
    if (CURRENT_TARGET_BYTE_ORDER == LITTLE_ENDIAN) {
	switch (sz) {
	case 3:
	    mem[7] = (data[1] >> 24) & 0xff;
	    mem[6] = (data[1] >> 16) & 0xff;
	    mem[5] = (data[1] >>  8) & 0xff;
	    mem[4] = data[1] & 0xff;
	    /* Fall through to 2 */
	case 2:
	    mem[3] = (data[0] >> 24) & 0xff;
	    mem[2] = (data[0] >> 16) & 0xff;
	    /* Fall through to 1 */
	case 1:
	    mem[1] = (data[0] >>  8) & 0xff;
	    /* Fall through to 0 */
	case 0:
	    mem[0] = data[0] & 0xff;
	    break;
	}
    } else {
	switch (sz) {
	case 3:
	    mem[7] = data[1] & 0xff;
	    mem[6] = (data[1] >>  8) & 0xff;
	    mem[5] = (data[1] >> 16) & 0xff;
	    mem[4] = (data[1] >> 24) & 0xff;
	    /* Fall through to 2 */
	case 2:
	    mem[3] = data[0] & 0xff;
	    mem[2] = (data[0] >>  8) & 0xff;
	    mem[1] = (data[0] >> 16) & 0xff;
	    mem[0] = (data[0] >> 24) & 0xff;
	    break;
	case 1:
	    mem[1] = data[0] & 0xff;
	    mem[0] = (data[0] >> 8) & 0xff;
	    break;
	case 0:
	    mem[0] = data[0] & 0xff;
	    break;
	    
	}
    }
}


/* Memory emulation */

int
memory_read(asi, addr, data, sz, ws)
    int32           asi;
    uint32          addr;
    uint32         *data;
    int32           sz;
    int32          *ws;
{
    int32           mexc;

#ifdef ERRINJ
    if (errmec) {
	if (sis_verbose)
	    printf("Inserted MEC error %d\n",errmec);
	set_sfsr(errmec, addr, asi, 1);
	if (errmec == 5) mecparerror();
	if (errmec == 6) iucomperr();
	errmec = 0;
	return(1);
    }
#endif

    if ((addr >= mem_ramstart) && (addr < (mem_ramstart + mem_ramsz))) {
	fetch_bytes (asi, &ramb[addr & mem_rammask], data, sz);
	*ws = mem_ramr_ws;
	return (0);
    } else if ((addr >= MEC_START) && (addr < MEC_END)) {
	mexc = mec_read(addr, asi, data);
	if (mexc) {
	    set_sfsr(MEC_ACC, addr, asi, 1);
	    *ws = MEM_EX_WS;
	} else {
	    *ws = 0;
	}
	return (mexc);

#ifdef ERA

    } else if (era) {
    	if ((addr < 0x100000) || 
	    ((addr>= 0x80000000) && (addr < 0x80100000))) {
	    fetch_bytes (asi, &romb[addr & ROM_MASK], data, sz);
	    *ws = 4;
	    return (0);
	} else if ((addr >= 0x10000000) && 
		   (addr < (0x10000000 + (512 << (mec_iocr & 0x0f)))) &&
		   (mec_iocr & 0x10))  {
	    *data = erareg;
	    return (0);
	}
	
    } else  if (addr < mem_romsz) {
	    fetch_bytes (asi, &romb[addr], data, sz);
	    *ws = mem_romr_ws;
	    return (0);

#else
    } else if (addr < mem_romsz) {
	fetch_bytes (asi, &romb[addr], data, sz);
	*ws = mem_romr_ws;
	return (0);
#endif

    }

    printf("Memory exception at %x (illegal address)\n", addr);
    set_sfsr(UIMP_ACC, addr, asi, 1);
    *ws = MEM_EX_WS;
    return (1);
}

int
memory_write(asi, addr, data, sz, ws)
    int32           asi;
    uint32          addr;
    uint32         *data;
    int32           sz;
    int32          *ws;
{
    uint32          byte_addr;
    uint32          byte_mask;
    uint32          waddr;
    uint32         *ram;
    int32           mexc;
    int             i;
    int             wphit[2];

#ifdef ERRINJ
    if (errmec) {
	if (sis_verbose)
	    printf("Inserted MEC error %d\n",errmec);
	set_sfsr(errmec, addr, asi, 0);
	if (errmec == 5) mecparerror();
	if (errmec == 6) iucomperr();
	errmec = 0;
	return(1);
    }
#endif

    if ((addr >= mem_ramstart) && (addr < (mem_ramstart + mem_ramsz))) {
	if (mem_accprot) {

	    waddr = (addr & 0x7fffff) >> 2;
	    for (i = 0; i < 2; i++)
		wphit[i] =
		    (((asi == 0xa) && (mec_wpr[i] & 1)) ||
		     ((asi == 0xb) && (mec_wpr[i] & 2))) &&
		    ((waddr >= mec_ssa[i]) && ((waddr | (sz == 3)) < mec_sea[i]));

	    if (((mem_blockprot) && (wphit[0] || wphit[1])) ||
		((!mem_blockprot) &&
		 !((mec_wpr[0] && wphit[0]) || (mec_wpr[1] && wphit[1]))
		 )) {
		if (sis_verbose)
		    printf("Memory access protection error at 0x%08x\n", addr);
		set_sfsr(PROT_EXC, addr, asi, 0);
		*ws = MEM_EX_WS;
		return (1);
	    }
	}

	store_bytes (&ramb[addr & mem_rammask], data, sz);

	switch (sz) {
	case 0:
	case 1:
	    *ws = mem_ramw_ws + 3;
	    break;
	case 2:
	    *ws = mem_ramw_ws;
	    break;
	case 3:
	    *ws = 2 * mem_ramw_ws + STD_WS;
	    break;
	}
	return (0);
    } else if ((addr >= MEC_START) && (addr < MEC_END)) {
	if ((sz != 2) || (asi != 0xb)) {
	    set_sfsr(MEC_ACC, addr, asi, 0);
	    *ws = MEM_EX_WS;
	    return (1);
	}
	mexc = mec_write(addr, *data);
	if (mexc) {
	    set_sfsr(MEC_ACC, addr, asi, 0);
	    *ws = MEM_EX_WS;
	} else {
	    *ws = 0;
	}
	return (mexc);

#ifdef ERA

    } else if (era) {
    	if ((erareg & 2) && 
	((addr < 0x100000) || ((addr >= 0x80000000) && (addr < 0x80100000)))) {
	    addr &= ROM_MASK;
	    *ws = sz == 3 ? 8 : 4;
	    store_bytes (&romb[addr], data, sz);
            return (0);
	} else if ((addr >= 0x10000000) && 
		   (addr < (0x10000000 + (512 << (mec_iocr & 0x0f)))) &&
		   (mec_iocr & 0x10))  {
	    erareg = *data & 0x0e;
	    return (0);
	}

    } else if ((addr < mem_romsz) && (mec_memcfg & 0x10000) && (wrp) &&
               (((mec_memcfg & 0x20000) && (sz > 1)) || 
		(!(mec_memcfg & 0x20000) && (sz == 0)))) {

	*ws = mem_romw_ws + 1;
	if (sz == 3)
	    *ws += mem_romw_ws + STD_WS;
	store_bytes (&romb[addr], data, sz);
        return (0);

#else
    } else if ((addr < mem_romsz) && (mec_memcfg & 0x10000) && (wrp) &&
               (((mec_memcfg & 0x20000) && (sz > 1)) || 
		(!(mec_memcfg & 0x20000) && (sz == 0)))) {

	*ws = mem_romw_ws + 1;
	if (sz == 3)
            *ws += mem_romw_ws + STD_WS;
	store_bytes (&romb[addr], data, sz);
        return (0);

#endif

    }
	
    *ws = MEM_EX_WS;
    set_sfsr(UIMP_ACC, addr, asi, 0);
    return (1);
}

static unsigned char  *
get_mem_ptr(addr, size)
    uint32          addr;
    uint32          size;
{
    if ((addr + size) < ROM_SZ) {
	return (&romb[addr]);
    } else if ((addr >= mem_ramstart) && ((addr + size) < mem_ramend)) {
	return (&ramb[addr & mem_rammask]);
    }

#ifdef ERA
      else if ((era) && ((addr <0x100000) || 
	((addr >= (unsigned) 0x80000000) && ((addr + size) < (unsigned) 0x80100000)))) {
	return (&romb[addr & ROM_MASK]);
    }
#endif

    return ((char *) -1);
}

int
sis_memory_write(addr, data, length)
    uint32               addr;
    const unsigned char *data;
    uint32               length;
{
    char           *mem;

    if ((mem = get_mem_ptr(addr, length)) == ((char *) -1))
	return (0);

    memcpy(mem, data, length);
    return (length);
}

int
sis_memory_read(addr, data, length)
    uint32          addr;
    char           *data;
    uint32          length;
{
    char           *mem;

    if ((mem = get_mem_ptr(addr, length)) == ((char *) -1))
	return (0);

    memcpy(data, mem, length);
    return (length);
}
