/***************************************************************************

    dsp32.c
    Core implementation for the portable DSP32 emulator.

****************************************************************************

    Copyright Aaron Giles
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are
    met:

        * Redistributions of source code must retain the above copyright
          notice, this list of conditions and the following disclaimer.
        * Redistributions in binary form must reproduce the above copyright
          notice, this list of conditions and the following disclaimer in
          the documentation and/or other materials provided with the
          distribution.
        * Neither the name 'MAME' nor the names of its contributors may be
          used to endorse or promote products derived from this software
          without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
    INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
    IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.

****************************************************************************

    Important note:

    At this time, the emulator is rather incomplete. However, it is
    sufficiently complete to run both Race Drivin' and Hard Drivin's
    Airborne, which is all I was after.

    Things that still need to be implemented:

        * interrupts
        * carry-reverse add operations
        * do loops
        * ieee/dsp conversions
        * input/output conversion
        * serial I/O

    In addition, there are several optimizations enabled which make
    assumptions about the code which may not be valid for other
    applications. Check dsp32ops.c for details.

***************************************************************************/

#include "emu.h"
#include "debugger.h"
#include "dsp32.h"


//**************************************************************************
//  DEBUGGING
//**************************************************************************

#define DETECT_MISALIGNED_MEMORY	0



//**************************************************************************
//  CONSTANTS
//**************************************************************************

// internal register numbering for PIO registers
#define PIO_PAR			0
#define PIO_PDR			1
#define PIO_EMR			2
#define PIO_ESR			3
#define PIO_PCR			4
#define PIO_PIR			5
#define PIO_PARE		6
#define PIO_PDR2		7
#define PIO_RESERVED	8

#define UPPER			(0x00ff << 8)
#define LOWER			(0xff00 << 8)

// bits in the PCR register
#define PCR_RESET		0x001
#define PCR_REGMAP		0x002
#define PCR_ENI			0x004
#define PCR_DMA			0x008
#define PCR_AUTO		0x010
#define PCR_PDFs		0x020
#define PCR_PIFs		0x040
#define PCR_RES			0x080
#define PCR_DMA32		0x100
#define PCR_PIO16		0x200
#define PCR_FLG			0x400

// internal flag bits
#define UFLAGBIT		1
#define VFLAGBIT		2



//**************************************************************************
//  MACROS
//**************************************************************************

// register mapping
#define R0				m_r[0]
#define R1				m_r[1]
#define R2				m_r[2]
#define R3				m_r[3]
#define R4				m_r[4]
#define R5				m_r[5]
#define R6				m_r[6]
#define R7				m_r[7]
#define R8				m_r[8]
#define R9				m_r[9]
#define R10				m_r[10]
#define R11				m_r[11]
#define R12				m_r[12]
#define R13				m_r[13]
#define R14				m_r[14]
#define PC				m_r[15]
#define R0_ALT			m_r[16]
#define R15				m_r[17]
#define R16				m_r[18]
#define R17				m_r[19]
#define R18				m_r[20]
#define R19				m_r[21]
#define RMM				m_r[22]
#define RPP				m_r[23]
#define R20				m_r[24]
#define R21				m_r[25]
#define DAUC			m_r[26]
#define IOC				m_r[27]
#define R22				m_r[29]
#define PCSH			m_r[30]

#define A0				m_a[0]
#define A1				m_a[1]
#define A2				m_a[2]
#define A3				m_a[3]
#define A_0				m_a[4]
#define A_1				m_a[5]

#define zFLAG			((m_nzcflags & 0xffffff) == 0)
#define nFLAG			((m_nzcflags & 0x800000) != 0)
#define cFLAG			((m_nzcflags & 0x1000000) != 0)
#define vFLAG			((m_vflags & 0x800000) != 0)
#define ZFLAG			(m_NZflags == 0)
#define NFLAG			(m_NZflags < 0)
#define UFLAG			(m_VUflags & UFLAGBIT)
#define VFLAG			(m_VUflags & VFLAGBIT)



//**************************************************************************
//  DEVICE INTERFACE
//**************************************************************************

const device_type DSP32C = &device_creator<dsp32c_device>;

//-------------------------------------------------
//  dsp32c_device - constructor
//-------------------------------------------------

dsp32c_device::dsp32c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
	: cpu_device(mconfig, DSP32C, "DSP32C", tag, owner, clock),
	  m_program_config("program", ENDIANNESS_LITTLE, 32, 24),
	  m_pin(0),
	  m_pout(0),
	  m_ivtp(0),
	  m_nzcflags(0),
	  m_vflags(0),
	  m_NZflags(0),
	  m_VUflags(0),
	  m_abuf_index(0),
	  m_mbuf_index(0),
	  m_par(0),
	  m_pare(0),
	  m_pdr(0),
	  m_pdr2(0),
	  m_pir(0),
	  m_pcr(0),
	  m_emr(0),
	  m_esr(0),
	  m_pcw(0),
	  m_piop(0),
	  m_ibuf(0),
	  m_isr(0),
	  m_obuf(0),
	  m_osr(0),
	  m_iotemp(0),
	  m_lastp(0),
	  m_icount(0),
	  m_lastpins(0),
	  m_ppc(0),
	  m_program(NULL),
	  m_direct(NULL)
{
	m_output_pins_changed = NULL;

	// set our instruction counter
	m_icountptr = &m_icount;
}


//-------------------------------------------------
//  static_set_config - set the configuration
//  structure
//-------------------------------------------------

void dsp32c_device::static_set_config(device_t &device, const dsp32_config &config)
{
	dsp32c_device &dsp = downcast<dsp32c_device &>(device);
	static_cast<dsp32_config &>(dsp) = config;
}


//-------------------------------------------------
//  device_start - start up the device
//-------------------------------------------------

void dsp32c_device::device_start()
{
	// get our address spaces
	m_program = space(AS_PROGRAM);
	m_direct = &m_program->direct();

	// register our state for the debugger
	astring tempstr;
	state_add(STATE_GENPC,     "GENPC",     m_r[15]).noshow();
	state_add(STATE_GENPCBASE, "GENPCBASE", m_ppc).noshow();
	state_add(STATE_GENSP,     "GENSP",     m_r[21]).noshow();
	state_add(STATE_GENFLAGS,  "GENFLAGS",  m_iotemp).callimport().callexport().formatstr("%6s").noshow();
	state_add(DSP32_PC,        "PC",        m_r[15]).mask(0xffffff);
	for (int regnum = 0; regnum <= 14; regnum++)
		state_add(DSP32_R0 + regnum, tempstr.format("R%d", regnum), m_r[regnum]).mask(0xffffff);
	state_add(DSP32_R15,       "R15",       m_r[17]).mask(0xffffff);
	state_add(DSP32_R16,       "R16",       m_r[18]).mask(0xffffff);
	state_add(DSP32_R17,       "R17",       m_r[19]).mask(0xffffff);
	state_add(DSP32_R18,       "R18",       m_r[20]).mask(0xffffff);
	state_add(DSP32_R19,       "R19",       m_r[21]).mask(0xffffff);
	state_add(DSP32_R20,       "R20",       m_r[24]).mask(0xffffff);
	state_add(DSP32_R21,       "R21",       m_r[25]).mask(0xffffff);
	state_add(DSP32_R22,       "R22",       m_r[29]).mask(0xffffff);
	state_add(DSP32_PIN,       "PIN",       m_pin).mask(0xffffff);
	state_add(DSP32_POUT,      "POUT",      m_pout).mask(0xffffff);
	state_add(DSP32_IVTP,      "IVTP",      m_ivtp).mask(0xffffff);
	state_add(DSP32_A0,        "A0",        m_a[0]).formatstr("%8s");
	state_add(DSP32_A1,        "A1",        m_a[1]).formatstr("%8s");
	state_add(DSP32_A2,        "A2",        m_a[2]).formatstr("%8s");
	state_add(DSP32_A3,        "A3",        m_a[3]).formatstr("%8s");
	state_add(DSP32_DAUC,      "DAUC",      m_r[26]).mask(0xff);
	state_add(DSP32_PAR,       "PAR",       m_par);
	state_add(DSP32_PDR,       "PDR",       m_pdr);
	state_add(DSP32_PIR,       "PIR",       m_pir);
	state_add(DSP32_PCR,       "PCR",       m_iotemp).mask(0x3ff).callimport();
	state_add(DSP32_EMR,       "EMR",       m_emr);
	state_add(DSP32_ESR,       "ESR",       m_esr);
	state_add(DSP32_PCW,       "PCW",       m_pcw);
	state_add(DSP32_PIOP,      "PIOP",      m_piop);
	state_add(DSP32_IBUF,      "IBUF",      m_ibuf);
	state_add(DSP32_ISR,       "ISR",       m_isr);
	state_add(DSP32_OBUF,      "OBUF",      m_obuf);
	state_add(DSP32_OSR,       "OSR" ,      m_osr);
	state_add(DSP32_IOC,       "IOC",       m_r[27]).mask(0xfffff);

	// register our state for saving
	save_item(NAME(m_r));
	save_item(NAME(m_pin));
	save_item(NAME(m_pout));
	save_item(NAME(m_ivtp));
	save_item(NAME(m_nzcflags));
	save_item(NAME(m_vflags));
	save_item(NAME(m_a));
	save_item(NAME(m_NZflags));
	save_item(NAME(m_VUflags));
	save_item(NAME(m_abuf));
	save_item(NAME(m_abufreg));
	save_item(NAME(m_abufVUflags));
	save_item(NAME(m_abufNZflags));
	save_item(NAME(m_abufcycle));
	save_item(NAME(m_abuf_index));
	save_item(NAME(m_mbufaddr));
	save_item(NAME(m_mbufdata));
	save_item(NAME(m_par));
	save_item(NAME(m_pare));
	save_item(NAME(m_pdr));
	save_item(NAME(m_pdr2));
	save_item(NAME(m_pir));
	save_item(NAME(m_pcr));
	save_item(NAME(m_emr));
	save_item(NAME(m_esr));
	save_item(NAME(m_pcw));
	save_item(NAME(m_piop));
	save_item(NAME(m_ibuf));
	save_item(NAME(m_isr));
	save_item(NAME(m_obuf));
	save_item(NAME(m_osr));
	save_item(NAME(m_lastpins));
	save_item(NAME(m_ppc));
}


//-------------------------------------------------
//  device_reset - reset the device
//-------------------------------------------------

void dsp32c_device::device_reset()
{
	// reset goes to 0
	PC = 0;

	// clear some registers
	m_pcw &= 0x03ff;
	update_pcr(m_pcr & PCR_RESET);
	m_esr = 0;
	m_emr = 0xffff;

	// initialize fixed registers
	R0 = R0_ALT = 0;
	RMM = -1;
	RPP = 1;
	A_0 = 0.0;
	A_1 = 1.0;

	// init internal stuff
	m_abufcycle[0] = m_abufcycle[1] = m_abufcycle[2] = m_abufcycle[3] = 12345678;
	m_mbufaddr[0] = m_mbufaddr[1] = m_mbufaddr[2] = m_mbufaddr[3] = 1;
}


//-------------------------------------------------
//  memory_space_config - return the configuration
//  of the specified address space, or NULL if
//  the space doesn't exist
//-------------------------------------------------

const address_space_config *dsp32c_device::memory_space_config(address_spacenum spacenum) const
{
	return (spacenum == AS_PROGRAM) ? &m_program_config : NULL;
}


//-------------------------------------------------
//  state_import - import state into the device,
//  after it has been set
//-------------------------------------------------

void dsp32c_device::state_import(const device_state_entry &entry)
{
	switch (entry.index())
	{
		case STATE_GENFLAGS:
			break;

		case DSP32_PCR:
			update_pcr(m_iotemp);
			break;

		default:
			fatalerror("dsp32c_device::state_import called for unexpected value\n");
			break;
	}
}


//-------------------------------------------------
//  state_export - export state out of the device
//-------------------------------------------------

void dsp32c_device::state_export(const device_state_entry &entry)
{
	switch (entry.index())
	{
		case STATE_GENFLAGS:
			// no actual flags register, so just make something up
			m_iotemp =	((zFLAG != 0) << 0) |
						((nFLAG != 0) << 1) |
						((cFLAG != 0) << 2) |
						((vFLAG != 0) << 3) |
						((ZFLAG != 0) << 4) |
						((NFLAG != 0) << 5) |
						((UFLAG != 0) << 6) |
						((VFLAG != 0) << 7);
			break;

		case DSP32_PCR:
			m_iotemp = m_pcr;
			break;

		default:
			fatalerror("dsp32c_device::state_export called for unexpected value\n");
			break;
	}
}


//-------------------------------------------------
//  state_string_export - export state as a string
//  for the debugger
//-------------------------------------------------

void dsp32c_device::state_string_export(const device_state_entry &entry, astring &string)
{
	switch (entry.index())
	{
		case STATE_GENFLAGS:
			string.printf("%c%c%c%c%c%c%c%c",
				NFLAG ? 'N':'.',
				ZFLAG ? 'Z':'.',
                UFLAG ? 'U':'.',
                VFLAG ? 'V':'.',
                nFLAG ? 'n':'.',
                zFLAG ? 'z':'.',
                cFLAG ? 'c':'.',
                vFLAG ? 'v':'.');
			break;

		case DSP32_A0:
		case DSP32_A1:
		case DSP32_A2:
		case DSP32_A3:
			string.printf("%8g", *(double *)entry.dataptr());
			break;
	}
}


//-------------------------------------------------
//  disasm_min_opcode_bytes - return the length
//  of the shortest instruction, in bytes
//-------------------------------------------------

UINT32 dsp32c_device::disasm_min_opcode_bytes() const
{
	return 4;
}


//-------------------------------------------------
//  disasm_max_opcode_bytes - return the length
//  of the longest instruction, in bytes
//-------------------------------------------------

UINT32 dsp32c_device::disasm_max_opcode_bytes() const
{
	return 4;
}


//-------------------------------------------------
//  disasm_disassemble - call the disassembly
//  helper function
//-------------------------------------------------

offs_t dsp32c_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options)
{
	extern CPU_DISASSEMBLE( dsp32c );
	return CPU_DISASSEMBLE_NAME(dsp32c)(NULL, buffer, pc, oprom, opram, 0);
}




//**************************************************************************
//  MEMORY ACCESSORS
//**************************************************************************

inline UINT32 dsp32c_device::ROPCODE(offs_t pc)
{
	return m_direct->read_decrypted_dword(pc);
}

inline UINT8 dsp32c_device::RBYTE(offs_t addr)
{
	return m_program->read_byte(addr);
}

inline void dsp32c_device::WBYTE(offs_t addr, UINT8 data)
{
	m_program->write_byte(addr, data);
}

inline UINT16 dsp32c_device::RWORD(offs_t addr)
{
#if DETECT_MISALIGNED_MEMORY
	if (addr & 1) fprintf(stderr, "Unaligned word read @ %06X, PC=%06X\n", addr, PC);
#endif
	return m_program->read_word(addr);
}

inline UINT32 dsp32c_device::RLONG(offs_t addr)
{
#if DETECT_MISALIGNED_MEMORY
	if (addr & 3) fprintf(stderr, "Unaligned long read @ %06X, PC=%06X\n", addr, PC);
#endif
	return m_program->read_dword(addr);
}

inline void dsp32c_device::WWORD(offs_t addr, UINT16 data)
{
#if DETECT_MISALIGNED_MEMORY
	if (addr & 1) fprintf(stderr, "Unaligned word write @ %06X, PC=%06X\n", addr, PC);
#endif
	m_program->write_word(addr, data);
}

inline void dsp32c_device::WLONG(offs_t addr, UINT32 data)
{
#if DETECT_MISALIGNED_MEMORY
	if (addr & 3) fprintf(stderr, "Unaligned long write @ %06X, PC=%06X\n", addr, PC);
#endif
	m_program->write_dword(addr, data);
}



//**************************************************************************
//  IRQ HANDLING
//**************************************************************************

void dsp32c_device::check_irqs()
{
	// finish me!
}


void dsp32c_device::set_irq_line(int irqline, int state)
{
	// finish me!
}



//**************************************************************************
//  REGISTER HANDLING
//**************************************************************************

void dsp32c_device::update_pcr(UINT16 newval)
{
	UINT16 oldval = m_pcr;
	m_pcr = newval;

	// reset the chip if we get a reset
	if ((oldval & PCR_RESET) == 0 && (newval & PCR_RESET) != 0)
		reset();

	// track the state of the output pins
	if (m_output_pins_changed != NULL)
	{
		UINT16 newoutput = ((newval & (PCR_PIFs | PCR_ENI)) == (PCR_PIFs | PCR_ENI)) ? DSP32_OUTPUT_PIF : 0;
		if (newoutput != m_lastpins)
		{
			m_lastpins = newoutput;
			(*m_output_pins_changed)(*this, newoutput);
		}
	}
}



//**************************************************************************
//  CORE INCLUDE
//**************************************************************************

#include "dsp32ops.c"



//**************************************************************************
//  CORE EXECUTION LOOP
//**************************************************************************

//-------------------------------------------------
//  execute_min_cycles - return minimum number of
//  cycles it takes for one instruction to execute
//-------------------------------------------------

UINT32 dsp32c_device::execute_min_cycles() const
{
	return 4;
}


//-------------------------------------------------
//  execute_max_cycles - return maximum number of
//  cycles it takes for one instruction to execute
//-------------------------------------------------

UINT32 dsp32c_device::execute_max_cycles() const
{
	return 4;
}


//-------------------------------------------------
//  execute_input_lines - return the number of
//  input/interrupt lines
//-------------------------------------------------

UINT32 dsp32c_device::execute_input_lines() const
{
	return 2;
}


void dsp32c_device::execute_set_input(int inputnum, int state)
{
}


void dsp32c_device::execute_run()
{
	// skip if halted
	if ((m_pcr & PCR_RESET) == 0)
	{
		m_icount = 0;
		return;
	}

	// update buffered accumulator values
	m_abufcycle[0] += m_icount;
	m_abufcycle[1] += m_icount;
	m_abufcycle[2] += m_icount;
	m_abufcycle[3] += m_icount;

	// handle interrupts
	check_irqs();

	while (m_icount > 0)
		execute_one();

	// normalize buffered accumulator values
	m_abufcycle[0] -= m_icount;
	m_abufcycle[1] -= m_icount;
	m_abufcycle[2] -= m_icount;
	m_abufcycle[3] -= m_icount;
}



//**************************************************************************
//  PARALLEL INTERFACE WRITES
//**************************************************************************

const UINT32 dsp32c_device::s_regmap[4][16] =
{
	{	// DSP32 compatible mode
		PIO_PAR|LOWER, PIO_PAR|UPPER, PIO_PDR|LOWER, PIO_PDR|UPPER,
		PIO_EMR|LOWER, PIO_EMR|UPPER, PIO_ESR|LOWER, PIO_PCR|LOWER,
		PIO_PIR|UPPER, PIO_PIR|UPPER, PIO_PIR|UPPER, PIO_PIR|UPPER,
		PIO_PIR|UPPER, PIO_PIR|UPPER, PIO_PIR|UPPER, PIO_PIR|UPPER
	},
	{	// DSP32C 8-bit mode
		PIO_PAR|LOWER, PIO_PAR|UPPER, PIO_PDR|LOWER, PIO_PDR|UPPER,
		PIO_EMR|LOWER, PIO_EMR|UPPER, PIO_ESR|LOWER, PIO_PCR|LOWER,
		PIO_PIR|LOWER, PIO_PIR|UPPER, PIO_PCR|UPPER, PIO_PARE|LOWER,
		PIO_PDR2|LOWER,PIO_PDR2|UPPER,PIO_RESERVED,  PIO_RESERVED
	},
	{	// DSP32C illegal mode
		PIO_RESERVED,  PIO_RESERVED,  PIO_RESERVED,  PIO_RESERVED,
		PIO_RESERVED,  PIO_RESERVED,  PIO_RESERVED,  PIO_RESERVED,
		PIO_RESERVED,  PIO_RESERVED,  PIO_RESERVED,  PIO_RESERVED,
		PIO_RESERVED,  PIO_RESERVED,  PIO_RESERVED,  PIO_RESERVED
	},
	{	// DSP32C 16-bit mode
		PIO_PAR,       PIO_RESERVED,  PIO_PDR,       PIO_RESERVED,
		PIO_EMR,       PIO_RESERVED,  PIO_ESR|LOWER, PIO_PCR,
		PIO_PIR,       PIO_RESERVED,  PIO_RESERVED,  PIO_PARE|LOWER,
		PIO_PDR2,      PIO_RESERVED,  PIO_RESERVED,  PIO_RESERVED
	}
};



//**************************************************************************
//  PARALLEL INTERFACE WRITES
//**************************************************************************

void dsp32c_device::dma_increment()
{
	if (m_pcr & PCR_AUTO)
	{
		int amount = (m_pcr & PCR_DMA32) ? 4 : 2;
		m_par += amount;
		if (m_par < amount)
			m_pare++;
	}
}


void dsp32c_device::dma_load()
{
	// only process if DMA is enabled
	if (m_pcr & PCR_DMA)
	{
		UINT32 addr = m_par | (m_pare << 16);

		// 16-bit case
		if (!(m_pcr & PCR_DMA32))
			m_pdr = RWORD(addr & 0xfffffe);

		// 32-bit case
		else
		{
			UINT32 temp = RLONG(addr & 0xfffffc);
			m_pdr = temp >> 16;
			m_pdr2 = temp & 0xffff;
		}

		// set the PDF flag to indicate we have data ready
		update_pcr(m_pcr | PCR_PDFs);
	}
}


void dsp32c_device::dma_store()
{
	// only process if DMA is enabled
	if (m_pcr & PCR_DMA)
	{
		UINT32 addr = m_par | (m_pare << 16);

		// 16-bit case
		if (!(m_pcr & PCR_DMA32))
			WWORD(addr & 0xfffffe, m_pdr);

		// 32-bit case
		else
			WLONG(addr & 0xfffffc, (m_pdr << 16) | m_pdr2);

		// clear the PDF flag to indicate we have taken the data
		update_pcr(m_pcr & ~PCR_PDFs);
	}
}


void dsp32c_device::pio_w(int reg, int data)
{
	UINT16 mask;
	UINT8 mode;

	// look up register and mask
	mode = ((m_pcr >> 8) & 2) | ((m_pcr >> 1) & 1);
	reg = s_regmap[mode][reg];
	mask = reg >> 8;
	if (mask == 0x00ff) data <<= 8;
	data &= ~mask;
	reg &= 0xff;

	// switch off the register
	switch (reg)
	{
		case PIO_PAR:
			m_par = (m_par & mask) | data;

			// trigger a load on the upper half
			if (!(mask & 0xff00))
				dma_load();
			break;

		case PIO_PARE:
			m_pare = (m_pare & mask) | data;
			break;

		case PIO_PDR:
			m_pdr = (m_pdr & mask) | data;

			// trigger a write and PDF setting on the upper half
			if (!(mask & 0xff00))
			{
				dma_store();
				dma_increment();
			}
			break;

		case PIO_PDR2:
			m_pdr2 = (m_pdr2 & mask) | data;
			break;

		case PIO_EMR:
			m_emr = (m_emr & mask) | data;
			break;

		case PIO_ESR:
			m_esr = (m_esr & mask) | data;
			break;

		case PIO_PCR:
			mask |= 0x0060;
			data &= ~mask;
			update_pcr((m_pcr & mask) | data);
			break;

		case PIO_PIR:
			m_pir = (m_pir & mask) | data;

			// set PIF on upper half
			if (!(mask & 0xff00))
				update_pcr(m_pcr | PCR_PIFs);
			break;

		// error case
		default:
			logerror("dsp32_pio_w called on invalid register %d\n", reg);
			break;
	}
}



//**************************************************************************
//  PARALLEL INTERFACE READS
//**************************************************************************

int dsp32c_device::pio_r(int reg)
{
	UINT16 mask, result = 0xffff;
	UINT8 mode, shift = 0;

	// look up register and mask
	mode = ((m_pcr >> 8) & 2) | ((m_pcr >> 1) & 1);
	reg = s_regmap[mode][reg];
	mask = reg >> 8;
	if (mask == 0x00ff) mask = 0xff00, shift = 8;
	reg &= 0xff;

	// switch off the register
	switch (reg)
	{
		case PIO_PAR:
			result = m_par | 1;
			break;

		case PIO_PARE:
			result = m_pare;
			break;

		case PIO_PDR:
			result = m_pdr;

			// trigger an increment on the lower half
			if (shift != 8)
				dma_increment();

			// trigger a fetch on the upper half
			if (!(mask & 0xff00))
				dma_load();
			break;

		case PIO_PDR2:
			result = m_pdr2;
			break;

		case PIO_EMR:
			result = m_emr;
			break;

		case PIO_ESR:
			result = m_esr;
			break;

		case PIO_PCR:
			result = m_pcr;
			break;

		case PIO_PIR:
			if (!(mask & 0xff00))
				update_pcr(m_pcr & ~PCR_PIFs);	// clear PIFs
			result = m_pir;
			break;

		// error case
		default:
			logerror("dsp32_pio_w called on invalid register %d\n", reg);
			break;
	}

	return (result >> shift) & ~mask;
}
