/*************************************************************************** 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 - 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(device); static_cast(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; m_pcr = PCR_RESET; m_esr = 0; m_emr = 0xffff; // clear the output pins if (m_output_pins_changed != NULL) (*m_output_pins_changed)(*this, 0); // 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)(this, buffer, pc, oprom, opram, options); } //************************************************************************** // 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(); } //************************************************************************** // OUTPUT HANDLING //************************************************************************** void dsp32c_device::update_pins(void) { if (m_pcr & PCR_ENI) { if (m_output_pins_changed != NULL) { UINT16 newoutput = 0; if (m_pcr & PCR_PIFs) newoutput |= DSP32_OUTPUT_PIF; if (m_pcr & PCR_PDFs) newoutput |= DSP32_OUTPUT_PDF; 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(); update_pins(); } 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); update_pins(); } 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(); update_pins(); } 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 update_pins(); } result = m_pir; break; // error case default: logerror("dsp32_pio_w called on invalid register %d\n", reg); break; } return (result >> shift) & ~mask; }