diff options
author | 2018-12-22 17:06:23 +0100 | |
---|---|---|
committer | 2018-12-22 17:06:47 +0100 | |
commit | dc8834b8dca7f63288f3f512d7e8600356485808 (patch) | |
tree | cb81425c648108f02542ae493e91ec96ad3d2ac4 /src/devices/cpu/dspp | |
parent | 1fd2f33b314b18237e80d8c6a9f70b580854802a (diff) |
Last round of macro removals before the freeze. (nw)
-sound/discrete, okim6295: Removed MCFG macros. [Ryan Holtz]
-norautp, osi, audio/mario: Removed MACHINE_CONFIG macros. [Ryan Holtz]
-vsmile: Split into its own driver from vii.cpp. [Ryan Holtz]
-vii: Fixed broken controller inputs. [Ryan Holtz]
-konamim2: Massive update. Most games work, but are still marked non-working due to rare MAME crashes in the PPC DRC. [Phil Bennett, Ryan Holtz]
Diffstat (limited to 'src/devices/cpu/dspp')
-rw-r--r-- | src/devices/cpu/dspp/dspp.cpp | 2646 | ||||
-rw-r--r-- | src/devices/cpu/dspp/dspp.h | 337 | ||||
-rw-r--r-- | src/devices/cpu/dspp/dsppdasm.cpp | 124 | ||||
-rw-r--r-- | src/devices/cpu/dspp/dsppdasm.h | 22 | ||||
-rw-r--r-- | src/devices/cpu/dspp/dsppdrc.cpp | 970 | ||||
-rw-r--r-- | src/devices/cpu/dspp/dsppfe.cpp | 625 | ||||
-rw-r--r-- | src/devices/cpu/dspp/dsppfe.h | 72 |
7 files changed, 4796 insertions, 0 deletions
diff --git a/src/devices/cpu/dspp/dspp.cpp b/src/devices/cpu/dspp/dspp.cpp new file mode 100644 index 00000000000..d8d8e7055e1 --- /dev/null +++ b/src/devices/cpu/dspp/dspp.cpp @@ -0,0 +1,2646 @@ +// license:BSD-3-Clause +// copyright-holders:Philip Bennett +/*************************************************************************** + + dspp.c + + Core implementation for the portable 3DO (M2) DSPP emulator. + DSPP = Don's Super Performing Processor + +***************************************************************************/ + +#include "emu.h" +#include "debugger.h" +#include "dspp.h" +#include "dsppfe.h" +#include "dsppdasm.h" + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +enum +{ + DSPP_PC = 1, + DSPP_ACC, + DSPP_FLAGS, + DSPP_CLOCK, +}; + + +// DRC +#define SINGLE_INSTRUCTION_MODE (0) + + +#define CACHE_SIZE (4 * 1024 * 1024) // FIXME +// FIXME!!! +/* compilation boundaries -- how far back/forward does the analysis extend? */ +#define COMPILE_BACKWARDS_BYTES 128 +#define COMPILE_FORWARDS_BYTES 512 +#define COMPILE_MAX_INSTRUCTIONS ((COMPILE_BACKWARDS_BYTES/4) + (COMPILE_FORWARDS_BYTES/4)) +#define COMPILE_MAX_SEQUENCE 64 + +/* exit codes */ +#define EXECUTE_OUT_OF_CYCLES 0 +#define EXECUTE_MISSING_CODE 1 +#define EXECUTE_UNMAPPED_CODE 2 +#define EXECUTE_RESET_CACHE 3 + + + + +//************************************************************************** +// MACROS +//************************************************************************** + +//#define DSPI_FLAG_CC_CARRY 0x0001 +//#define DSPI_FLAG_CC_ZERO 0x0002 +//#define DSPI_FLAG_CC_NEG 0x0004 +//#define DSPI_FLAG_CC_OVER 0x0008 +//#define DSPI_FLAG_CC_EXACT 0x0010 +//#define DSPI_FLAG_AUDLOCK 0x0020 +//#define DSPI_FLAG_SLEEP 0x0040 +//#define DSPI_FLAG_CC_MASK 0x001f + +#define DSPX_CONTROL_GWILLING 0x0001 +#define DSPX_CONTROL_STEP_CYCLE 0x0002 +#define DSPX_CONTROL_STEP_PC 0x0004 +#define DSPX_CONTROL_SNOOP 0x0008 + +#define DSPX_RESET_DSPP 0x0001 +#define DSPX_RESET_INPUT 0x0002 +#define DSPX_RESET_OUTPUT 0x0004 + +#define DSPX_F_DMA_NEXTVALID 0x0001 +#define DSPX_F_DMA_GO_FOREVER 0x0002 +#define DSPX_F_INT_DMANEXT_EN 0x0004 +#define DSPX_F_SHADOW_SET_DMANEXT 0x00040000 +#define DSPX_F_SHADOW_SET_FOREVER 0x00020000 +#define DSPX_F_SHADOW_SET_NEXTVALID 0x00010000 +#define DSPX_F_SHADOW_SET_ADDRESS_COUNT 0x80000000 + +#define DSPX_F_INT_TIMER 0x00000100 +#define DSPX_F_INT_INPUT_UNDER 0x00000080 +#define DSPX_F_INT_INPUT_OVER 0x00000040 +#define DSPX_F_INT_OUTPUT_UNDER 0x00000020 +#define DSPX_F_INT_OUTPUT_OVER 0x00000010 +#define DSPX_F_INT_UNDEROVER 0x00000008 +#define DSPX_F_INT_CONSUMED 0x00000002 +#define DSPX_F_INT_DMANEXT 0x00000001 + +#define DSPX_F_INT_ALL_DMA (DSPX_F_INT_DMANEXT | DSPX_F_INT_CONSUMED | DSPX_F_INT_UNDEROVER) + +#define DSPX_FLD_INT_SOFT_WIDTH 16 /* width of the field and the number of interrupts */ +#define DSPX_FLD_INT_SOFT_SHIFT 16 +#define DSPX_FLD_INT_SOFT_MASK (0xffff0000) + + + +//************************************************************************** +// INTERNAL MEMORY MAPS +//************************************************************************** + +void dspp_device::code_map(address_map &map) +{ + map(0x000, 0x3ff).ram(); +} + +void dspp_device::data_map(address_map &map) +{ + map(0x000, 0x2df).ram(); + map(0x2f0, 0x2f1).r(FUNC(dspp_device::input_r)); + map(0x2e0, 0x2e7).w(FUNC(dspp_device::output_w)); + map(0x300, 0x37f).rw(FUNC(dspp_device::fifo_osc_r), FUNC(dspp_device::fifo_osc_w)); + map(0x3bc, 0x3bc).nopr(); // ? + map(0x3d6, 0x3d6).w(FUNC(dspp_device::input_control_w)); + map(0x3d7, 0x3d7).w(FUNC(dspp_device::output_control_w)); + map(0x3de, 0x3de).r(FUNC(dspp_device::input_status_r)); + map(0x3df, 0x3df).r(FUNC(dspp_device::output_status_r)); + map(0x3e6, 0x3e6).w(FUNC(dspp_device::cpu_int_w)); + map(0x3ee, 0x3ee).rw(FUNC(dspp_device::pc_r), FUNC(dspp_device::pc_w)); + map(0x3f6, 0x3f6).rw(FUNC(dspp_device::audlock_r), FUNC(dspp_device::audlock_w)); + map(0x3f7, 0x3f7).rw(FUNC(dspp_device::clock_r), FUNC(dspp_device::clock_w)); + map(0x3ff, 0x3ff).r(FUNC(dspp_device::noise_r)); +} + + + +//************************************************************************** +// DEVICE INTERFACE +//************************************************************************** + +DEFINE_DEVICE_TYPE(DSPP, dspp_device, "dspp", "3DO DSPP") + +//------------------------------------------------- +// dspp_device - constructor +//------------------------------------------------- + +dspp_device::dspp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : dspp_device(mconfig, DSPP, tag, owner, clock, address_map_constructor(FUNC(dspp_device::code_map), this), + address_map_constructor(FUNC(dspp_device::data_map), this)) +{ +} + +dspp_device::dspp_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, address_map_constructor code_map_ctor, address_map_constructor data_map_ctor) + : cpu_device(mconfig, type, tag, owner, clock), + m_int_handler(*this), + m_dma_read_handler(*this), + m_dma_write_handler(*this), + m_code_config("code", ENDIANNESS_BIG, 16, 10, -1, code_map_ctor), + m_data_config("data", ENDIANNESS_BIG, 16, 10, -1, data_map_ctor), + m_code(nullptr), + m_data(nullptr), + m_output_fifo_start(0), + m_output_fifo_count(0), + m_dspx_control(0), + m_dspx_reset(0), + m_dspx_int_enable(0), + m_dspx_channel_enable(0), + m_dspx_channel_complete(0), + m_dspx_channel_direction(0), + m_dspx_channel_8bit(0), + m_dspx_channel_sqxd(0), + m_dspx_shadow_current_addr(0), + m_dspx_shadow_current_count(0), + m_dspx_shadow_next_addr(0), + m_dspx_shadow_next_count(0), + m_dspx_dmanext_int(0), + m_dspx_dmanext_enable(0), + m_dspx_consumed_int(0), + m_dspx_consumed_enable(0), + m_dspx_underover_int(0), + m_dspx_underover_enable(0), + m_dspx_audio_time(0), + m_dspx_audio_duration(0), + m_cache(CACHE_SIZE), + m_drcuml(nullptr), + m_drcfe(nullptr), + m_drcoptions(0) +{ +#if 0 + memset(m_core->m_stack, 0, sizeof(m_core->m_stack)); + memset(m_core->m_rbase, 0, sizeof(m_core->m_rbase)); + memset(m_outputs, 0, sizeof(m_outputs)); + memset(m_output_fifo, 0, sizeof(m_output_fifo)); + memset(m_fifo_dma, 0, sizeof(m_fifo_dma)); +#endif +} + + +//------------------------------------------------- +// device_start - start up the device +//------------------------------------------------- + +void dspp_device::device_start() +{ + // DSPP DRC is not yet ready for prime time + m_isdrc = false;//allow_drc(); + + m_core = (dspp_internal_state *)m_cache.alloc_near(sizeof(dspp_internal_state)); + memset(m_core, 0, sizeof(dspp_internal_state)); + + uint32_t flags = 0; + m_drcuml = std::make_unique<drcuml_state>(*this, m_cache, flags, 1, 16, 0); + + m_drcfe = std::make_unique<dspp_frontend>(this, COMPILE_BACKWARDS_BYTES, COMPILE_FORWARDS_BYTES, SINGLE_INSTRUCTION_MODE ? 1 : COMPILE_MAX_SEQUENCE); + + // Resolve our callbacks + m_int_handler.resolve_safe(); + m_dma_read_handler.resolve_safe(0); + m_dma_write_handler.resolve_safe(); + + // Get our address spaces + m_code = &space(AS_PROGRAM); + m_data = &space(AS_DATA); + auto code_cache = m_code->cache<1, -1, ENDIANNESS_BIG>(); + m_code_cache = code_cache; + m_codeptr = [code_cache](offs_t address) -> const void * { return code_cache->read_ptr(address); }; + + // Register our state for the debugger + state_add(DSPP_PC, "PC", m_core->m_pc); + state_add(DSPP_ACC, "ACC", m_core->m_acc); + state_add(STATE_GENPC, "GENPC", m_core->m_pc).noshow(); +#if 0 + state_add(STATE_GENFLAGS, "GENFLAGS", m_core->m_flags).callimport().callexport().formatstr("%6s").noshow(); + state_add(STATE_GENPCBASE, "GENPCBASE", m_ppc).noshow(); + state_add(STATE_GENSP, "GENSP", m_src2val[REGBASE + 31]).noshow(); + + state_add(DSPP_PS, "PS", m_core->m_flagsio).callimport().callexport(); + for (int regnum = 0; regnum < 32; regnum++) + state_add(DSPP_R0 + regnum, tempstr.format("R%d", regnum), m_src2val[REGBASE + regnum]); +#endif + + // Register our state for saving + save_item(NAME(m_core->m_pc)); + save_item(NAME(m_core->m_stack)); + save_item(NAME(m_core->m_stack_ptr)); + save_item(NAME(m_core->m_rbase)); + save_item(NAME(m_core->m_acc)); + save_item(NAME(m_core->m_tclock)); + + save_item(NAME(m_core->m_flag_carry)); + save_item(NAME(m_core->m_flag_zero)); + save_item(NAME(m_core->m_flag_neg)); + save_item(NAME(m_core->m_flag_over)); + save_item(NAME(m_core->m_flag_exact)); + save_item(NAME(m_core->m_flag_audlock)); + save_item(NAME(m_core->m_flag_sleep)); + + save_item(NAME(m_outputs)); + save_item(NAME(m_output_fifo_start)); + save_item(NAME(m_output_fifo_count)); + + for (uint32_t i = 0; i < NUM_DMA_CHANNELS; ++i) + { + save_item(NAME(m_fifo_dma[i].m_current_addr), i); + save_item(NAME(m_fifo_dma[i].m_current_count), i); + save_item(NAME(m_fifo_dma[i].m_next_addr), i); + save_item(NAME(m_fifo_dma[i].m_next_count), i); + save_item(NAME(m_fifo_dma[i].m_prev_value), i); + save_item(NAME(m_fifo_dma[i].m_prev_current), i); + save_item(NAME(m_fifo_dma[i].m_go_forever), i); + save_item(NAME(m_fifo_dma[i].m_next_valid), i); + save_item(NAME(m_fifo_dma[i].m_reserved), i); + save_item(NAME(m_fifo_dma[i].m_fifo), i); + save_item(NAME(m_fifo_dma[i].m_dma_ptr), i); + save_item(NAME(m_fifo_dma[i].m_dspi_ptr), i); + save_item(NAME(m_fifo_dma[i].m_depth), i); + } + + save_item(NAME(m_last_frame_clock)); + save_item(NAME(m_last_osc_count)); + save_item(NAME(m_osc_phase)); + save_item(NAME(m_osc_freq)); + + save_item(NAME(m_core->m_partial_int)); + + save_item(NAME(m_dspx_control)); + save_item(NAME(m_dspx_reset)); + save_item(NAME(m_dspx_int_enable)); + save_item(NAME(m_dspx_channel_enable)); + save_item(NAME(m_dspx_channel_complete)); + save_item(NAME(m_dspx_channel_direction)); + save_item(NAME(m_dspx_channel_8bit)); + save_item(NAME(m_dspx_channel_sqxd)); + save_item(NAME(m_dspx_shadow_current_addr)); + save_item(NAME(m_dspx_shadow_current_count)); + save_item(NAME(m_dspx_shadow_next_addr)); + save_item(NAME(m_dspx_shadow_next_count)); + save_item(NAME(m_dspx_dmanext_int)); + save_item(NAME(m_dspx_dmanext_enable)); + save_item(NAME(m_dspx_consumed_int)); + save_item(NAME(m_dspx_consumed_enable)); + save_item(NAME(m_dspx_underover_int)); + save_item(NAME(m_dspx_underover_enable)); + save_item(NAME(m_dspx_audio_time)); + save_item(NAME(m_dspx_audio_duration)); + + // Set our instruction counter + set_icountptr(m_core->m_icount); + + m_cache_dirty = true; +} + + +//------------------------------------------------- +// device_reset - reset the device +//------------------------------------------------- + +void dspp_device::device_reset() +{ + // initialize the state + m_core->m_pc = 0; + m_core->m_stack_ptr = 0; + m_output_fifo_start = 0; + m_output_fifo_count = 0; + + m_core->m_flag_audlock = 0; + m_core->m_flag_sleep = 0; + m_core->m_stack_ptr = 0; + m_core->m_writeback = ~1; // TODO + set_rbase(0, 0); + + // TODO: CLEAR DMA CHANNELS + + // Clear interrupts + m_core->m_partial_int = 0; + update_host_interrupt(); + + m_cache_dirty = true; +} + + +//------------------------------------------------- +// memory_space_config - return the configuration +// of the CPU's address spaces +//------------------------------------------------- + +device_memory_interface::space_config_vector dspp_device::memory_space_config() const +{ + return space_config_vector { + std::make_pair(AS_PROGRAM, &m_code_config), + std::make_pair(AS_DATA, &m_data_config) + }; +} + + +//------------------------------------------------- +// state_import - import state into the device, +// after it has been set +//------------------------------------------------- + +void dspp_device::state_import(const device_state_entry &entry) +{ + +} + + +//------------------------------------------------- +// state_export - export state from the device, +// to a known location where it can be read +//------------------------------------------------- + +void dspp_device::state_export(const device_state_entry &entry) +{ + +} + + +//------------------------------------------------- +// state_string_export - export state as a string +// for the debugger +//------------------------------------------------- + +void dspp_device::state_string_export(const device_state_entry &entry, std::string &str) const +{ + switch (entry.index()) + { + case STATE_GENFLAGS: + str = string_format("%c%c.%c%c%c%c%c", + m_core->m_flag_audlock ? 'A' : '.', + m_core->m_flag_sleep ? 'S' : '.', + m_core->m_flag_carry ? 'C' : '.', + m_core->m_flag_zero ? 'Z' : '.', + m_core->m_flag_neg ? 'N' : '.', + m_core->m_flag_over ? 'V' : '.', + m_core->m_flag_exact ? 'E' : '.'); + break; + } +} + +std::unique_ptr<util::disasm_interface> dspp_device::create_disassembler() +{ + return std::make_unique<dspp_disassembler>(); +} + + + +//************************************************************************** +// INLINE HELPERS +//************************************************************************** + +//------------------------------------------------- +// update_pc - +//------------------------------------------------- + +inline void dspp_device::update_pc() +{ + ++m_core->m_pc; +} + +inline void dspp_device::update_ticks() +{ + --m_core->m_tclock; + --m_core->m_icount; +} + + +//------------------------------------------------- +// readop - Read an opcode at the given address +//------------------------------------------------- + +uint16_t dspp_device::read_op(offs_t pc) +{ + return m_code_cache->read_word(pc); +} + + +//------------------------------------------------- +// read_data - Read a word from the data space +//------------------------------------------------- + +inline uint16_t dspp_device::read_data(offs_t addr) +{ + return m_data->read_word(addr); +} + + +//------------------------------------------------- +// write_data - Write a word to the data space +//------------------------------------------------- + +inline void dspp_device::write_data(offs_t addr, uint16_t data) +{ + m_data->write_word(addr, data); +} + + +//------------------------------------------------- +// get_interrupt_state - +//------------------------------------------------- + +uint32_t dspp_device::get_interrupt_state() +{ + uint32_t host_int = m_core->m_partial_int; + + if (m_dspx_dmanext_int & m_dspx_dmanext_enable) + host_int |= DSPX_F_INT_DMANEXT; + + if (m_dspx_consumed_int & m_dspx_consumed_enable) + host_int |= DSPX_F_INT_CONSUMED; + + return host_int; +} + + +//------------------------------------------------- +// update_host_interrupt - +//------------------------------------------------- + +void dspp_device::update_host_interrupt() +{ + // TODO: Underflow/overflow interrupts + m_int_handler(get_interrupt_state() & m_dspx_int_enable ? ASSERT_LINE : CLEAR_LINE); +} + + +//------------------------------------------------- +// parse_operand - Parse instruction operands +//------------------------------------------------- + +void dspp_device::parse_operands(uint32_t numops) +{ + uint32_t addr, val = 0xBAD; + uint32_t opidx = 0; + uint32_t operand = 0; + uint32_t numregs = 0; + + for (uint32_t i = 0; i < MAX_OPERANDS; ++i) + { + // Reset operands + m_operands[i].value = -1; + m_operands[i].addr = -1; + } + + // Reset global op index + m_core->m_opidx = 0; + + while (opidx < numops) + { + operand = read_op(m_core->m_pc); + update_pc(); + update_ticks(); + + if (operand & 0x8000) + { + // Immediate value + if ((operand & 0xc000) == 0xc000) + { + val = operand & 0x1fff; + + if (operand & 0x2000) + { + // Left justify + val = val << 3; + } + else + { + // Sign extend if right justified + if (val & 0x1000) + val |= 0xe000; + } + m_operands[opidx++].value = val; + } + else if((operand & 0xe000) == 0x8000) + { + // Address operand + addr = operand & 0x03ff; + + if (operand & 0x0400) + { + // Indirect + addr = read_data(addr); + } + m_operands[opidx].addr = addr; + + if (operand & 0x0800) + { + // Write Back + m_core->m_writeback = addr; + } + ++opidx; + } + else if ((operand & 0xe000) == 0xa000) + { + // 1 or 2 register operand + numregs = (operand & 0x0400) ? 2 : 1; + } + } + else + { + numregs = 3; + } + + if (numregs > 0) + { + uint32_t shifter, regdi; + + // Shift successive register operands from a single operand word + for (uint32_t i = 0; i < numregs; ++i) + { + shifter = ((numregs - i) - 1) * 5; + regdi = (operand >> shifter) & 0x1f; + addr = translate_reg(regdi & 0xf); + + if (regdi & 0x0010) + { + // Indirect? + addr = read_data(addr); + } + + if (numregs == 2) + { + if ((i == 0) && (operand & 0x1000)) + m_core->m_writeback = addr; + else if ((i == 1) && (operand & 0x0800)) + m_core->m_writeback = addr; + } + else if (numregs == 1) + { + if (operand & 0x800) + m_core->m_writeback = addr; + } + + m_operands[opidx++].addr = addr; + } + numregs = 0; + } + } +} + + +//------------------------------------------------- +// read_next_operand - Return the value encoded by +// the next operand +//------------------------------------------------- + +uint16_t dspp_device::read_next_operand() +{ + int32_t value = m_operands[m_core->m_opidx].value; + + if (value < 0) + value = read_data(m_operands[m_core->m_opidx].addr); + + // Next operand + ++m_core->m_opidx; + + return value; +} + + +//------------------------------------------------- +// write_next_operand - Write to the address +// encoded by the next operand +//------------------------------------------------- + +void dspp_device::write_next_operand(uint16_t value) +{ + int32_t addr = m_operands[m_core->m_opidx].addr; + + assert(addr != -1); + + write_data(addr, value); + + // Advance to the next operand + ++m_core->m_opidx; +} + + +//------------------------------------------------- +// push_pc - Push program counter onto the stack +//------------------------------------------------- + +inline void dspp_device::push_pc() +{ + if (m_core->m_stack_ptr < PC_STACK_DEPTH) + m_core->m_stack[m_core->m_stack_ptr++] = m_core->m_pc; + else + fatalerror("DSPP stack overflow!"); +} + + +//------------------------------------------------- +// pop_pc - Pop program counter from the stack +//------------------------------------------------- + +inline uint16_t dspp_device::pop_pc() +{ + if (m_core->m_stack_ptr == 0) + fatalerror("DSPP stack underflow!"); + + return m_core->m_stack[--m_core->m_stack_ptr]; +} + + +//------------------------------------------------- +// set_rbase - Set register address base +//------------------------------------------------- + +inline void dspp_device::set_rbase(uint32_t base, uint32_t addr) +{ + switch (base) + { + case 4: + m_core->m_rbase[1] = addr + 4 - base; + break; + case 0: + m_core->m_rbase[0] = addr; + m_core->m_rbase[1] = addr + 4 - base; + // Intentional fall-through + case 8: + m_core->m_rbase[2] = addr + 8 - base; + + case 12: + m_core->m_rbase[3] = addr + 12 - base; + break; + } +} + + +//------------------------------------------------- +// translate_reg - Translate register address +//------------------------------------------------- + +inline uint16_t dspp_device::translate_reg(uint16_t reg) +{ + uint32_t base = (reg >> 2) & 3; + return m_core->m_rbase[base] + reg - (reg & ~3); +} + + + +//************************************************************************** +// CORE EXECUTION +//************************************************************************** + +//------------------------------------------------- +// execute_min_cycles - return minimum number of +// cycles it takes for one instruction to execute +//------------------------------------------------- + +uint32_t dspp_device::execute_min_cycles() const +{ + return 1; +} + + +//------------------------------------------------- +// execute_max_cycles - return maximum number of +// cycles it takes for one instruction to execute +//------------------------------------------------- + +uint32_t dspp_device::execute_max_cycles() const +{ + return 5; // TODO ? +} + + +//------------------------------------------------- +// execute_run - core execution loop +//------------------------------------------------- + +void dspp_device::execute_run() +{ + if (m_isdrc) + { + // TODO: TEMPORARY HACK! + if (m_dspx_control & DSPX_CONTROL_GWILLING) + execute_run_drc(); + else + m_core->m_icount = 0; + return; + } + + bool check_debugger = ((device_t::machine().debug_flags & DEBUG_FLAG_ENABLED) != 0); + + do + { + update_ticks(); + update_fifo_dma(); + + // Only run if enabled + if (m_dspx_control & DSPX_CONTROL_GWILLING) + { + if (check_debugger) + debugger_instruction_hook(m_core->m_pc); + + m_core->m_op = read_op(m_core->m_pc); + update_pc(); + + // Decode and execute + if (m_core->m_op & 0x8000) + exec_control(); + else + exec_arithmetic(); + } + + } while (m_core->m_icount > 0); +} + + + +//************************************************************************** +// OPCODE IMPLEMENTATIONS +//************************************************************************** + +//------------------------------------------------- +// exec_super_special - Execute a super special +// control op +//------------------------------------------------- + +inline void dspp_device::exec_super_special() +{ + uint32_t sel = (m_core->m_op >> 7) & 7; + + switch (sel) + { + case 1: // BAC + { + m_core->m_pc = m_core->m_acc >> 4; + break; + } + case 4: // RTS + { + m_core->m_pc = pop_pc(); + break; + } + case 5: // OP_MASK + { + // TODO + break; + } + + case 7: // SLEEP + { + // TODO: How does sleep work? + --m_core->m_pc; + m_core->m_flag_sleep = 1; + break; + } + + case 0: // NOP + case 2: // Unused + case 3: + case 6: + break; + } +} + + +//------------------------------------------------- +// exec_special - Execute a special control op +//------------------------------------------------- + +inline void dspp_device::exec_special() +{ + switch ((m_core->m_op >> 10) & 7) + { + case 0: + { + exec_super_special(); + break; + } + case 1: // JUMP + { + m_core->m_pc = m_core->m_op & 0x3ff; + break; + } + case 2: // JSR + { + push_pc(); + m_core->m_pc = m_core->m_op & 0x3ff; + break; + } + case 3: // BFM + { + break; + } + case 4: // MOVEREG + { + uint32_t regdi = m_core->m_op & 0x3f; + uint32_t addr = translate_reg(regdi & 0xf); + + // Indirect + if (regdi & 0x0010) + { + addr = read_data(addr); + break; + } + + parse_operands(1); + write_data(addr, read_next_operand()); + break; + } + case 5: // RBASE + { + set_rbase((m_core->m_op & 3) << 2, m_core->m_op & 0x3fc); + break; + } + case 6: // MOVED + { + parse_operands(1); + write_data(m_core->m_op & 0x3ff, read_next_operand()); + break; + } + case 7: // MOVEI + { + parse_operands(1); + uint32_t addr = read_data(m_core->m_op & 0x3ff); + write_data(addr, read_next_operand()); + break; + } + + default: + break; + } +} + + +//------------------------------------------------- +// exec_branch - Execute a branch control op +//------------------------------------------------- + +void dspp_device::exec_branch() +{ + uint32_t mode = (m_core->m_op >> 13) & 3; + uint32_t select = (m_core->m_op >> 12) & 1; + uint32_t mask = (m_core->m_op >> 10) & 3; + + bool flag0, flag1; + + if (select == 0) + { + flag0 = m_core->m_flag_neg; + flag1 = m_core->m_flag_over; + } + else + { + flag0 = m_core->m_flag_carry; + flag1 = m_core->m_flag_zero; + } + + bool mask0 = (mask & 2) != 0; + bool mask1 = (mask & 1) != 0; + + bool branch = (flag0 || !mask0) && (flag1 || !mask1); + + if (mode == 2) + branch = !branch; + + if (branch) + m_core->m_pc = m_core->m_op & 0x3ff; +} + + +//------------------------------------------------- +// exec_complex_branch - Execute a complex branch +// control op +//------------------------------------------------- + +inline void dspp_device::exec_complex_branch() +{ + uint32_t type = (m_core->m_op >> 10) & 7; + + const bool c = m_core->m_flag_carry; + const bool z = m_core->m_flag_zero; + const bool n = m_core->m_flag_neg; + const bool v = m_core->m_flag_over; + const bool x = m_core->m_flag_exact; + + bool branch = false; + + switch (type) + { + case 0: // BLT + branch = (n && !v) || (!n && v); + break; + case 1: // BLE + branch = ((n && !v) || (!n && v)) || z; + break; + case 2: // BGE + branch = ((n && v) || (!n && !v)); + break; + case 3: // BGT + branch = ((n && v) || (!n && !v)) && !z; + break; + case 4: // BHI + branch = c && !z; + break; + case 5: // BLS + branch = !c || z; + break; + case 6: // BXS + branch = x; + break; + case 7: // BXC + branch = !x; + break; + } + + if (branch) + m_core->m_pc = m_core->m_op & 0x3ff; +} + + +//------------------------------------------------- +// exec_control - Execute a control op +//------------------------------------------------- + +inline void dspp_device::exec_control() +{ + uint32_t mode = (m_core->m_op >> 13) & 3; + + switch (mode) + { + // Special + case 0: + { + exec_special(); + break; + } + + // Branches + case 1: case 2: + { + exec_branch(); + break; + } + + // Complex branches + case 3: + { + exec_complex_branch(); + break; + } + + default: + fatalerror("Invalid DSPP control instruction mode"); + break; + } +} + +//------------------------------------------------- +// sign_extend8 - Sign extend 8-bits to 32-bits +//------------------------------------------------- + +static inline int32_t sign_extend8(uint8_t val) +{ + return (int32_t)(int8_t)val; +} + + +//------------------------------------------------- +// sign_extend16 - Sign extend 16-bits to 32-bits +//------------------------------------------------- + +static inline int32_t sign_extend16(uint16_t val) +{ + return (int32_t)(int16_t)val; +} + + +//------------------------------------------------- +// sign_extend20 - Sign extend 20-bits to 32-bits +//------------------------------------------------- + +static inline int32_t sign_extend20(uint32_t val) +{ + if (val & 0x00080000) + return (int32_t)(0xfff00000 | val); + else + return (int32_t)val; +} + + +//------------------------------------------------- +// exec_arithmetic - Execute an arithmetic op +//------------------------------------------------- + +inline void dspp_device::exec_arithmetic() +{ + // Decode the various fields + uint32_t numops = (m_core->m_op >> 13) & 3; + uint32_t muxa = (m_core->m_op >> 10) & 3; + uint32_t muxb = (m_core->m_op >> 8) & 3; + uint32_t alu_op = (m_core->m_op >> 4) & 0xf; + uint32_t barrel_code = m_core->m_op & 0xf; + + int32_t mul_res = 0; + uint32_t alu_res = 0; + + // Check for operand overflow + if (numops == 0 && ((muxa == 1) || (muxa == 2) || (muxb == 1) || (muxb == 2))) + numops = 4; + + // Implicit barrel shift + if (barrel_code == 8) + ++numops; + + // Parse ops... + parse_operands(numops); + + if (muxa == 3 || muxb == 3) + { + uint32_t mul_sel = (m_core->m_op >> 12) & 1; + + int32_t op1 = sign_extend16(read_next_operand()); + int32_t op2 = sign_extend16(mul_sel ? read_next_operand() : m_core->m_acc >> 4); + + mul_res = (op1 * op2) >> 11; + } + + int32_t alu_a, alu_b; + + switch (muxa) + { + case 0: + { + alu_a = m_core->m_acc; + break; + } + case 1: case 2: + { + alu_a = read_next_operand() << 4; + break; + } + case 3: + { + alu_a = mul_res; + break; + } + } + + switch (muxb) + { + case 0: + { + alu_b = m_core->m_acc; + break; + } + case 1: case 2: + { + alu_b = read_next_operand() << 4; + break; + } + case 3: + { + alu_b = mul_res; + break; + } + } + + // For carry detection apparently + alu_a &= 0x00fffff; + alu_b &= 0x00fffff; + + switch (alu_op) + { + case 0: // _TRA + { + alu_res = alu_a; + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + case 1: // _NEG + { + alu_res = -alu_b; + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + case 2: // _+ + { + alu_res = alu_a + alu_b; + m_core->m_flag_over = (((alu_a & 0x80000) == (alu_b & 0x80000) && (alu_a & 0x80000) != (alu_res & 0x80000))); + m_core->m_flag_carry = (alu_res & 0x00100000) != 0; + break; + } + case 3: // _+C + { + alu_res = alu_a + (m_core->m_flag_carry << 4); + m_core->m_flag_over = 0; + m_core->m_flag_carry = (alu_res & 0x00100000) != 0; + break; + } + case 4: // _- + { + alu_res = alu_a - alu_b; + m_core->m_flag_over = ((alu_a & 0x80000) == (~alu_b & 0x80000) && (alu_a & 0x80000) != (alu_res & 0x80000)); + m_core->m_flag_carry = (alu_res & 0x00100000) != 0; + break; + } + case 5: // _-B + { + alu_res = alu_a - (m_core->m_flag_carry << 4); + m_core->m_flag_over = 0; + m_core->m_flag_carry = (alu_res & 0x00100000) != 0; + break; + } + case 6: // _++ + { + alu_res = alu_a + 1; + m_core->m_flag_over = !(alu_a & 0x80000) && (alu_res & 0x80000); + m_core->m_flag_carry = 0; + break; + } + case 7: // _-- + { + alu_res = alu_a - 1; + m_core->m_flag_over = (alu_a & 0x80000) && !(alu_res & 0x80000); + m_core->m_flag_carry = 0; + break; + } + case 8: // _TRL + { + alu_res = alu_a; + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + case 9: // _NOT + { + alu_res = ~alu_a; + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + case 10: // _AND + { + alu_res = alu_a & alu_b; + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + case 11: // _NAND + { + alu_res = ~(alu_a & alu_b); + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + case 12: // _OR + { + alu_res = alu_a | alu_b; + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + case 13: // _NOR + { + alu_res = ~(alu_a | alu_b); + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + case 14: // _XOR + { + alu_res = alu_a ^ alu_b; + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + case 15: // _XNOR + { + alu_res = ~(alu_a ^ alu_b); + m_core->m_flag_over = 0; + m_core->m_flag_carry = 0; + break; + } + } + + m_core->m_flag_neg = (alu_res & 0x00080000) != 0; + m_core->m_flag_zero = (alu_res & 0x000ffff0) == 0; + m_core->m_flag_exact = (alu_res & 0x0000000f) == 0; + + // Barrel shift + static const int32_t shifts[8] = { 0, 1, 2, 3, 4, 5, 8, 16 }; + + if (barrel_code == 8) + barrel_code = read_next_operand(); + + if (barrel_code & 8) + { + // Right shift + uint32_t shift = shifts[(~barrel_code + 1) & 7]; + + if (alu_op < 8) + { + // Arithmetic + m_core->m_acc = sign_extend20(alu_res) >> shift; + } + else + { + // Logical + m_core->m_acc = (alu_res & 0xfffff) >> shift; + } + + } + else + { + // Left shift + uint32_t shift = shifts[barrel_code]; + + if (shift == 16) + { + // Clip and saturate + if (m_core->m_flag_over) + m_core->m_acc = m_core->m_flag_neg ? 0x7ffff : 0xfff80000; + else + m_core->m_acc = sign_extend20(alu_res); + } + else + { + m_core->m_acc = sign_extend20(alu_res) << shift; + } + } + + if (m_core->m_writeback >= 0) + { + write_data(m_core->m_writeback, m_core->m_acc >> 4); + m_core->m_writeback = -1; + } + else if (m_core->m_opidx < numops) + { + write_next_operand(m_core->m_acc >> 4); + } +} + + + +//************************************************************************** +// FIFO DMA +//************************************************************************** + +//------------------------------------------------- +// write_dma_to_fifo - +//------------------------------------------------- + +void dspp_device::write_dma_to_fifo(int32_t channel, int16_t value) +{ + fifo_dma &dma = m_fifo_dma[channel]; + + dma.m_fifo[dma.m_dma_ptr] = value; + + if (dma.m_depth < DMA_FIFO_DEPTH) + { + dma.m_dma_ptr = (dma.m_dma_ptr + 1) & DMA_FIFO_MASK; + dma.m_depth += 1; + } + else + { + fatalerror("DMA TO FIFO OVERFLOW"); + } +} + + +//------------------------------------------------- +// write_dspp_to_fifo - +//------------------------------------------------- + +void dspp_device::write_dspp_to_fifo(int32_t channel, int16_t value) +{ + fifo_dma &dma = m_fifo_dma[channel]; + + dma.m_fifo[dma.m_dspi_ptr] = value; + + if (dma.m_depth < DMA_FIFO_DEPTH) + { + dma.m_dspi_ptr = (dma.m_dspi_ptr + 1) & DMA_FIFO_MASK; + dma.m_depth += 1; + } + else + { + fatalerror("DSPP TO FIFO OVERFLOW"); + } +} + + +//------------------------------------------------- +// read_fifo_to_dspp - +//------------------------------------------------- + +int16_t dspp_device::read_fifo_to_dspp(int32_t channel) +{ + int16_t data = 0; + + fifo_dma &dma = m_fifo_dma[channel]; + + if (dma.m_depth > 0) + { + data = dma.m_fifo[dma.m_dspi_ptr]; + + dma.m_dspi_ptr = (dma.m_dspi_ptr + 1) & DMA_FIFO_MASK; + --dma.m_depth; + + if (dma.m_depth == 0) + { + // Set consumed interrupt + if (m_dspx_channel_complete & (1 << channel)) + { + m_dspx_consumed_int |= 1 << channel; + update_host_interrupt(); + } + } + + dma.m_prev_current = data; + } + else + { + // TODO: Is this right? + m_dspx_underover_int |= 1 << channel; + update_host_interrupt(); + data = dma.m_prev_current; + } + + return data; +} + + +//------------------------------------------------- +// read_fifo_to_dma - +//------------------------------------------------- + +int16_t dspp_device::read_fifo_to_dma(int32_t channel) +{ + fifo_dma &dma = m_fifo_dma[channel]; + + uint32_t data = dma.m_fifo[dma.m_dma_ptr]; + + if (dma.m_depth > 0) + { + dma.m_dma_ptr = (dma.m_dma_ptr + 1) & DMA_FIFO_MASK; + dma.m_depth -= 1; + } + else + { + //fatalerror("FIFO TO DMA UNDERFLOW"); + } + return data; +} + + +//------------------------------------------------- +// run_oscillator - +//------------------------------------------------- + +void dspp_device::run_oscillator(int32_t channel) +{ + fifo_dma &dma = m_fifo_dma[channel]; + + // Add phase increment + m_osc_phase += m_osc_freq; + + // Extract two high bits to advance FIFO + uint32_t count = (m_osc_phase >> 15) & 3; + + // Clip to positive phase + m_osc_phase &= 0x7fff; + + // Advance FIFO if data present + if (count > dma.m_depth) + count = dma.m_depth; + + for (uint32_t i = 0; i < count; ++i) + read_fifo_to_dspp(channel); + + // Return count to program for counting samples + m_last_osc_count = count; +} + + +//------------------------------------------------- +// advance_audio_timer - +//------------------------------------------------- + +void dspp_device::advance_audio_timer() +{ + // Advance time on each frame count + ++m_dspx_audio_time; + + // Interrupt on transition from 0 to 0xFFFF + if (--m_dspx_audio_duration == 0xffff) + { + m_core->m_partial_int |= DSPX_F_INT_TIMER; + update_host_interrupt(); + } +} + + +//------------------------------------------------- +// advance_audio_frame - +//------------------------------------------------- + +void dspp_device::advance_audio_frame() +{ + m_last_frame_clock = m_clock; + advance_audio_timer(); + + if (m_core->m_flag_audlock) + { + device_reset(); + } +} + + +//------------------------------------------------- +// process_next_dma - +//------------------------------------------------- + +void dspp_device::process_next_dma(int32_t channel) +{ + fifo_dma &dma = m_fifo_dma[channel]; + + if (dma.m_current_count <= 0) + { + uint32_t chmask = 1 << channel; + + if (dma.m_next_valid) + { + dma.m_current_addr = dma.m_next_addr; + dma.m_current_count = dma.m_next_count; + + if (!dma.m_go_forever) + dma.m_next_valid = 0; + + // Set completion bit + m_dspx_channel_complete &= ~chmask; + m_dspx_dmanext_int |= chmask; + update_host_interrupt(); + } + else + { + // Disable the channel so we don't keep servicing it + m_dspx_channel_enable &= ~chmask; + } + } +} + + +//------------------------------------------------- +// decode_sqxd - Decompress an SQXD coded sample +//------------------------------------------------- + +int16_t dspp_device::decode_sqxd(int8_t data, int16_t prev) +{ + int16_t temp = sign_extend8(data & 0xfe); + int32_t expanded = (temp * abs(temp)) << 1; + int16_t output; + + if (data & 1) + { + expanded = expanded >> 2; + output = expanded + prev; + } + else + { + output = expanded; + } + + return output; +} + + +//------------------------------------------------- +// service_output_dma - +//------------------------------------------------- + +void dspp_device::service_output_dma(int32_t channel) +{ + fifo_dma & dma = m_fifo_dma[channel]; + + if (dma.m_current_count == 0) + return; + + // Transfer a maximum of 4 samples per tick + uint32_t count = dma.m_current_count; + + if (count > 4) + count = 4; + + for (uint32_t i = 0; i < count; ++i) + { + uint16_t sample = read_fifo_to_dma(channel); + + m_dma_write_handler(dma.m_current_addr++, sample >> 8); + m_dma_write_handler(dma.m_current_addr++, sample & 0xff); + } + + dma.m_current_count -= count; + + process_next_dma(channel); +} + + +//------------------------------------------------- +// service_input_dma - +//------------------------------------------------- + +void dspp_device::service_input_dma(int32_t channel) +{ + fifo_dma &dma = m_fifo_dma[channel]; + + if (dma.m_current_count == 0) + return; + + // Transfer a maximum of 4 samples per tick + uint32_t count = dma.m_current_count; + + if (count > 4) + count = 4; + + // Determine sample format + bool is8bit = (m_dspx_channel_8bit & (1 << channel)) != 0; + bool isSQXD = (m_dspx_channel_sqxd & (1 << channel)) != 0; + + if (is8bit) + { + // Fetch data from memory, {decompress}, and write to FIFO + for (uint32_t i = 0; i < count; ++i) + { + int16_t sample; + int8_t curbyte = m_dma_read_handler(dma.m_current_addr++); + + if (isSQXD) + { + printf("SQXD NOT TESTED!"); + + sample = decode_sqxd(curbyte, dma.m_prev_value); + dma.m_prev_value = sample; + } + else + { + sample = curbyte << 8; + } + + write_dma_to_fifo(channel, sample); + } + } + else + { + for (uint32_t i = 0; i < count; ++i) + { + int16_t sample; + + sample = m_dma_read_handler(dma.m_current_addr++) << 8; + sample |= m_dma_read_handler(dma.m_current_addr++); + + write_dma_to_fifo(channel, sample); + } + } + + dma.m_current_count -= count; + + process_next_dma(channel); +} + + +//------------------------------------------------- +// update_fifo_dma - +//------------------------------------------------- + +void dspp_device::update_fifo_dma() +{ + uint32_t mask = m_dspx_channel_enable & ~m_dspx_channel_complete; + + while (mask != 0) + { + uint32_t channel = 31 - count_leading_zeros(mask); + + const fifo_dma & dma = m_fifo_dma[channel]; + + if (m_dspx_channel_direction & (1 << channel)) + { + if (dma.m_depth >= 4) + { + service_output_dma(channel); + break; + } + } + else + { + if (dma.m_depth <= 4) + { + service_input_dma(channel); + break; + } + } + + mask &= (1 << channel) - 1; + } +} + + +//------------------------------------------------- +// reset_channel +//------------------------------------------------- + +void dspp_device::reset_channel(int32_t channel) +{ + fifo_dma &dma = m_fifo_dma[channel]; + + m_dspx_channel_complete &= ~(1 << channel); + + dma.m_dma_ptr = 0; + dma.m_dspi_ptr = 0; + dma.m_depth = 0; +} + + + +//************************************************************************** +// INTERNAL REGISTERS +//************************************************************************** + +//------------------------------------------------- +// input_r - Read digital input +//------------------------------------------------- + +READ16_MEMBER( dspp_device::input_r ) +{ + // TODO + return 0; +} + + +//------------------------------------------------- +// output_w - Write to the 8 output registers +//------------------------------------------------- + +WRITE16_MEMBER( dspp_device::output_w ) +{ + m_outputs[offset] = data; +} + + +//------------------------------------------------- +// fifo_osc_r - +//------------------------------------------------- + +READ16_MEMBER( dspp_device::fifo_osc_r ) +{ + uint32_t data = 0; + uint32_t channel = offset / 8; + + fifo_dma &dma = m_fifo_dma[channel]; + + switch (offset & 7) + { + // DSPI_FIFO_OSC_OFFSET_CURRENT + case 0: + { + if (dma.m_depth == 0) + data = dma.m_prev_current; + else + data = dma.m_fifo[dma.m_dspi_ptr]; + + break; + } + + // DSPI_FIFO_OSC_OFFSET_NEXT + case 1: + { + if (dma.m_depth == 0) + data = dma.m_prev_current; + else if (dma.m_depth == 1) + data = dma.m_fifo[dma.m_dspi_ptr]; + else + data = dma.m_fifo[(dma.m_dspi_ptr + 1) & DMA_FIFO_MASK]; + + break; + } + + // DSPI_FIFO_OSC_OFFSET_FREQUENCY + case 2: + { + data = m_last_osc_count; + break; + } + + // DSPI_FIFO_OSC_OFFSET_PHASE + case 3: + { + data = m_osc_phase; + break; + } + + // DSPI_FIFO_OFFSET_DATA + case 4: + { + data = read_fifo_to_dspp(channel); + break; + } + + // DSPI_FIFO_OFFSET_CONTROL + case 5: + { + data = dma.m_depth; + break; + } + } + + return data; +} + + +//------------------------------------------------- +// fifo_osc_w - +//------------------------------------------------- + +WRITE16_MEMBER( dspp_device::fifo_osc_w ) +{ + uint32_t channel = offset / 8; + + switch (offset & 7) + { + // DSPI_FIFO_OSC_OFFSET_CURRENT + case 0: + { + // Read only + break; + } + + // DSPI_FIFO_OSC_OFFSET_NEXT + case 1: + { + // Read only + break; + } + + // DSPI_FIFO_OSC_OFFSET_FREQUENCY + case 2: + { + m_osc_freq = data; + run_oscillator(channel); + break; + } + + // DSPI_FIFO_OSC_OFFSET_PHASE + case 3: + { + m_osc_phase = data; + break; + } + + // DSPI_FIFO_OFFSET_DATA + case 4: + { + write_dspp_to_fifo(channel, data); + break; + } + + // DSPI_FIFO_OFFSET_CONTROL + case 5: + { + // Read only + break; + } + } +} + + +//------------------------------------------------- +// input_control_w - +//------------------------------------------------- + +WRITE16_MEMBER( dspp_device::input_control_w ) +{ + // TODO +} + + +//------------------------------------------------- +// output_control_w - +//------------------------------------------------- + +WRITE16_MEMBER( dspp_device::output_control_w ) +{ + // TODO + if (data & 1) + { + uint32_t end; + + if (m_output_fifo_count == OUTPUT_FIFO_DEPTH) + { + // Overflow + end = (m_output_fifo_start + m_output_fifo_count) & OUTPUT_FIFO_MASK; + + m_output_fifo_start = (m_output_fifo_start + 2) & OUTPUT_FIFO_MASK; + + m_output_fifo[(end + 0) & OUTPUT_FIFO_MASK] = m_outputs[0]; + m_output_fifo[(end + 1) & OUTPUT_FIFO_MASK] = m_outputs[1]; + } + else + { + end = (m_output_fifo_start + m_output_fifo_count) & OUTPUT_FIFO_MASK; + + m_output_fifo[(end + 0) & OUTPUT_FIFO_MASK] = m_outputs[0]; + m_output_fifo[(end + 1) & OUTPUT_FIFO_MASK] = m_outputs[1]; + + // Advance and update FIFO status + m_output_fifo_count += 2; + } + + advance_audio_frame(); + } +} + + +//------------------------------------------------- +// input_status_r - Read input state +//------------------------------------------------- + +READ16_MEMBER( dspp_device::input_status_r ) +{ + // TODO: How should this work? + return 1; +} + + +//------------------------------------------------- +// output_status_r - Return number of unread +// entries in the output FIFO +//------------------------------------------------- + +READ16_MEMBER( dspp_device::output_status_r ) +{ + return m_output_fifo_count; +} + + +//------------------------------------------------- +// cpu_int_w - Host CPU soft interrupt +//------------------------------------------------- + +WRITE16_MEMBER( dspp_device::cpu_int_w ) +{ + m_core->m_partial_int |= (data << DSPX_FLD_INT_SOFT_SHIFT) & DSPX_FLD_INT_SOFT_MASK; + update_host_interrupt(); +} + + +//------------------------------------------------- +// pc_r - Read program counter +//------------------------------------------------- + +READ16_MEMBER( dspp_device::pc_r ) +{ + return m_core->m_pc; +} + + +//------------------------------------------------- +// pc_w - Write program counter +//------------------------------------------------- + +WRITE16_MEMBER(dspp_device:: pc_w ) +{ + m_core->m_pc = data; +} + + +//------------------------------------------------- +// audlock_r - Read Audio Lock status +//------------------------------------------------- + +READ16_MEMBER( dspp_device::audlock_r ) +{ + return m_core->m_flag_audlock; +} + + +//------------------------------------------------- +// audlock_w - Write Audio Lock status +//------------------------------------------------- + +WRITE16_MEMBER( dspp_device::audlock_w ) +{ + m_core->m_flag_audlock = data & 1; +} + + +//------------------------------------------------- +// clock_r - Read CPU tick counter +//------------------------------------------------- + +READ16_MEMBER( dspp_device::clock_r ) +{ + return m_core->m_tclock; +} + + +//------------------------------------------------- +// clock_w - Write CPU tick counter +//------------------------------------------------- + +WRITE16_MEMBER( dspp_device::clock_w ) +{ + m_core->m_tclock = data; +} + + +//------------------------------------------------- +// noise_r - PRNG noise +//------------------------------------------------- + +READ16_MEMBER( dspp_device::noise_r ) +{ + // TODO: Obviously this isn't accurate + return machine().rand(); +} + + + +//************************************************************************** +// EXTERNAL INTERFACE AND CONTROL REGISTERS +//************************************************************************** + +//------------------------------------------------- +// read_ext_control - +//------------------------------------------------- + +uint32_t dspp_device::read_ext_control(offs_t offset) +{ + uint32_t data = 0; + + switch (offset) + { + // DSPX_INTERRUPT_SET + case 0x4000/4: + // DSPX_INTERRUPT_CLR + case 0x4004/4: + { + data = get_interrupt_state(); + break; + } + + // DSPX_INTERRUPT_ENABLE + case 0x4008/4: + // DSPX_INTERRUPT_DISABLE + case 0x400C/4: + { + data = m_dspx_int_enable; + break; + } + // DSPX_INT_DMANEXT_SET + case 0x4010/4: + // DSPX_INT_DMANEXT_CLR + case 0x4014/4: + { + data = m_dspx_dmanext_int; + break; + } + // DSPX_INT_DMANEXT_ENABLE + case 0x4018/4: + { + data = m_dspx_dmanext_enable; + break; + } + // DSPX_INT_CONSUMED_SET + case 0x4020/4: + // DSPX_INT_CONSUMED_CLR + case 0x4024/4: + { + data = m_dspx_consumed_int; + break; + } + // DSPX_INT_CONSUMED_ENABLE + case 0x4028/4: + // DSPX_INT_CONSUMED_DISABLE + case 0x402c/4: + { + data = m_dspx_consumed_enable; + break; + } + + // DSPX_INT_UNDEROVER_SET + case 0x4030/4: + // DSPX_INT_UNDEROVER_CLR + case 0x4034/4: + { + data = m_dspx_underover_int; + break; + } + + // DSPX_INT_UNDEROVER_ENABLE + case 0x4038/4: + // DSPX_INT_UNDEROVER_DISABLE + case 0x403c/4: + { + data = m_dspx_underover_enable; + break; + } + + // DSPX_CHANNEL_ENABLE + case 0x6000/4: + // DSPX_CHANNEL_DISABLE + case 0x6004/4: + { + data = m_dspx_channel_enable; + break; + } + // DSPX_CHANNEL_DIRECTION_SET + case 0x6008/4: + // DSPX_CHANNEL_DIRECTION_CLR + case 0x600c/4: + { + data = m_dspx_channel_direction; + break; + } + // DSPX_CHANNEL_8BIT_SET + case 0x6010/4: + // DSPX_CHANNEL_8BIT_CLR + case 0x6014/4: + { + data = m_dspx_channel_8bit; + break; + } + // DSPX_CHANNEL_SQXD_SET + case 0x6018/4: + // DSPX_CHANNEL_SQXD_CLR + case 0x601c/4: + { + data = m_dspx_channel_sqxd; + break; + } + // DSPX_CHANNEL_STATUS + case 0x603c/4: + { + data = m_dspx_channel_complete; + break; + } + + // DSPX_FRAME_DOWN_COUNTER: + case 0x6040/4: + { + data = m_dspx_audio_duration; + break; + } + // DSPX_FRAME_UP_COUNTER: + case 0x6044/4: + { + data = m_dspx_audio_time; + break; + } + + // AUDIO_CONFIG + case 0x6050/4: + { + break; + } + // AUDIN_CONFIG + case 0x6060/4: + { + break; + } + // AUDOUT_CONFIG + case 0x6068/4: + { + break; + } + // DSPX_CONTROL + case 0x6070/4: + { + data = m_dspx_control; + break; + } + default: + { + printf("DSPP: Unhandled external control read (%.4x)\n", offset << 2); + break; + } + } + + return data; +} + + +//------------------------------------------------- +// write_ext_control - +//------------------------------------------------- + +void dspp_device::write_ext_control(offs_t offset, uint32_t data) +{ + switch (offset) + { + // DSPX_INTERRUPT_SET + case 0x4000/4: + { + m_core->m_partial_int |= data & ~DSPX_F_INT_ALL_DMA; + update_host_interrupt(); + break; + } + // DSPX_INTERRUPT_CLR + case 0x4004/4: + { + m_core->m_partial_int &= ~(data & ~DSPX_F_INT_ALL_DMA); + update_host_interrupt(); + break; + } + // DSPX_INTERRUPT_ENABLE + case 0x4008/4: + { + m_dspx_int_enable |= data; + update_host_interrupt(); + break; + } + // DSPX_INTERRUPT_DISABLE + case 0x400C/4: + { + m_dspx_int_enable &= ~data; + update_host_interrupt(); + break; + } + // DSPX_INT_DMANEXT_SET + case 0x4010/4: + { + m_dspx_dmanext_int |= data; + update_host_interrupt(); + break; + } + // DSPX_INT_DMANEXT_CLR + case 0x4014/4: + { + m_dspx_dmanext_int &= ~data; + update_host_interrupt(); + break; + } + // DSPX_INT_DMANEXT_ENABLE + case 0x4018/4: + { + m_dspx_dmanext_enable |= data; + update_host_interrupt(); + break; + } + // DSPX_INT_CONSUMED_SET + case 0x4020/4: + { + m_dspx_consumed_int |= data; + update_host_interrupt(); + break; + } + // DSPX_INT_CONSUMED_CLR + case 0x4024/4: + { + m_dspx_consumed_int &= ~data; + update_host_interrupt(); + break; + } + // DSPX_INT_CONSUMED_ENABLE + case 0x4028/4: + { + m_dspx_consumed_enable |= data; + update_host_interrupt(); + break; + } + // DSPX_INT_CONSUMED_DISABLE + case 0x402c/4: + { + m_dspx_consumed_enable &= ~data; + update_host_interrupt(); + break; + } + + // DSPX_INT_UNDEROVER_SET + case 0x4030/4: + { + m_dspx_underover_int |= data; + update_host_interrupt(); + break; + } + // DSPX_INT_UNDEROVER_CLR + case 0x4034/4: + { + m_dspx_underover_int &= ~data; + update_host_interrupt(); + break; + } + // DSPX_INT_UNDEROVER_ENABLE + case 0x4038/4: + { + m_dspx_underover_enable |= data; + update_host_interrupt(); + break; + } + // DSPX_INT_UNDEROVER_DISABLE + case 0x403c/4: + { + m_dspx_underover_enable &= ~data; + update_host_interrupt(); + break; + } + + // DSPX_CHANNEL_ENABLE + case 0x6000/4: + { + m_dspx_channel_enable |= data; + update_host_interrupt(); + break; + } + // DSPX_CHANNEL_DISABLE + case 0x6004/4: + { + m_dspx_channel_enable &= ~data; + update_host_interrupt(); + break; + } + // DSPX_CHANNEL_DIRECTION_SET + case 0x6008/4: + { + m_dspx_channel_direction |= data; + break; + } + // DSPX_CHANNEL_DIRECTION_CLR + case 0x600c/4: + { + m_dspx_channel_direction &= ~data; + break; + } + // DSPX_CHANNEL_8BIT_SET + case 0x6010/4: + { + m_dspx_channel_8bit |= data; + break; + } + // DSPX_CHANNEL_8BIT_CLR + case 0x6014/4: + { + m_dspx_channel_8bit &= ~data; + break; + } + // DSPX_CHANNEL_SQXD_SET + case 0x6018/4: + { + m_dspx_channel_sqxd |= data; + break; + } + // DSPX_CHANNEL_SQXD_CLR + case 0x601c/4: + { + m_dspx_channel_sqxd &= ~data; + break; + } + // DSPX_CHANNEL_RESET + case 0x6030/4: + { + for (uint32_t i = 0; i < NUM_DMA_CHANNELS; ++i) + { + if (data & (1 << i)) + reset_channel(i); + } + break; + } + + // DSPX_FRAME_DOWN_COUNTER: + case 0x6040/4: + { + m_dspx_audio_duration = data; + break; + } + // DSPX_FRAME_UP_COUNTER: + case 0x6044/4: + { + m_dspx_audio_time = data; + break; + } + + // AUDIO_CONFIG + case 0x6050/4: + { + break; + } + + // AUDIN_CONFIG + case 0x6060/4: + { + break; + } + + // AUDOUT_CONFIG + case 0x6068/4: + { + break; + } + + // DSPX_CONTROL + case 0x6070/4: + { + m_dspx_control = data; + break; + } + + // DSPX_RESET + case 0x6074/4: + { + if (data & 1) + device_reset(); + + // TODO: DSPX_F_RESET_INPUT and DSPX_F_RESET_OUTPUT + + break; + } + default: + { + printf("DSPP: Unhandled external control write (%.4x with %.8x)\n", offset << 2, data); + break; + } + } +} + + +//------------------------------------------------- +// read - host CPU read from DSPP internals +//------------------------------------------------- + +READ32_MEMBER( dspp_device::read ) +{ + if (offset < 0x1000/4) + { + // 16-bit code memory + return m_code->read_word(offset); + } + else if (offset >= 0x1000/4 && offset < 0x2000/4) + { + // 16-bit data memory and registers + return m_data->read_word((offset - 0x1000/4)); + } + else if(offset >= 0x5000/4 && offset < 0x6000/4) + { + // DMA registers + return read_dma_stack(offset - 0x5000/4); + } + else + { + // 32-bit control registers + return read_ext_control(offset); + } +} + + +//------------------------------------------------- +// read_dma_stack - +//------------------------------------------------- + +uint32_t dspp_device::read_dma_stack(offs_t offset) +{ + uint32_t data = 0; + + if (offset < 0x200 / 4) + { + uint32_t channel = offset / (16/4); + uint32_t reg = offset & 3; + fifo_dma &dma = m_fifo_dma[channel]; + + switch (reg) + { + case 0x00/4://DSPX_DMA_ADDR_OFFSET: + { + data = dma.m_current_addr; + break; + } + case 0x04/4://DSPX_DMA_COUNT_OFFSET: + { + data = dma.m_current_count; + break; + } + case 0x08/4://DSPX_DMA_NEXT_ADDR_OFFSET: + { + data = dma.m_next_addr; + break; + } + case 0x0c/4://DSPX_DMA_NEXT_COUNT_OFFSET: + { + data = dma.m_next_count; + break; + } + } + } + else if (offset >= 0x200/4 && offset < (0x200/4 + (NUM_DMA_CHANNELS * (16/4)))) + { + uint32_t channel = (offset - 0x200/4) / (16/4); + fifo_dma &dma = m_fifo_dma[channel]; + + data = dma.m_go_forever ? DSPX_F_DMA_GO_FOREVER : 0; + data |= dma.m_next_valid ? DSPX_F_DMA_NEXTVALID : 0; + } + else + { + fatalerror("Unhandled DMA stack read"); + } + + return data; +} + + +//------------------------------------------------- +// write_dma_stack - +//------------------------------------------------- + +void dspp_device::write_dma_stack(offs_t offset, uint32_t data) +{ + if (offset < 0x200 / 4) + { + switch (offset & 3) + { + case 0x00/4://DSPX_DMA_ADDR_OFFSET: + { + m_dspx_shadow_current_addr = data; + break; + } + case 0x04/4://DSPX_DMA_COUNT_OFFSET: + { + m_dspx_shadow_current_count = data; + break; + } + case 0x08/4://DSPX_DMA_NEXT_ADDR_OFFSET: + { + m_dspx_shadow_next_addr = data; + break; + } + case 0x0c/4://DSPX_DMA_NEXT_COUNT_OFFSET: + { + m_dspx_shadow_next_count = data; + break; + } + } + } + else if (offset >= 0x200/4 && offset < (0x200/4 + (NUM_DMA_CHANNELS * (16/4)))) + { + uint32_t channel = (offset - 0x200/4) / (16/4); + fifo_dma &dma = m_fifo_dma[channel]; + + if (data & DSPX_F_SHADOW_SET_NEXTVALID) + { + dma.m_next_valid = (data & DSPX_F_DMA_NEXTVALID) != 0; + } + if (data & DSPX_F_SHADOW_SET_FOREVER) + { + dma.m_go_forever = (data & DSPX_F_DMA_GO_FOREVER) != 0; + } + if (data & DSPX_F_SHADOW_SET_DMANEXT) + { + if (data & DSPX_F_INT_DMANEXT_EN) + { + m_dspx_dmanext_enable |= (1 << channel); + } + else + { + m_dspx_dmanext_enable &= ~(1 << channel); + } + } + if (data & DSPX_F_SHADOW_SET_ADDRESS_COUNT) + { + if (offset & (8/4)) + { + dma.m_next_addr = m_dspx_shadow_next_addr; + dma.m_next_count = m_dspx_shadow_next_count; + } + else + { + dma.m_current_addr = m_dspx_shadow_current_addr; + dma.m_current_count = m_dspx_shadow_current_count; + } + } + } + else + { + fatalerror("Unhandled DMA stack write"); + } +} + + +//------------------------------------------------- +// write - host CPU write to DSPP internals +//------------------------------------------------- + +WRITE32_MEMBER( dspp_device::write ) +{ + if (offset < 0x1000/4) + { + // 16-bit code memory + m_code->write_word(offset, data); + } + else if (offset >= 0x1000/4 && offset < 0x2000/4) + { + // 16-bit data memory and registers + m_data->write_word((offset - 0x1000/4), data); + } + else if(offset >= 0x5000/4 && offset < 0x6000/4) + { + // DMA registers + write_dma_stack(offset - 0x5000/4, data); + + // Better safe than sorry... + machine().scheduler().synchronize(); + } + else + { + // 32-bit control registers + write_ext_control(offset, data); + + // Better safe than sorry... + machine().scheduler().synchronize(); + } +} + + +//------------------------------------------------- +// read_output_fifo - Get data for the DACs +//------------------------------------------------- + +uint16_t dspp_device::read_output_fifo() +{ + uint16_t data = 0; + + if (m_output_fifo_count == 0) + { + // Underflow + return m_output_fifo[m_output_fifo_start]; + } + + data = m_output_fifo[m_output_fifo_start]; + + m_output_fifo_start = (m_output_fifo_start + 1) & OUTPUT_FIFO_MASK; + --m_output_fifo_count; + + return data; +} + +// DEBUG! + +char * GetBinary(char * buffer, uint32_t val, uint32_t bits) +{ + uint32_t i; + + for (i = 0; i < bits; ++i) + buffer[i] = (val >> (bits - 1 - i)) & 1 ? '1' : '0'; + + buffer[i] = '\0'; + + return buffer; +} + +void dspp_device::dump_state() +{ + // DMA + for (uint32_t i = 0; i < NUM_DMA_CHANNELS; ++i) + { + printf("\n=== CHANNEL %02X ===\n", i); + printf("CURR_ADDRESS: %08X\n", m_fifo_dma[i].m_current_addr); + printf("CURR_COUNT: %08X\n", m_fifo_dma[i].m_current_count); + printf("NEXT_ADDR: %08X\n", m_fifo_dma[i].m_next_addr); + printf("NEXT_COUNT: %08X\n", m_fifo_dma[i].m_next_count); + printf("PREV_VALUE: %08X\n", m_fifo_dma[i].m_prev_value); + printf("PREV_CURRENT: %08X\n", m_fifo_dma[i].m_prev_current); + printf("GO_FOREVER: %X\n", m_fifo_dma[i].m_go_forever); + printf("NEXT_VALID: %X\n", m_fifo_dma[i].m_next_valid); + } + + char buffer[64]; + + printf("\n=== GLOBAL REGISTER===\n"); + printf("DSPX_CONTROL: %08X\n", m_dspx_control); + printf("DSPX_RESET: %08X\n", m_dspx_reset); + printf("DSPX_INT_ENABLE: %08X\n", m_dspx_int_enable); + printf("DSPX_CHANNEL_ENABLE: %08X %s\n", m_dspx_channel_enable, GetBinary(buffer, m_dspx_channel_enable, 32)); + printf("DSPX_CHANNEL_COMPLETE: %08X %s\n", m_dspx_channel_complete, GetBinary(buffer, m_dspx_channel_complete, 32)); + printf("DSPX_CHANNEL_DIRECTION: %08X %s\n", m_dspx_channel_direction, GetBinary(buffer, m_dspx_channel_direction, 32)); + printf("DSPX_CHANNEL_8BIT: %08X %s\n", m_dspx_channel_8bit, GetBinary(buffer, m_dspx_channel_8bit, 32)); + printf("DSPX_CHANNEL_SQXD: %08X %s\n", m_dspx_channel_sqxd, GetBinary(buffer, m_dspx_channel_sqxd, 32)); + +#if 0 + uint32_t m_dspx_shadow_current_addr; + uint32_t m_dspx_shadow_current_count; + uint32_t m_dspx_shadow_next_addr; + uint32_t m_dspx_shadow_next_count; + uint32_t m_dspx_dmanext_int; + uint32_t m_dspx_dmanext_enable; + uint32_t m_dspx_consumed_int; + uint32_t m_dspx_consumed_enable; + uint32_t m_dspx_underover_int; + uint32_t m_dspx_underover_enable; + uint32_t m_dspx_audio_time; + uint16_t m_dspx_audio_duration; +#endif +} diff --git a/src/devices/cpu/dspp/dspp.h b/src/devices/cpu/dspp/dspp.h new file mode 100644 index 00000000000..848c2931714 --- /dev/null +++ b/src/devices/cpu/dspp/dspp.h @@ -0,0 +1,337 @@ +// license:BSD-3-Clause +// copyright-holders:Philip Bennett +/*************************************************************************** + + dspp.h + + Core implementation for the portable DSPP emulator. + +***************************************************************************/ + +#pragma once + +#ifndef __DSPP_H__ +#define __DSPP_H__ + +#include "cpu/drcfe.h" +#include "cpu/drcuml.h" +#include "cpu/drcumlsh.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class dspp_frontend; + +// ======================> dspp_device + +class dspp_device : public cpu_device +{ + friend class dspp_frontend; +public: + // Construction/destruction + dspp_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, address_map_constructor code_map_ctor, + address_map_constructor data_map_ctor); + dspp_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // Static configuration helpers + auto int_handler() { return m_int_handler.bind(); } + auto dma_read_handler() { return m_dma_read_handler.bind(); } + auto dma_write_handler() { return m_dma_write_handler.bind(); } + + // Public interfaces + DECLARE_READ32_MEMBER( read ); + DECLARE_WRITE32_MEMBER( write ); + + uint16_t read_output_fifo(); + + void dump_state(); // TODO: DEBUG REMOVE ME + + // Internal registers + DECLARE_READ16_MEMBER( input_r ); + DECLARE_WRITE16_MEMBER( output_w ); + DECLARE_READ16_MEMBER( fifo_osc_r ); + DECLARE_WRITE16_MEMBER( fifo_osc_w ); + DECLARE_WRITE16_MEMBER( input_control_w ); + DECLARE_WRITE16_MEMBER( output_control_w ); + DECLARE_READ16_MEMBER( input_status_r ); + DECLARE_READ16_MEMBER( output_status_r ); + DECLARE_WRITE16_MEMBER( cpu_int_w ); + DECLARE_READ16_MEMBER( pc_r ); + DECLARE_WRITE16_MEMBER( pc_w ); + DECLARE_READ16_MEMBER( audlock_r ); + DECLARE_WRITE16_MEMBER( audlock_w ); + DECLARE_READ16_MEMBER( clock_r ); + DECLARE_WRITE16_MEMBER( clock_w ); + DECLARE_READ16_MEMBER( noise_r ); + +protected: + // device-level overrides + virtual void device_start() override; + virtual void device_reset() override; + + // device_execute_interface overrides + virtual uint32_t execute_min_cycles() const override; + virtual uint32_t execute_max_cycles() const override; + virtual void execute_run() override; + + // device_memory_interface overrides + virtual space_config_vector memory_space_config() const override; + + // device_state_interface overrides + virtual void state_import(const device_state_entry &entry) override; + virtual void state_export(const device_state_entry &entry) override; + virtual void state_string_export(const device_state_entry &entry, std::string &str) const override; + + // device_disasm_interface overrides + virtual std::unique_ptr<util::disasm_interface> create_disassembler() override; + + void code_map(address_map &map); + void data_map(address_map &map); + +private: + // Constants + static const uint32_t PC_STACK_DEPTH = 4; + static const uint32_t MAX_OPERANDS = 8; + + static const uint32_t NUM_DMA_CHANNELS = 32; + static const uint32_t DMA_FIFO_DEPTH = 8; + static const uint32_t DMA_FIFO_MASK = DMA_FIFO_DEPTH - 1; + + static const uint32_t NUM_INPUTS = 2; + static const uint32_t NUM_OUTPUTS = 8; + static const uint32_t OUTPUT_FIFO_DEPTH = 8; + static const uint32_t OUTPUT_FIFO_MASK = OUTPUT_FIFO_DEPTH - 1; + + // Handlers + devcb_write_line m_int_handler; + devcb_read8 m_dma_read_handler; + devcb_write8 m_dma_write_handler; + + // Internal functions + uint16_t read_op(offs_t pc); + inline uint16_t read_data(offs_t addr); + inline void write_data(offs_t addr, uint16_t data); + + inline void update_pc(); + inline void update_ticks(); + inline void exec_control(); + inline void exec_super_special(); + inline void exec_special(); + inline void exec_branch(); + inline void exec_complex_branch(); + inline void exec_arithmetic(); + void parse_operands(uint32_t numops); + uint16_t read_next_operand(); + void write_next_operand(uint16_t value); + inline void push_pc(); + inline uint16_t pop_pc(); + inline void set_rbase(uint32_t base, uint32_t addr); + inline uint16_t translate_reg(uint16_t reg); + + void update_fifo_dma(); + void process_next_dma(int32_t channel); + void service_input_dma(int32_t channel); + void service_output_dma(int32_t channel); + int16_t read_fifo_to_dspp(int32_t channel); + int16_t read_fifo_to_dma(int32_t channel); + void write_dma_to_fifo(int32_t channel, int16_t value); + void write_dspp_to_fifo(int32_t channel, int16_t value); + + void run_oscillator(int32_t channel); + void reset_channel(int32_t channel); + void advance_audio_timer(); + void advance_audio_frame(); + int16_t decode_sqxd(int8_t val, int16_t prev); + + uint32_t get_interrupt_state(); + void update_host_interrupt(); + + uint32_t read_dma_stack(offs_t offset); + void write_dma_stack(offs_t offset, uint32_t data); + + uint32_t read_ext_control(offs_t offset); + void write_ext_control(offs_t offset, uint32_t data); + + bool m_isdrc; + + // Address spaces + const address_space_config m_code_config; + const address_space_config m_data_config; + address_space * m_code; + address_space * m_data; + memory_access_cache<1, -1, ENDIANNESS_BIG> *m_code_cache; + std::function<const void * (offs_t)> m_codeptr; + + struct dspp_internal_state + { + // Internal state + int m_icount; + uint16_t m_pc; + uint16_t m_stack[PC_STACK_DEPTH]; + uint32_t m_stack_ptr; + uint16_t m_rbase[4]; + uint32_t m_acc; + uint32_t m_tclock; + + uint32_t m_flag_carry; + uint32_t m_flag_zero; + uint32_t m_flag_neg; + uint32_t m_flag_over; + uint32_t m_flag_exact; + uint32_t m_flag_audlock; + uint32_t m_flag_sleep; + + uint32_t m_partial_int; + uint16_t m_op; + uint32_t m_opidx; + int32_t m_writeback; + + const char *m_format; + uint32_t m_arg0; + } * m_core; + + struct + { + uint32_t value; + uint32_t addr; + } m_operands[MAX_OPERANDS]; + + // DMA + struct fifo_dma + { + uint32_t m_current_addr; + int32_t m_current_count; + uint32_t m_next_addr; + uint32_t m_next_count; + uint32_t m_prev_value; + uint32_t m_prev_current; + uint8_t m_go_forever; + uint8_t m_next_valid; + uint8_t m_reserved; + uint16_t m_fifo[DMA_FIFO_DEPTH]; + uint32_t m_dma_ptr; + uint32_t m_dspi_ptr; + uint32_t m_depth; + } m_fifo_dma[NUM_DMA_CHANNELS]; + + // Oscillator + uint32_t m_last_frame_clock; + uint32_t m_last_osc_count; + uint32_t m_osc_phase; + uint32_t m_osc_freq; + + // Output FIFO + uint16_t m_outputs[NUM_OUTPUTS]; + uint16_t m_output_fifo[OUTPUT_FIFO_DEPTH]; + uint32_t m_output_fifo_start; + uint32_t m_output_fifo_count; + + // External control registers + uint32_t m_dspx_control; + uint32_t m_dspx_reset; + uint32_t m_dspx_int_enable; + uint32_t m_dspx_channel_enable; + uint32_t m_dspx_channel_complete; + uint32_t m_dspx_channel_direction; + uint32_t m_dspx_channel_8bit; + uint32_t m_dspx_channel_sqxd; + uint32_t m_dspx_shadow_current_addr; + uint32_t m_dspx_shadow_current_count; + uint32_t m_dspx_shadow_next_addr; + uint32_t m_dspx_shadow_next_count; + uint32_t m_dspx_dmanext_int; + uint32_t m_dspx_dmanext_enable; + uint32_t m_dspx_consumed_int; + uint32_t m_dspx_consumed_enable; + uint32_t m_dspx_underover_int; + uint32_t m_dspx_underover_enable; + uint32_t m_dspx_audio_time; + uint16_t m_dspx_audio_duration; + + // + // DRC + // + + // Core state + /* internal stuff */ + bool m_cache_dirty; + drc_cache m_cache; + std::unique_ptr<drcuml_state> m_drcuml; + std::unique_ptr<dspp_frontend> m_drcfe; + uint32_t m_drcoptions; + + /* internal compiler state */ + struct compiler_state + { + uint32_t cycles; /* accumulated cycles */ + uint8_t checkints; /* need to check interrupts before next instruction */ + uint8_t checksoftints; /* need to check software interrupts before next instruction */ + uml::code_label labelnum; /* index for local labels */ + }; + +public: // TODO + void alloc_handle(drcuml_state *drcuml, uml::code_handle **handleptr, const char *name); + void load_fast_iregs(drcuml_block &block); + void save_fast_iregs(drcuml_block &block); +// void arm7_drc_init(); +// void arm7_drc_exit(); + void execute_run_drc(); +// void arm7drc_set_options(uint32_t options); +// void arm7drc_add_fastram(offs_t start, offs_t end, uint8_t readonly, void *base); +// void arm7drc_add_hotspot(offs_t pc, uint32_t opcode, uint32_t cycles); + void flush_cache(); + void compile_block(offs_t pc); + void cfunc_get_cycles(); + void cfunc_unimplemented(); + void static_generate_entry_point(); + void static_generate_nocode_handler(); + void static_generate_out_of_cycles(); + void static_generate_memory_accessor(int size, bool istlb, bool iswrite, const char *name, uml::code_handle **handleptr); + void generate_update_cycles(drcuml_block &block, compiler_state *compiler, uml::parameter param); + void generate_checksum_block(drcuml_block &block, compiler_state *compiler, const opcode_desc *seqhead, const opcode_desc *seqlast); + void generate_sequence_instruction(drcuml_block &block, compiler_state *compiler, const opcode_desc *desc); + + bool generate_opcode(drcuml_block &block, compiler_state *compiler, const opcode_desc *desc); + + + /* subroutines */ + uml::code_handle * m_entry; /* entry point */ + uml::code_handle * m_nocode; /* nocode exception handler */ + uml::code_handle * m_out_of_cycles; /* out of cycles exception handler */ +}; + + +/*************************************************************************** + COMPILER-SPECIFIC OPTIONS + ***************************************************************************/ + +#define DSPPDRC_STRICT_VERIFY 0x0001 /* verify all instructions */ +#define DSPPDRC_FLUSH_PC 0x0002 /* flush the PC value before each memory access */ + +#define DSPPDRC_COMPATIBLE_OPTIONS (DSPPDRC_STRICT_VERIFY | DSPPDRC_FLUSH_PC) +#define DSPPDRC_FASTEST_OPTIONS (0) + + + +/*************************************************************************** + DEVICE CONFIGURATION MACROS +***************************************************************************/ + +#define MCFG_DSPP_INT_HANDLER(_devcb) \ + devcb = &dspp_device::set_int_handler(*device, DEVCB_##_devcb); + +#define MCFG_DSPP_DMA_READ_HANDLER(_devcb) \ + devcb = &dspp_device::set_dma_read_handler(*device, DEVCB_##_devcb); + +#define MCFG_DSPP_DMA_WRITE_HANDLER(_devcb) \ + devcb = &dspp_device::set_dma_write_handler(*device, DEVCB_##_devcb); + + + +// device type definition +DECLARE_DEVICE_TYPE(DSPP, dspp_device); + + +#endif /* __DSPP_H__ */ diff --git a/src/devices/cpu/dspp/dsppdasm.cpp b/src/devices/cpu/dspp/dsppdasm.cpp new file mode 100644 index 00000000000..6b43df5d452 --- /dev/null +++ b/src/devices/cpu/dspp/dsppdasm.cpp @@ -0,0 +1,124 @@ +// license:BSD-3-Clause +// copyright-holders:Philip Bennett +/*************************************************************************** + + dsppdasm.c + Disassembler for the portable DSPP emulator. + +***************************************************************************/ + +#include "emu.h" +#include "dspp.h" +#include "dsppdasm.h" + + +/*************************************************************************** + CODE CODE +***************************************************************************/ + +uint32_t dspp_disassembler::opcode_alignment() const +{ + return 2; +} + +offs_t dspp_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) +{ +#if 0 + uint32_t op = oprom[0] | (oprom[1] << 8) | (oprom[2] << 16) | (oprom[3] << 24); + int opcode = op >> 27; + int cond = (op >> 21) & 1; + int rdst = (op >> 22) & 31; + int rsrc1 = (op >> 16) & 31; + int rsrc2 = op & 0xffff; + int rsrc2_iszero = (!rsrc2 || rsrc2 == 0xffe0); + uint32_t flags = 0; + + switch (opcode) + { + case 0x00: sprintf(buffer, "trap $00"); flags = DASMFLAG_STEP_OVER; break; + case 0x01: sprintf(buffer, "b%s $%08x", condition[rdst & 15], pc + ((INT32)(op << 10) >> 8)); break; + case 0x02: if ((op & 0x003fffff) == 3) + { + uint32_t nextop = oprom[4] | (oprom[5] << 8) | (oprom[6] << 16) | (oprom[7] << 24); + if ((nextop >> 27) == 0x10 && ((nextop >> 22) & 31) == rdst && (nextop & 0xffff) == 0) + { + uint32_t nextnextop = oprom[8] | (oprom[9] << 8) | (oprom[10] << 16) | (oprom[11] << 24); + sprintf(buffer, "llit%s $%08x,%s", setcond[cond], nextnextop, reg[rdst]); + return 12 | DASMFLAG_STEP_OVER | DASMFLAG_SUPPORTED; + } + } + if (rdst) + { + flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); + sprintf(buffer, "bsr %s,$%08x", reg[rdst], pc + ((INT32)(op << 10) >> 8)); + } + else + sprintf(buffer, "bra $%08x", pc + ((INT32)(op << 10) >> 8)); + break; + case 0x03: sprintf(buffer, "lea%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,2), reg[rdst]); break; + case 0x04: sprintf(buffer, "leah%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,1), reg[rdst]); break; + case 0x05: sprintf(buffer, "subr%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x06: sprintf(buffer, "xor%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x07: sprintf(buffer, "xorn%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x08: if (!rsrc1 && !rdst && rsrc2_iszero) + sprintf(buffer, "nop"); + else if (!rsrc1) + sprintf(buffer, "mov%s %s,%s", setcond[cond], src2(op,0), reg[rdst]); + else if (rsrc2_iszero) + sprintf(buffer, "mov%s %s,%s", setcond[cond], reg[rsrc1], reg[rdst]); + else + sprintf(buffer, "add%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x09: sprintf(buffer, "sub%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x0a: sprintf(buffer, "addc%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x0b: sprintf(buffer, "subc%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x0c: sprintf(buffer, "and%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x0d: sprintf(buffer, "andn%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x0e: if (!rsrc1 && !rdst && rsrc2_iszero) + sprintf(buffer, "nop"); + else if (!rsrc1) + sprintf(buffer, "mov%s %s,%s", setcond[cond], src2(op,0), reg[rdst]); + else if (rsrc2_iszero) + sprintf(buffer, "mov%s %s,%s", setcond[cond], reg[rsrc1], reg[rdst]); + else + sprintf(buffer, "or%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x0f: sprintf(buffer, "orn%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x10: sprintf(buffer, "ld%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,2), reg[rdst]); break; + case 0x11: sprintf(buffer, "ldh%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,1), reg[rdst]); break; + case 0x12: sprintf(buffer, "lduh%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,1), reg[rdst]); break; + case 0x13: sprintf(buffer, "sth%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,1), reg[rdst]); break; + case 0x14: sprintf(buffer, "st%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,2), reg[rdst]); break; + case 0x15: sprintf(buffer, "ldb%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x16: sprintf(buffer, "ldub%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x17: sprintf(buffer, "stb%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x18: sprintf(buffer, "ashr%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x19: sprintf(buffer, "lshr%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x1a: sprintf(buffer, "ashl%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x1b: sprintf(buffer, "rotl%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break; + case 0x1c: sprintf(buffer, "getps %s", reg[rdst]); break; + case 0x1d: sprintf(buffer, "putps %s", src2(op,0)); break; + case 0x1e: if (rdst && rsrc2_iszero) + { + flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); + sprintf(buffer, "jsr%s %s,%s", setcond[cond], reg[rdst], reg[rsrc1]); + } + else if (rdst) + { + flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); + sprintf(buffer, "jsr%s %s,%s[%s]", setcond[cond], reg[rdst], reg[rsrc1], src2(op,2)); + } + else if (rsrc2_iszero) + { + if (rsrc1 == 28) + flags = DASMFLAG_STEP_OUT; + sprintf(buffer, "jmp%s %s", setcond[cond], reg[rsrc1]); + } + else + sprintf(buffer, "jmp%s %s[%s]", setcond[cond], reg[rsrc1], src2(op,2)); + break; + case 0x1f: sprintf(buffer, "trap $1f"); flags = DASMFLAG_STEP_OVER; break; + } + + sprintf(buffer, "????"); +#endif + return 4 | 2 | SUPPORTED; +} diff --git a/src/devices/cpu/dspp/dsppdasm.h b/src/devices/cpu/dspp/dsppdasm.h new file mode 100644 index 00000000000..7f0c526e18a --- /dev/null +++ b/src/devices/cpu/dspp/dsppdasm.h @@ -0,0 +1,22 @@ +// license:BSD-3-Clause +// copyright-holders:Philip Bennett +/* + DSPP disassembler shim +*/ + +#ifndef MAME_CPU_DSPP_DSPPDASM_H +#define MAME_CPU_DSPP_DSPPDASM_H + +#pragma once + +class dspp_disassembler : public util::disasm_interface +{ +public: + dspp_disassembler() = default; + virtual ~dspp_disassembler() = default; + + virtual uint32_t opcode_alignment() const override; + virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer ¶ms) override; +}; + +#endif diff --git a/src/devices/cpu/dspp/dsppdrc.cpp b/src/devices/cpu/dspp/dsppdrc.cpp new file mode 100644 index 00000000000..6c1e60b4874 --- /dev/null +++ b/src/devices/cpu/dspp/dsppdrc.cpp @@ -0,0 +1,970 @@ +// license:BSD-3-Clause +// copyright-holders:Philip Bennett + +/****************************************************************************** + + DSPP UML recompiler core + +******************************************************************************/ + +#include "emu.h" +#include "debugger.h" +#include "dspp.h" +#include "dsppfe.h" +#include "cpu/drcfe.h" +#include "cpu/drcuml.h" +#include "cpu/drcumlsh.h" + +using namespace uml; + +#define USE_SWAPDQ 0 + + +// map variables +#define MAPVAR_PC M0 +#define MAPVAR_CYCLES M1 +#define MAPVAR_ACC M2 + +// exit codes +#define EXECUTE_OUT_OF_CYCLES 0 +#define EXECUTE_MISSING_CODE 1 +#define EXECUTE_UNMAPPED_CODE 2 +#define EXECUTE_RESET_CACHE 3 + +#define FLAG_C(reg) mem(&m_core->m_flag_carry) +#define FLAG_Z(reg) mem(&m_core->m_flag_zero) +#define FLAG_N(reg) mem(&m_core->m_flag_neg) +#define FLAG_V(reg) mem(&m_core->m_flag_over) +#define FLAG_X(reg) mem(&m_core->m_flag_exact) + +inline void dspp_device::alloc_handle(drcuml_state *drcuml, code_handle **handleptr, const char *name) +{ + if (*handleptr == nullptr) + *handleptr = drcuml->handle_alloc(name); +} + +static inline uint32_t epc(const opcode_desc *desc) +{ + return desc->pc; +} + + +#if 0 +static void cfunc_unimplemented(void *param) +{ + dspp_device *dspp = (dspp_device *)param; + dspp->cfunc_unimplemented(); +} +#endif + +void dspp_device::cfunc_unimplemented() +{ +// uint64_t op = m_core->m_arg0; +// fatalerror("PC=%08X: Unimplemented op %04X%08X\n", m_core->m_pc, (uint32_t)(op >> 32), (uint32_t)(op)); +} + + +/*------------------------------------------------- +load_fast_iregs - load any fast integer +registers +-------------------------------------------------*/ + +inline void dspp_device::load_fast_iregs(drcuml_block &block) +{ +#if 0 // TODO + for (uint32_t regnum = 0; regnum < ARRAY_LENGTH(m_regmap); regnum++) + { + if (m_regmap[regnum].is_int_register()) + { + UML_MOV(block, ireg(m_regmap[regnum].ireg() - REG_I0), mem(&m_core->r[regnum])); + } + } +#endif +} + + +/*------------------------------------------------- +save_fast_iregs - save any fast integer +registers +-------------------------------------------------*/ + +void dspp_device::save_fast_iregs(drcuml_block &block) +{ +#if 0 // TODO + int regnum; + + for (regnum = 0; regnum < ARRAY_LENGTH(m_regmap); regnum++) + { + if (m_regmap[regnum].is_int_register()) + { + UML_MOV(block, mem(&m_core->r[regnum]), ireg(m_regmap[regnum].ireg() - REG_I0)); + } + } +#endif +} + +#if 0 +void dspp_device::static_generate_memory_accessor(MEM_ACCESSOR_TYPE type, const char *name, code_handle *&handleptr) +{ + // I0 = read/write data + // I1 = address + + drcuml_block &block = m_drcuml->begin_block(1024); + + // add a global entry for this + alloc_handle(m_drcuml.get(), &handleptr, name); + UML_HANDLE(block, *handleptr); // handle *handleptr + + switch (type) + { + case MEM_ACCESSOR_PM_READ48: + UML_SHL(block, I1, I1, 3); + UML_DREAD(block, I0, I1, SIZE_QWORD, SPACE_PROGRAM); + break; + + case MEM_ACCESSOR_PM_WRITE48: + UML_SHL(block, I1, I1, 3); + UML_DWRITE(block, I1, I0, SIZE_QWORD, SPACE_PROGRAM); + UML_MOV(block, mem(&m_core->force_recompile), 1); + break; + + case MEM_ACCESSOR_PM_READ32: + UML_SHL(block, I1, I1, 3); + UML_READ(block, I0, I1, SIZE_DWORD, SPACE_PROGRAM); + break; + + case MEM_ACCESSOR_PM_WRITE32: + UML_SHL(block, I1, I1, 3); + UML_WRITE(block, I1, I0, SIZE_DWORD, SPACE_PROGRAM); + UML_MOV(block, mem(&m_core->force_recompile), 1); + break; + + case MEM_ACCESSOR_DM_READ32: + UML_SHL(block, I1, I1, 2); + UML_READ(block, I0, I1, SIZE_DWORD, SPACE_DATA); + break; + + case MEM_ACCESSOR_DM_WRITE32: + UML_SHL(block, I1, I1, 2); + UML_WRITE(block, I1, I0, SIZE_DWORD, SPACE_DATA); + break; + } + + UML_RET(block); + + block.end(); +} +#endif + + +void dspp_device::execute_run_drc() +{ + drcuml_state *drcuml = m_drcuml.get(); + int execute_result; + + if (m_cache_dirty) + flush_cache(); + + m_cache_dirty = false; + + do + { + execute_result = drcuml->execute(*m_entry); + + /* if we need to recompile, do it */ + if (execute_result == EXECUTE_MISSING_CODE) + { + compile_block(m_core->m_pc); + } + else if (execute_result == EXECUTE_UNMAPPED_CODE) + { + fatalerror("Attempted to execute unmapped code at PC=%08X\n", m_core->m_pc); + } + else if (execute_result == EXECUTE_RESET_CACHE) + { + flush_cache(); + } + } while (execute_result != EXECUTE_OUT_OF_CYCLES); +} + +void dspp_device::compile_block(offs_t pc) +{ + drcuml_state *drcuml = m_drcuml.get(); + compiler_state compiler = { 0 }; + const opcode_desc *seqhead, *seqlast; + const opcode_desc *desclist; + int override = false; + + g_profiler.start(PROFILER_DRC_COMPILE); + + /* get a description of this sequence */ + desclist = m_drcfe->describe_code(pc); + + bool succeeded = false; + while (!succeeded) + { + try + { + /* start the block */ + drcuml_block &block = drcuml->begin_block(4096); + + /* loop until we get through all instruction sequences */ + for (seqhead = desclist; seqhead != nullptr; seqhead = seqlast->next()) + { + const opcode_desc *curdesc; + uint32_t nextpc; + + /* add a code log entry */ + if (drcuml->logging()) + block.append_comment("-------------------------"); // comment + + /* determine the last instruction in this sequence */ + for (seqlast = seqhead; seqlast != nullptr; seqlast = seqlast->next()) + if (seqlast->flags & OPFLAG_END_SEQUENCE) + break; + assert(seqlast != nullptr); + + /* if we don't have a hash for this mode/pc, or if we are overriding all, add one */ + if (override || !drcuml->hash_exists(0, seqhead->pc)) + UML_HASH(block, 0, seqhead->pc); // hash mode,pc + + /* if we already have a hash, and this is the first sequence, assume that we */ + /* are recompiling due to being out of sync and allow future overrides */ + else if (seqhead == desclist) + { + override = true; + UML_HASH(block, 0, seqhead->pc); // hash mode,pc + } + + /* otherwise, redispatch to that fixed PC and skip the rest of the processing */ + else + { + UML_LABEL(block, seqhead->pc | 0x80000000); // label seqhead->pc + UML_HASHJMP(block, 0, seqhead->pc, *m_nocode); + // hashjmp <0>,seqhead->pc,nocode + continue; + } + + generate_checksum_block(block, &compiler, seqhead, seqlast); + + /* label this instruction, if it may be jumped to locally */ + if (seqhead->flags & OPFLAG_IS_BRANCH_TARGET) + UML_LABEL(block, seqhead->pc | 0x80000000); // label seqhead->pc + + /* iterate over instructions in the sequence and compile them */ + for (curdesc = seqhead; curdesc != seqlast->next(); curdesc = curdesc->next()) + generate_sequence_instruction(block, &compiler, curdesc); + + /* if we need to return to the start, do it */ + if (seqlast->flags & OPFLAG_RETURN_TO_START) + nextpc = pc; + + /* otherwise we just go to the next instruction */ + else + nextpc = seqlast->pc + (seqlast->skipslots + 1) * 4; + + /* count off cycles and go there */ + generate_update_cycles(block, &compiler, nextpc); // <subtract cycles> + + /* if the last instruction can change modes, use a variable mode; otherwise, assume the same mode */ + if (seqlast->next() == nullptr || seqlast->next()->pc != nextpc) + UML_HASHJMP(block, 0, nextpc, *m_nocode); // hashjmp <mode>,nextpc,nocode + } + + /* end the sequence */ + block.end(); + g_profiler.stop(); + succeeded = true; + } + catch (drcuml_block::abort_compilation &) + { + flush_cache(); + } + } +} + +void dspp_device::generate_checksum_block(drcuml_block &block, compiler_state *compiler, const opcode_desc *seqhead, const opcode_desc *seqlast) +{ + const opcode_desc *curdesc; + if (m_drcuml->logging()) + block.append_comment("[Validation for %08X]", seqhead->pc); // comment + + /* loose verify or single instruction: just compare and fail */ + if (!(m_drcoptions & DSPPDRC_STRICT_VERIFY) || seqhead->next() == nullptr) + { + if (!(seqhead->flags & OPFLAG_VIRTUAL_NOOP)) + { + uint32_t sum = seqhead->opptr.l[0]; + uint32_t addr = seqhead->physpc; + const void *base = m_codeptr(addr); + UML_LOAD(block, I0, base, 0, SIZE_DWORD, SCALE_x4); // load i0,base,0,dword + + if (seqhead->delay.first() != nullptr && seqhead->physpc != seqhead->delay.first()->physpc) + { + addr = seqhead->delay.first()->physpc; + base = m_codeptr(addr); + assert(base != nullptr); + UML_LOAD(block, I1, base, 0, SIZE_DWORD, SCALE_x4); // load i1,base,dword + UML_ADD(block, I0, I0, I1); // add i0,i0,i1 + + sum += seqhead->delay.first()->opptr.l[0]; + } + + UML_CMP(block, I0, sum); // cmp i0,opptr[0] + UML_EXHc(block, COND_NE, *m_nocode, epc(seqhead)); // exne nocode,seqhead->pc + } + } + + /* full verification; sum up everything */ + else + { +#if 0 + for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next()) + if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP)) + { + void *base = m_code_direct->read_ptr(seqhead->physpc); + UML_LOAD(block, I0, base, 0, SIZE_DWORD, SCALE_x4); // load i0,base,0,dword + UML_CMP(block, I0, curdesc->opptr.l[0]); // cmp i0,opptr[0] + UML_EXHc(block, COND_NE, *m_nocode, epc(seqhead)); // exne nocode,seqhead->pc + } +#else + uint32_t sum = 0; + uint32_t addr = seqhead->physpc; + const void *base = m_codeptr(addr); + UML_LOAD(block, I0, base, 0, SIZE_DWORD, SCALE_x4); // load i0,base,0,dword + sum += seqhead->opptr.l[0]; + for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next()) + if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP)) + { + addr = curdesc->physpc; + base = m_codeptr(addr); + assert(base != nullptr); + UML_LOAD(block, I1, base, 0, SIZE_DWORD, SCALE_x4); // load i1,base,dword + UML_ADD(block, I0, I0, I1); // add i0,i0,i1 + sum += curdesc->opptr.l[0]; + + if (curdesc->delay.first() != nullptr && (curdesc == seqlast || (curdesc->next() != nullptr && curdesc->next()->physpc != curdesc->delay.first()->physpc))) + { + addr = curdesc->delay.first()->physpc; + base = m_codeptr(addr); + assert(base != nullptr); + UML_LOAD(block, I1, base, 0, SIZE_DWORD, SCALE_x4); // load i1,base,dword + UML_ADD(block, I0, I0, I1); // add i0,i0,i1 + sum += curdesc->delay.first()->opptr.l[0]; + } + } + UML_CMP(block, I0, sum); // cmp i0,sum + UML_EXHc(block, COND_NE, *m_nocode, epc(seqhead)); // exne nocode,seqhead->pc +#endif + } +} + + +void dspp_device::flush_cache() +{ + /* empty the transient cache contents */ + m_drcuml->reset(); + + try + { + // generate the entry point and out-of-cycles handlers + static_generate_entry_point(); + static_generate_nocode_handler(); + static_generate_out_of_cycles(); + +#if 0 + // generate utility functions + static_generate_push_pc(); + static_generate_pop_pc(); + static_generate_push_loop(); + static_generate_pop_loop(); + static_generate_push_status(); + static_generate_pop_status(); + static_generate_mode1_ops(); + + // generate exception handlers + static_generate_exception(EXCEPTION_INTERRUPT, "exception_interrupt"); + + // generate memory accessors + static_generate_memory_accessor(MEM_ACCESSOR_PM_READ48, "pm_read48", m_pm_read48); + static_generate_memory_accessor(MEM_ACCESSOR_PM_WRITE48, "pm_write48", m_pm_write48); + static_generate_memory_accessor(MEM_ACCESSOR_PM_READ32, "pm_read32", m_pm_read32); + static_generate_memory_accessor(MEM_ACCESSOR_PM_WRITE32, "pm_write32", m_pm_write32); + static_generate_memory_accessor(MEM_ACCESSOR_DM_READ32, "dm_read32", m_dm_read32); + static_generate_memory_accessor(MEM_ACCESSOR_DM_WRITE32, "dm_write32", m_dm_write32); +#endif + } + catch (drcuml_block::abort_compilation &) + { + fatalerror("Error generating dspp static handlers\n"); + } +} + + +void dspp_device::static_generate_entry_point() +{ + /* begin generating */ + drcuml_block &block = m_drcuml->begin_block(20); + + /* forward references */ + alloc_handle(m_drcuml.get(), &m_nocode, "nocode"); +// alloc_handle(m_drcuml.get(), &m_exception[EXCEPTION_INTERRUPT], "exception_interrupt"); + + alloc_handle(m_drcuml.get(), &m_entry, "entry"); + UML_HANDLE(block, *m_entry); // handle entry + + load_fast_iregs(block); // <load fastregs> + +#if 0 // TODO: No interrupts? + /* check for interrupts */ + UML_CMP(block, mem(&m_core->irq_pending), 0); // cmp [irq_pending],0 + UML_JMPc(block, COND_E, skip); // je skip + UML_CMP(block, mem(&m_core->interrupt_active), 0); // cmp [interrupt_active],0 + UML_JMPc(block, COND_NE, skip); // jne skip + UML_TEST(block, mem(&m_core->irq_pending), IMASK); // test [irq_pending],IMASK + UML_JMPc(block, COND_Z, skip); // jz skip + UML_TEST(block, mem(&m_core->mode1), MODE1_IRPTEN); // test MODE1,MODE1_IRPTEN + UML_JMPc(block, COND_Z, skip); // jz skip +#endif + +// UML_MOV(block, I0, mem(&m_core->m_pc)); // mov i0,nextpc +// UML_MOV(block, I1, 0); // mov i1,0 +// UML_CALLH(block, *m_exception[EXCEPTION_INTERRUPT]); // callh m_exception[EXCEPTION_INTERRUPT] + +// UML_LABEL(block, skip); + + /* generate a hash jump via the current mode and PC */ + UML_HASHJMP(block, 0, mem(&m_core->m_pc), *m_nocode); // hashjmp <mode>,<pc>,nocode + + block.end(); +} + + +void dspp_device::static_generate_nocode_handler() +{ + /* begin generating */ + drcuml_block &block = m_drcuml->begin_block(10); + + /* generate a hash jump via the current mode and PC */ + alloc_handle(m_drcuml.get(), &m_nocode, "nocode"); + UML_HANDLE(block, *m_nocode); // handle nocode + UML_GETEXP(block, I0); // getexp i0 + UML_MOV(block, mem(&m_core->m_pc), I0); // mov [pc],i0 + save_fast_iregs(block); // <save fastregs> + UML_EXIT(block, EXECUTE_MISSING_CODE); // exit EXECUTE_MISSING_CODE + + block.end(); +} + +void dspp_device::static_generate_out_of_cycles() +{ + /* begin generating */ + drcuml_block &block = m_drcuml->begin_block(10); + + /* generate a hash jump via the current mode and PC */ + alloc_handle(m_drcuml.get(), &m_out_of_cycles, "out_of_cycles"); + UML_HANDLE(block, *m_out_of_cycles); // handle out_of_cycles + UML_GETEXP(block, I0); // getexp i0 + UML_MOV(block, mem(&m_core->m_pc), I0); // mov <pc>,i0 + save_fast_iregs(block); // <save fastregs> + UML_EXIT(block, EXECUTE_OUT_OF_CYCLES); // exit EXECUTE_OUT_OF_CYCLES + + block.end(); +} + + +void dspp_device::generate_sequence_instruction(drcuml_block &block, compiler_state *compiler, const opcode_desc *desc) +{ + /* add an entry for the log */ +// if (m_drcuml->logging() && !(desc->flags & OPFLAG_VIRTUAL_NOOP)) +// log_add_disasm_comment(block, desc->pc, desc->opptr.l[0]); + + /* set the PC map variable */ + UML_MAPVAR(block, MAPVAR_PC, desc->pc); // mapvar PC,desc->pc + + /* accumulate total cycles */ + compiler->cycles += desc->cycles; + + /* update the icount map variable */ + UML_MAPVAR(block, MAPVAR_CYCLES, compiler->cycles); // mapvar CYCLES,compiler->cycles + + /* if we are debugging, call the debugger */ + if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0) + { + UML_MOV(block, mem(&m_core->m_pc), desc->pc); // mov [pc],desc->pc + save_fast_iregs(block); // <save fastregs> + UML_DEBUG(block, desc->pc); // debug desc->pc + } + + /* if we hit an unmapped address, fatal error */ + if (desc->flags & OPFLAG_COMPILER_UNMAPPED) + { + UML_MOV(block, mem(&m_core->m_pc), desc->pc); // mov [pc],desc->pc + save_fast_iregs(block); // <save fastregs> + UML_EXIT(block, EXECUTE_UNMAPPED_CODE); // exit EXECUTE_UNMAPPED_CODE + } + + /* if this is an invalid opcode, generate the exception now */ +// if (desc->flags & OPFLAG_INVALID_OPCODE) +// UML_EXH(block, *m_exception[EXCEPTION_PROGRAM], 0x80000); // exh exception_program,0x80000 + + /* unless this is a virtual no-op, it's a regular instruction */ + if (!(desc->flags & OPFLAG_VIRTUAL_NOOP)) + { + /* compile the instruction */ + //if (!generate_opcode(block, compiler, desc)) + { + UML_MOV(block, mem(&m_core->m_pc), desc->pc); // mov [pc],desc->pc + UML_DMOV(block, mem(&m_core->m_arg0), desc->opptr.q[0]); // dmov [m_arg0],*desc->opptr.q // FIXME +// UML_CALLC(block, cfunc_unimplemented, this); // callc cfunc_unimplemented,ppc + } + } +} + +void dspp_device::generate_update_cycles(drcuml_block &block, compiler_state *compiler, uml::parameter param) +{ +#if 0 // No interrupts + if (compiler->checkints) + { + code_label skip = compiler->labelnum++; + compiler->checkints = false; + + UML_CMP(block, mem(&m_core->irq_pending), 0); // cmp [irq_pending],0 + UML_JMPc(block, COND_E, skip); // je skip + UML_CMP(block, mem(&m_core->interrupt_active), 0); // cmp [interrupt_active],0 + UML_JMPc(block, COND_NE, skip); // jne skip + UML_TEST(block, mem(&m_core->irq_pending), IMASK); // test [irq_pending],IMASK + UML_JMPc(block, COND_Z, skip); // jz skip + UML_TEST(block, mem(&m_core->mode1), MODE1_IRPTEN); // test MODE1,MODE1_IRPTEN + UML_JMPc(block, COND_Z, skip); // jz skip + + UML_MOV(block, I0, param); // mov i0,nextpc + UML_MOV(block, I1, compiler->cycles); // mov i1,cycles + UML_CALLH(block, *m_exception[EXCEPTION_INTERRUPT]); // callh m_exception[EXCEPTION_INTERRUPT] + + UML_LABEL(block, skip); + } +#endif + + /* account for cycles */ + if (compiler->cycles > 0) + { + UML_SUB(block, mem(&m_core->m_icount), mem(&m_core->m_icount), MAPVAR_CYCLES); // sub icount,icount,cycles + UML_MAPVAR(block, MAPVAR_CYCLES, 0); // mapvar cycles,0 +#if 0 // FIXME + if (allow_exception) + UML_EXHc(block, COND_S, *m_out_of_cycles, param); // exh out_of_cycles,nextpc +#endif + } + compiler->cycles = 0; +} + +#if 0 +bool dspp_device::generate_opcode(drcuml_block &block, compiler_state *compiler, const opcode_desc *desc) +{ + uint32_t op = desc->opptr.l[0]; + + if (op & 0x8000) + { + switch ((op >> 13) & 3) + { + case 0: + return generate_special_opcode(op, desc); + + case 1: + case 2: + return generate_branch_opcode(op, desc); + + case 3: + return generate_complex_branch_opcode(op, desc); + } + + return false; + } + else + { + return generate_arithmetic_opcode(op, desc); + } + + return false; +} + +bool dspp_device::generate_special_opcode(drcuml_block &block, compiler_state *compiler, const opcode_desc *desc) +{ + return true; +} + +bool dspp_device::generate_branch_opcode(drcuml_block &block, compiler_state *compiler, const opcode_desc *desc) +{ + uint32_t mode = (m_core->m_op >> 13) & 3; + uint32_t select = (m_core->m_op >> 12) & 1; + uint32_t mask = (m_core->m_op >> 10) & 3; + + bool flag0, flag1; + + if (select == 0) + { + flag0 = (m_core->m_flags & DSPI_FLAG_CC_NEG) != 0; + flag1 = (m_core->m_flags & DSPI_FLAG_CC_OVER) != 0; + } + else + { + flag0 = (m_core->m_flags & DSPI_FLAG_CC_CARRY) != 0; + flag1 = (m_core->m_flags & DSPI_FLAG_CC_ZERO) != 0; + } + + bool mask0 = (mask & 2) != 0; + bool mask1 = (mask & 1) != 0; + + bool branch = (flag0 || !mask0) && (flag1 || !mask1); + + if (mode == 2) + branch = !branch; + + if (branch) + m_core->m_pc = m_core->m_op & 0x3ff; + + return true; +} + + +void adsp21062_device::generate_jump(drcuml_block &block, compiler_state *compiler, const opcode_desc *desc, bool delayslot, bool loopabort, bool clearint) +{ + compiler_state compiler_temp = *compiler; + + // save branch target + if (desc->targetpc == BRANCH_TARGET_DYNAMIC) + { + UML_MOV(block, mem(&m_core->jmpdest), I0); // mov [jmpdest],i0 + } + + // update cycles and hash jump + if (desc->targetpc != BRANCH_TARGET_DYNAMIC) + { + generate_update_cycles(block, &compiler_temp, desc->targetpc, true); + if (desc->flags & OPFLAG_INTRABLOCK_BRANCH) + UML_JMP(block, desc->targetpc | 0x80000000); // jmp targetpc | 0x80000000 + else + UML_HASHJMP(block, 0, desc->targetpc, *m_nocode); // hashjmp 0,targetpc,nocode + } + else + { + generate_update_cycles(block, &compiler_temp, mem(&m_core->jmpdest), true); + UML_HASHJMP(block, 0, mem(&m_core->jmpdest), *m_nocode); // hashjmp 0,jmpdest,nocode + } + + // update compiler label + compiler->labelnum = compiler_temp.labelnum; + + /* reset the mapvar to the current cycles and account for skipped slots */ +// compiler->cycles += desc->skipslots; + UML_MAPVAR(block, MAPVAR_CYCLES, compiler->cycles); // mapvar CYCLES,compiler->cycles +} + +bool dspp_device::generate_complex_branch_opcode(drcuml_block &block, compiler_state *compiler, const opcode_desc *desc) +{ + uint32_t op = desc->opptr.l[0]; + + code_label skip_label = compiler->labelnum++; + +// generate_branch_target(block, compiler, desc, op & 0x3ff, ef2); +// generate_condition(block, compiler, desc, ef1, true, skip_label, true); + generate_branch(block, compiler, desc); + UML_LABEL(block, skip_label); + + switch ((op >> 10) & 7) + { + case 0: // BLT + // branch = (n && !v) || (!n && v); + UML_XOR(I0, FLAG_N, 1); + UML_XOR(I1, FLAG_V, 1); + UML_AND(I0, I0, FLAG_V); + UML_AND(I1, FLAG_N, I1); + UML_OR(I0, I1, I2); + UML_CMP(block, I0, 1); + break; + case 1: // BLE + //branch = ((n && !v) || (!n && v)) || z; + break; + case 2: // BGE + //branch = ((n && v) || (!n && !v)); + break; + case 3: // BGT + //branch = ((n && v) || (!n && !v)) && !z; + UML_AND(I2, FLAG_N, FLAG_V); + UML_XOR(I0, FLAG_N, 1); + UML_XOR(I1, FLAG_V, 1); + UML_AND(I0, I0, I1); + UML_OR(I0, I2, I0); + UML_XOR(I1, FLAG_Z, 1); + UML_AND(I0, I0, I1); + UML_CMP(I0, 1); + break; + case 4: // BHI + //branch = c && !z; + UML_XOR(I0, FLAG_Z, 1) + UML_AND(I0, FLAG_C, I0); + UML_CMP(I0, 1); + break; + case 5: // BLS + //branch = !c || z; + UML_XOR(I0, FLAG_C, 1); + UML_OR(I0, I0, FLAG_Z); + UML_CMP(block, I0, 1); + break; + case 6: // BXS + //branch = x; + UML_CMP(block, FLAG_X, 1); + break; + case 7: // BXC + //branch = !x; + UML_CMP(block, FLAG_X, 0); + break; + } + + UML_JMPc(block, COND_E, skip_label); + + return true; +} + +bool dspp_device::generate_arithmetic_opcode(drcuml_block &block, compiler_state *compiler, const opcode_desc *desc) +{ + uint32_t numops = (m_core->m_op >> 13) & 3; + uint32_t muxa = (m_core->m_op >> 10) & 3; + uint32_t muxb = (m_core->m_op >> 8) & 3; + uint32_t alu_op = (m_core->m_op >> 4) & 0xf; + uint32_t barrel_code = m_core->m_op & 0xf; + + int32_t mul_res = 0; + uint32_t alu_res = 0; + + // Check for operand overflow + if (numops == 0 && ((muxa == 1) || (muxa == 2) || (muxb == 1) || (muxb == 2))) + numops = 4; + + // Implicit barrel shift + if (barrel_code == 8) + ++numops; + + // Parse ops... + parse_operands(numops); + + if (muxa == 3 || muxb == 3) + { + uint32_t mul_sel = (m_core->m_op >> 12) & 1; + + int32_t op1 = sign_extend16(read_next_operand()); + int32_t op2 = sign_extend16(mul_sel ? read_next_operand() : m_core->m_acc >> 4); + + mul_res = (op1 * op2) >> 11; + +#if 0 + // SELECT + UML_DSEXT(block, I0, OP1, SIZE_WORD); + UML_DSEXT(block, I1, OP1, SIZE_WORD); + UML_MULS(block, I0, I1, I0, I1); + UML_SHR(block, I2, I0, 11); +#endif + } + + // MULRES = I2 + // MUXA = I0 + // MUXB = I1 + + // ALURES = I2 + // ACC_RESULT = I1? + switch (alu_op) + { + case 0: // _TRA + // alu_res = alu_a; + UML_MOV(block, I2, I0); + break; + + case 1: // _NEG + // alu_res = -alu_b; + UML_SUB(block, I2, 0, I1); + break; + + case 2: // _+ + // alu_res = alu_a + alu_b; + UML_ADD(block, I2, I0, I1); + + // if ((alu_a & 0x80000) == (alu_b & 0x80000) && + // (alu_a & 0x80000) != (alu_res & 0x80000)) + // m_core->m_flags |= DSPI_FLAG_CC_OVER; + + // if (alu_res & 0x00100000) + // m_core->m_flags |= DSPI_FLAG_CC_CARRY; + + // CC_V_MODIFIED(desc); + // CC_C_MODIFIED(desc); + break; + + case 3: // _+C + UML_ADD(block, I2, I0, mew); + // alu_res = alu_a + (m_core->m_flags & DSPI_FLAG_CC_CARRY) ? (1 << 4) : 0; + + // if (alu_res & 0x00100000) + // m_core->m_flags |= DSPI_FLAG_CC_CARRY; + + CC_C_USED(desc); + CC_C_MODIFIED(desc); + break; + + case 4: // _- + // alu_res = alu_a - alu_b; + UML_SUB(block, I2, I0, I1); + + // if ((alu_a & 0x80000) == (~alu_b & 0x80000) && + // (alu_a & 0x80000) != (alu_res & 0x80000)) + // m_core->m_flags |= DSPI_FLAG_CC_OVER; + + // if (alu_res & 0x00100000) + // m_core->m_flags |= DSPI_FLAG_CC_CARRY; + + CC_C_MODIFIED(desc); + CC_V_MODIFIED(desc); + break; + + case 5: // _-B + // alu_res = alu_a - (m_core->m_flags & DSPI_FLAG_CC_CARRY) ? (1 << 4) : 0; + + // if (alu_res & 0x00100000) + // m_core->m_flags |= DSPI_FLAG_CC_CARRY; + + CC_C_USED(desc); + CC_C_MODIFIED(desc); + break; + + case 6: // _++ + UML_ADD(block, I2, I0, 1); + // alu_res = alu_a + 1; + + // if (!(alu_a & 0x80000) && (alu_res & 0x80000)) + // m_core->m_flags |= DSPI_FLAG_CC_OVER; + + CC_V_MODIFIED(desc); + break; + + case 7: // _-- + // alu_res = alu_a - 1; + UML_SUB(block, I2, I0, 1); + + // if ((alu_a & 0x80000) && !(alu_res & 0x80000)) + // m_core->m_flags |= DSPI_FLAG_CC_OVER; + + CC_V_MODIFIED(desc); + break; + + case 8: // _TRL + //alu_res = alu_a; + UML_MOV(block, I2, I0); + break; + + case 9: // _NOT + //alu_res = ~alu_a; + UML_XOR(block, I2, I0, 0xffff); + break; + + case 10: // _AND + //alu_res = alu_a & alu_b; + UML_AND(block, I2, I0, I1); + break; + + case 11: // _NAND + //alu_res = ~(alu_a & alu_b); + UML_AND(block, I2, I0, I1); + UML_XOR(block, I2, 0xffff); + break; + + case 12: // _OR + //alu_res = alu_a | alu_b; + UML_OR(block, I2, I0, I1); + break; + + case 13: // _NOR + //alu_res = ~(alu_a | alu_b); + UML_OR(block, I2, I0, I1); + UML_XOR(block, I2, I2, 0xffff); + break; + + case 14: // _XOR + //alu_res = alu_a ^ alu_b; + UML_XOR(block, I2, I0, I1); + break; + + case 15: // _XNOR + //alu_res = ~(alu_a ^ alu_b); + UML_XOR(block, I2, I0, I1); + UML_XOR(block, I2, I0, 0xffff); + break; + } + + // SET FLAGS + CC_SET_NEG(alu_res & 0x00080000); + CC_SET_ZERO((alu_res & 0x000ffff0) == 0); + CC_SET_EXACT((alu_res & 0x0000000f) == 0); + + // Barrel shift + static const int32_t shifts[8] = { 0, 1, 2, 3, 4, 5, 8, 16 }; + + if (barrel_code == 8) + barrel_code = read_next_operand(); + + if (barrel_code & 8) + { + // Right shift + uint32_t shift = shifts[(~barrel_code + 1) & 7]; + + if (alu_op < 8) + { + // Arithmetic +// m_core->m_acc = sign_extend20(alu_res) >> shift; + + // TODO: Sign Extend to 20-bits + UML_SHR(block, I2, I2, shift); + UML_MAPVAR(block, MAPVAR_ACC, I2); + } + else + { + // Logical +// m_core->m_acc = (alu_res & 0xfffff) >> shift; + UML_AND(block, I2, 0xfffff); + UML_SHR(block, I2, I2, shift); + UML_MAPVAR(block, MAPVAR_ACC, I2); + } + + } + else + { + // Left shift + uint32_t shift = shifts[barrel_code]; + + if (shift == 16) + { + // Clip and saturate + if (m_core->m_flags & DSPI_FLAG_CC_OVER) + m_core->m_acc = (m_core->m_flags & DSPI_FLAG_CC_NEG) ? 0x7ffff : 0xfff80000; + else + m_core->m_acc = sign_extend20(alu_res); + } + else + { + m_core->m_acc = sign_extend20(alu_res) << shift; + } + } + + if (m_core->m_writeback >= 0) + { + write_data(m_core->m_writeback, m_core->m_acc >> 4); + m_core->m_writeback = -1; + } + else if (m_core->m_opidx < numops) + { + write_next_operand(m_core->m_acc >> 4); + } + + return true; +} + +#endif diff --git a/src/devices/cpu/dspp/dsppfe.cpp b/src/devices/cpu/dspp/dsppfe.cpp new file mode 100644 index 00000000000..533a020ab89 --- /dev/null +++ b/src/devices/cpu/dspp/dsppfe.cpp @@ -0,0 +1,625 @@ +// license:BSD-3-Clause +// copyright-holders:Philip Bennett + +/****************************************************************************** + + Front-end for DSPP recompiler + +******************************************************************************/ + +#include "emu.h" +#include "dsppfe.h" + + +//#define REG_USED(desc,x) do { (desc).regin[0] |= 1 << (x); } while(0) +//#define REG_MODIFIED(desc,x) do { (desc).regout[0] |= 1 << (x); } while(0) + +#define CC_C_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0) +#define CC_C_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0) +#define CC_Z_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0) +#define CC_Z_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0) +#define CC_N_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0) +#define CC_N_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0) +#define CC_V_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0) +#define CC_V_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0) +#define CC_X_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0) +#define CC_X_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0) + +#define CC_FLAGS_MODIFIED(desc) do { } while(0) +//#define MULT_FLAGS_MODIFIED(desc) do { MN_MODIFIED(desc);MV_MODIFIED(desc);MU_MODIFIED(desc);MI_MODIFIED(desc); } while(0) +//#define SHIFT_FLAGS_MODIFIED(desc) do { SZ_MODIFIED(desc);SV_MODIFIED(desc);SS_MODIFIED(desc); } while(0) + +dspp_frontend::dspp_frontend(dspp_device *dspp, uint32_t window_start, uint32_t window_end, uint32_t max_sequence) + : drc_frontend(*dspp, window_start, window_end, max_sequence), + m_dspp(dspp) +{ + +} + + +#if 0 +// opcode branch flags +const uint32_t OPFLAG_IS_UNCONDITIONAL_BRANCH = 0x00000001; // instruction is unconditional branch +const uint32_t OPFLAG_IS_CONDITIONAL_BRANCH = 0x00000002; // instruction is conditional branch +const uint32_t OPFLAG_IS_BRANCH = (OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_IS_CONDITIONAL_BRANCH); +const uint32_t OPFLAG_IS_BRANCH_TARGET = 0x00000004; // instruction is the target of a branch +const uint32_t OPFLAG_IN_DELAY_SLOT = 0x00000008; // instruction is in the delay slot of a branch +const uint32_t OPFLAG_INTRABLOCK_BRANCH = 0x00000010; // instruction branches within the block + +// opcode exception flags +const uint32_t OPFLAG_CAN_TRIGGER_SW_INT = 0x00000020; // instruction can trigger a software interrupt +const uint32_t OPFLAG_CAN_EXPOSE_EXTERNAL_INT = 0x00000040; // instruction can expose an external interrupt +const uint32_t OPFLAG_CAN_CAUSE_EXCEPTION = 0x00000080; // instruction may generate exception +const uint32_t OPFLAG_WILL_CAUSE_EXCEPTION = 0x00000100; // instruction will generate exception +const uint32_t OPFLAG_PRIVILEGED = 0x00000200; // instruction is privileged + +// opcode virtual->physical translation flags +const uint32_t OPFLAG_VALIDATE_TLB = 0x00000400; // instruction must validate TLB before execution +const uint32_t OPFLAG_MODIFIES_TRANSLATION = 0x00000800; // instruction modifies the TLB +const uint32_t OPFLAG_COMPILER_PAGE_FAULT = 0x00001000; // compiler hit a page fault when parsing +const uint32_t OPFLAG_COMPILER_UNMAPPED = 0x00002000; // compiler hit unmapped memory when parsing + +// opcode flags +const uint32_t OPFLAG_INVALID_OPCODE = 0x00004000; // instruction is invalid +const uint32_t OPFLAG_VIRTUAL_NOOP = 0x00008000; // instruction is a virtual no-op + +// opcode sequence flow flags +const uint32_t OPFLAG_REDISPATCH = 0x00010000; // instruction must redispatch after completion +const uint32_t OPFLAG_RETURN_TO_START = 0x00020000; // instruction must jump back to the beginning after completion +const uint32_t OPFLAG_END_SEQUENCE = 0x00040000; // this is the last instruction in a sequence +const uint32_t OPFLAG_CAN_CHANGE_MODES = 0x00080000; // instruction can change modes + +// execution semantics +const uint32_t OPFLAG_READS_MEMORY = 0x00100000; // instruction reads memory +const uint32_t OPFLAG_WRITES_MEMORY = 0x00200000; // instruction writes memory +#endif + +bool dspp_frontend::describe(opcode_desc &desc, const opcode_desc *prev) +{ + uint16_t op = desc.opptr.w[0] = m_dspp->read_op(desc.physpc); + + // TODO: NOOOOPE + desc.cycles = 1; // TODO: Extra cycles for extra operands + desc.length = 2; + + // Decode and execute + if (op & 0x8000) + { + switch ((op >> 13) & 3) + { + case 0: + return describe_special(op, desc); + + case 1: + case 2: + return describe_branch(op, desc); + + case 3: + return describe_complex_branch(op, desc); + } + + return false; + } + else + { + return describe_arithmetic(op, desc); + } + + return false; +} + + +bool dspp_frontend::describe_special(uint16_t op, opcode_desc &desc) +{ + switch ((op >> 10) & 7) + { + case 0: + { + // Super-special + switch ((op >> 7) & 7) + { + case 1: // BAC - TODO: MERGE? + { + //desc.regin[0] = m_acc; + desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE; + desc.targetpc = BRANCH_TARGET_DYNAMIC; + return true; + } + case 4: // RTS + { + desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE; + desc.targetpc = BRANCH_TARGET_DYNAMIC; + return true; + } + case 5: // OP_MASK + { + // TODO + return true; + } + + case 7: // SLEEP + { + // TODO + return true; + } + + case 0: // NOP + case 2: // Unused + case 3: + case 6: + return true; + } + + break; + } + case 1: // JUMP - TODO: MERGE? + { + desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE; + desc.targetpc = op & 0x3ff; + return true; + } + case 2: // JSR + { + desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE; + desc.targetpc = op & 0x3ff; + return true; + } + case 3: // BFM + { + // TODO: What sort of branch is this? +// desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE; +// desc.targetpc = 0; // FIXME + return false; + } + case 4: // MOVEREG + { + desc.flags |= OPFLAG_WRITES_MEMORY; + + // Indirect + if (op & 0x0010) + desc.flags |= OPFLAG_READS_MEMORY; + + return true; + } + case 5: // RBASE + { + return true; + } + case 6: // MOVED + { + desc.flags |= OPFLAG_WRITES_MEMORY; + return true; + } + case 7: // MOVEI + { + desc.flags |= OPFLAG_READS_MEMORY | OPFLAG_WRITES_MEMORY; + return true; + } + } + + return false; +} + +bool dspp_frontend::describe_branch(uint16_t op, opcode_desc &desc) +{ + const uint32_t select = (op >> 12) & 1; + + if (select == 0) + { + CC_N_USED(desc); + CC_V_USED(desc); + } + else + { + CC_C_USED(desc); + CC_Z_USED(desc); + } + + // TODO: Can these be unconditional? + desc.flags |= OPFLAG_IS_CONDITIONAL_BRANCH; + desc.targetpc = op & 0x3ff; + + return true; +} + +bool dspp_frontend::describe_complex_branch(uint16_t op, opcode_desc &desc) +{ + switch ((op >> 10) & 7) + { + case 0: // BLT + CC_N_USED(desc); + CC_V_USED(desc); + break; + case 1: // BLE + CC_N_USED(desc); + CC_V_USED(desc); + CC_Z_USED(desc); + break; + case 2: // BGE + CC_N_USED(desc); + CC_V_USED(desc); + break; + case 3: // BGT + CC_N_USED(desc); + CC_V_USED(desc); + CC_Z_USED(desc); + break; + case 4: // BHI + case 5: // BLS + CC_C_USED(desc); + CC_Z_USED(desc); + break; + case 6: // BXS + case 7: // BXC + CC_X_USED(desc); + break; + } + + desc.flags |= OPFLAG_IS_CONDITIONAL_BRANCH; + desc.targetpc = op & 0x3ff; + + return true; +} + +bool dspp_frontend::describe_arithmetic(uint16_t op, opcode_desc &desc) +{ + #if 0 + // Decode the various fields + uint32_t numops = (op >> 13) & 3; + uint32_t muxa = (op >> 10) & 3; + uint32_t muxb = (op >> 8) & 3; + uint32_t alu_op = (op >> 4) & 0xf; + uint32_t barrel_code = op & 0xf; + + int32_t mul_res = 0; + uint32_t alu_res = 0; + + // Check for operand overflow + if (numops == 0 && ((muxa == 1) || (muxa == 2) || (muxb == 1) || (muxb == 2))) + numops = 4; + + // Implicit barrel shift + if (barrel_code == 8) + ++numops; + + // TODO: We need to know: + // Number of cycles + // Number of bytes + // Registers read + // Registers written + // Does it read memory? + // Does it write memory? + +// parse_operands(numops); + + if (muxa == 3 || muxb == 3) + { + uint32_t mul_sel = (op >> 12) & 1; + + int32_t op1 = sign_extend16(read_next_operand()); + int32_t op2 = sign_extend16(mul_sel ? read_next_operand() : m_core->m_acc >> 4); + + mul_res = (op1 * op2) >> 11; + } + + int32_t alu_a, alu_b; + + switch (muxa) + { + case 0: + { + ACCUMULATOR_USED + alu_a = m_core->m_acc; + break; + } + case 1: case 2: + { + alu_a = read_next_operand() << 4; + break; + } + case 3: + { + alu_a = mul_res; + break; + } + } + + switch (muxb) + { + case 0: + { + ACCUMULATOR_USED + alu_b = m_core->m_acc; + break; + } + case 1: case 2: + { + alu_b = read_next_operand() << 4; + break; + } + case 3: + { + alu_b = mul_res; + break; + } + } + + // All flags ar emodifed + CC_FLAGS_MODIFIED(desc); + + switch (alu_op) + { + case 0: // _TRA + { +// alu_res = alu_a; + break; + } + case 1: // _NEG + { +// alu_res = -alu_b; + break; + } + case 2: // _+ + { +// alu_res = alu_a + alu_b; + +// if ((alu_a & 0x80000) == (alu_b & 0x80000) && +// (alu_a & 0x80000) != (alu_res & 0x80000)) +// m_core->m_flags |= DSPI_FLAG_CC_OVER; + +// if (alu_res & 0x00100000) +// m_core->m_flags |= DSPI_FLAG_CC_CARRY; + +// CC_V_MODIFIED(desc); +// CC_C_MODIFIED(desc); + break; + } + case 3: // _+C + { +// alu_res = alu_a + (m_core->m_flags & DSPI_FLAG_CC_CARRY) ? (1 << 4) : 0; + +// if (alu_res & 0x00100000) +// m_core->m_flags |= DSPI_FLAG_CC_CARRY; + + CC_C_USED(desc); + CC_C_MODIFIED(desc); + break; + } + case 4: // _- + { +// alu_res = alu_a - alu_b; + +// if ((alu_a & 0x80000) == (~alu_b & 0x80000) && +// (alu_a & 0x80000) != (alu_res & 0x80000)) +// m_core->m_flags |= DSPI_FLAG_CC_OVER; + +// if (alu_res & 0x00100000) +// m_core->m_flags |= DSPI_FLAG_CC_CARRY; + + CC_C_MODIFIED(desc); + CC_V_MODIFIED(desc); + break; + } + case 5: // _-B + { +// alu_res = alu_a - (m_core->m_flags & DSPI_FLAG_CC_CARRY) ? (1 << 4) : 0; + +// if (alu_res & 0x00100000) +// m_core->m_flags |= DSPI_FLAG_CC_CARRY; + + CC_C_USED(desc); + CC_C_MODIFIED(desc); + break; + } + case 6: // _++ + { +// alu_res = alu_a + 1; + +// if (!(alu_a & 0x80000) && (alu_res & 0x80000)) +// m_core->m_flags |= DSPI_FLAG_CC_OVER; + + CC_V_MODIFIED(desc); + break; + } + case 7: // _-- + { +// alu_res = alu_a - 1; + +// if ((alu_a & 0x80000) && !(alu_res & 0x80000)) +// m_core->m_flags |= DSPI_FLAG_CC_OVER; + + CC_V_MODIFIED(desc); + break; + } + case 8: // _TRL + { + //alu_res = alu_a; + break; + } + case 9: // _NOT + { + //alu_res = ~alu_a; + break; + } + case 10: // _AND + { + //alu_res = alu_a & alu_b; + break; + } + case 11: // _NAND + { + //alu_res = ~(alu_a & alu_b); + break; + } + case 12: // _OR + { + //alu_res = alu_a | alu_b; + break; + } + case 13: // _NOR + { + //alu_res = ~(alu_a | alu_b); + break; + } + case 14: // _XOR + { + //alu_res = alu_a ^ alu_b; + break; + } + case 15: // _XNOR + { + //alu_res = ~(alu_a ^ alu_b); + break; + } + } + + + if (alu_res & 0x00080000) + m_core->m_flags |= DSPI_FLAG_CC_NEG; + + if ((alu_res & 0x000ffff0) == 0) + m_core->m_flags |= DSPI_FLAG_CC_ZERO; + + if ((alu_res & 0x0000000f) == 0) + m_core->m_flags |= DSPI_FLAG_CC_EXACT; + + CC_N_MODIFIED(desc); + CC_Z_MODIFIED(desc); + CC_X_MODIFIED(desc); + + ACCUMULATOR_MODIFIED_ALWAYS; + + // Barrel shift + static const int32_t shifts[8] = { 0, 1, 2, 3, 4, 5, 8, 16 }; + + if (barrel_code == 8) + barrel_code = read_next_operand(); + + if (barrel_code & 8) + { + // Right shift + uint32_t shift = shifts[(~barrel_code + 1) & 7]; + + if (alu_op < 8) + { + // Arithmetic + m_core->m_acc = sign_extend20(alu_res) >> shift; + } + else + { + // Logical + m_core->m_acc = (alu_res & 0xfffff) >> shift; + } + } + else + { + // Left shift + uint32_t shift = shifts[barrel_code]; + + if (shift == 16) + { + // Clip and saturate + if (m_core->m_flags & DSPI_FLAG_CC_OVER) + m_core->m_acc = (m_core->m_flags & DSPI_FLAG_CC_NEG) ? 0x7ffff : 0xfff80000; + else + m_core->m_acc = sign_extend20(alu_res); + } + else + { + m_core->m_acc = sign_extend20(alu_res) << shift; + } + } + + if (m_core->m_writeback >= 0) + { + write_data(m_core->m_writeback, m_core->m_acc >> 4); + m_core->m_writeback = -1; + } + else if (opidx < numops) + { + write_next_operand(m_core->m_acc >> 4); + } +#endif + return true; +} + + +#if 0 +void dspp_device::parse_operands(uint32_t numops) +{ + uint32_t addr, val = 0xBAD; + uint32_t opidx = 0; + uint32_t operand = 0; + uint32_t numregs = 0; + + uint32_t opidx = 0; + + while (opidx < numops) + { + uint16_t op = desc.opptr.w[opidx + 1] = m_dspp->read_op(desc.physpc + opidx * 2); + + desc.length += 2; + ++desc.cycles; + + if (op & 0x8000) + { + // Immediate value + if ((op & 0xc000) == 0xc000) + { + // Nothing?- + } + else if((op & 0xe000) == 0x8000) + { + if (op & 0x0400) // Indirect + desc.flags |= OPFLAG_READS_MEMORY; + + if (op & 0x0800 )// Write Back + desc.flags |= OPFLAG_WRITES_MEMORY; + + ++opidx; + } + else if ((op & 0xe000) == 0xa000) + { + // 1 or 2 register operand + numregs = (op & 0x0400) ? 2 : 1; + } + } + else + { + numregs = 3; + } + + if (numregs > 0) + { + // Shift successive register operands from a single operand word + for (uint32_t i = 0; i < numregs; ++i) + { + uint32_t shift = ((numregs - i) - 1) * 5; + uint32_t regdi = (operand >> shift) & 0x1f; + // OP USES REGBASE? + + if (regdi & 0x0010) + { + // Indirect + desc.flags |= OPFLAG_READS_MEMORY; + } + + if (numregs == 2) + { + // Write back + if ((i == 0) && (operand & 0x1000)) + desc.flags |= OPFLAG_WRITES_MEMORY; + else if ((i == 1) && (operand & 0x0800)) + desc.flags |= OPFLAG_WRITES_MEMORY; + } + else if (numregs == 1) + { + if (operand & 0x800) + desc.flags |= OPFLAG_WRITES_MEMORY; + } + } + numregs = 0; + } + } +} +#endif diff --git a/src/devices/cpu/dspp/dsppfe.h b/src/devices/cpu/dspp/dsppfe.h new file mode 100644 index 00000000000..a90dc5d0225 --- /dev/null +++ b/src/devices/cpu/dspp/dsppfe.h @@ -0,0 +1,72 @@ +// license:BSD-3-Clause +// copyright-holders:Philip Bennett +/*************************************************************************** + + dsppfe.h + + Front-end for DSPP recompiler + +***************************************************************************/ + +#ifndef DEVICES_CPU_DSPP_DSPPFE_H +#define DEVICES_CPU_DSPP_DSPPFE_H + +#include "dspp.h" +#include "cpu/drcfe.h" + + +//************************************************************************** +// MACROS +//************************************************************************** + +// register flags 0 +#define REGFLAG_R(n) (1 << (n)) +#define REGFLAG_RZ(n) (((n) == 0) ? 0 : REGFLAG_R(n)) + +// register flags 1 +#define REGFLAG_FR(n) (1 << (n)) + +// register flags 2 +#define REGFLAG_CR(n) (0xf0000000 >> (4 * (n))) +#define REGFLAG_CR_BIT(n) (0x80000000 >> (n)) + +// register flags 3 +#define REGFLAG_XER_CA (1 << 0) +#define REGFLAG_XER_OV (1 << 1) +#define REGFLAG_XER_SO (1 << 2) +#define REGFLAG_XER_COUNT (1 << 3) +#define REGFLAG_CTR (1 << 4) +#define REGFLAG_LR (1 << 5) +#define REGFLAG_FPSCR(n) (1 << (6 + (n))) + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class dspp_frontend : public drc_frontend +{ +public: + // construction/destruction + dspp_frontend(dspp_device *dspp, uint32_t window_start, uint32_t window_end, uint32_t max_sequence); + +protected: + // required overrides + virtual bool describe(opcode_desc &desc, const opcode_desc *prev) override; + +private: + // inlines + + // internal helpers + bool describe_special(uint16_t op, opcode_desc &desc); + bool describe_branch(uint16_t op, opcode_desc &desc); + bool describe_complex_branch(uint16_t op, opcode_desc &desc); + bool describe_arithmetic(uint16_t op, opcode_desc &desc); + + // internal state + dspp_device *m_dspp; +}; + + +#endif /* DEVICES_CPU_DSPP_DSPPFE_H */ |