summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/dsp16/dsp16.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/dsp16/dsp16.cpp')
-rw-r--r--src/devices/cpu/dsp16/dsp16.cpp2334
1 files changed, 2006 insertions, 328 deletions
diff --git a/src/devices/cpu/dsp16/dsp16.cpp b/src/devices/cpu/dsp16/dsp16.cpp
index 33c25a32dc6..ba5737087b0 100644
--- a/src/devices/cpu/dsp16/dsp16.cpp
+++ b/src/devices/cpu/dsp16/dsp16.cpp
@@ -1,430 +1,2108 @@
// license:BSD-3-Clause
-// copyright-holders:Andrew Gardner
+// copyright-holders:Vas Crabb
/***************************************************************************
- dsp16.h
+ WE|AT&T DSP16 series emulator
- WE|AT&T DSP16 series emulator.
+ There are three basic functional units:
+ * XAAU: ROM Address Arithmetic Unit
+ - Four 16-bit address registers (pc, pt, pr, pi)
+ - One 12-bit increment register (i)
+ - One 12-bit adder
+ * YAAU: RAM Address Arithmetic Unit
+ - Four 9-bit or 16-bit pointer registers (r0, r1, r2, r3)
+ - Two 9-bit or 16-bit increment registers (j, k)
+ - Two 9-bit or 16-bit array boundary registers (rb, re)
+ - One 9-bit or 16-bit adder
+ * DAU: Data Arithmetic Unit
+ - One 16-bit multiplier register (x)
+ - One 32-bit source register (y)
+ - One 32-bit product register (p)
+ - Two 36-bit accumulators (a0, a1)
+ - Three 8-bit counter registers (c0, c1, c2)
+ - One 7-bit control register (auc)
+ - One 16-bit status register (psw)
+ - One 16*16->32 multiplier
+ - One 36-bit ALU
+ - One 36->16 extract/saturate unit
+
+ The instruction set allows for explicit parallelism. It's possible
+ to issue a multiply, and ALU operation, a RAM/ROM transfer, and a
+ pointer postmodification in a single instruction.
+
+ There's a 15-word "cache" that the DSP can execute from without the
+ cost of instruction fetches. There's an instruction to cache the
+ next N instructions and repeat them M times, and in instruction to
+ execute the currently cached instructions M times. Interrupts are
+ not serviced while executing from cache, and not all instructions
+ can be cached.
+
+ One ROM load and one RAM load/store can be issued per machine cycle.
+ Normally the DSP will overlap fetch/decode for the next instruction
+ with execution of the current instruction. However, when executing
+ from cache, the DSP doesn't need to fetch instructions from ROM so
+ it can overlap a data fetch for the next instruction with execution
+ of the current instruction. A RAM store can only occur in the
+ second machine cycle of an instruction.
+
+ When loading the cache, all but the last instruction loaded into the
+ cache run with with normal timings. The last instruction always
+ executes in two cycles. The second cycle is necessary to ensure the
+ DSP has an opportunity to overlap a data fetch for the first cached
+ instruction if necessary. On the final iteration, the last cached
+ instruction runs with normal timings, allowing the DSP to overlap
+ the fetch for the next instruction to be executed from ROM.
+
+ There's a buffered full duplex synchronous serial transceiver, and
+ a strobed parallel port. Both have have interrupt capability and
+ configurable timing.
+
+ The parallel port consists of sixteen bidirectional data lines
+ (PDB00-PDB15), a single peripheral select line (PSEL), a single
+ bidirectional input strobe line (PIDS), and a single bidirectional
+ output strobe line (PODS). In active mode, reading the port yields
+ the current value in the input register and initiates a read.
+
+ TODO:
+ * Remaining instructions
+ * PSW overflow bits
+ * Clarify rounding behaviour (F2 1011)
+ * Random condition
+ * Clarify serial behaviour
+ * Serial input
+ * More serial I/O signals
+ * Parallel I/O S/C mode
+ * More complete low-level parallel I/O hooks
***************************************************************************/
#include "emu.h"
-#include "debugger.h"
#include "dsp16.h"
#include "dsp16dis.h"
-//
-// TODO:
-// * Store the cache in 15 unique memory locations as it is on-chip.
-// * Modify cycle counts when running from within the cache
-// * A write to PI resets the pseudoramdom sequence generator) (page 2-4)
-// * Handle virtual shift addressing using RB & RE (when RE is enabled) (page 2-6)
-// * The ALU sign-extends 32-bit operands from y or p to 36 bits and produces a 36-bit output
-// * Interrupt lines (page 2-15)
-//
-
-//**************************************************************************
-// DEVICE INTERFACE
-//**************************************************************************
-
-// device type definition
+#include "debugger.h"
+
+#include <functional>
+#include <limits>
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_INT (1U << 1)
+#define LOG_SIO (1U << 2)
+#define LOG_PIO (1U << 3)
+
+//#define VERBOSE (LOG_GENERAL | LOG_INT | LOG_SIO | LOG_PIO)
+//#define LOG_OUTPUT_STREAM std::cout
+#include "logmacro.h"
+
+#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
+#define LOGSIO(...) LOGMASKED(LOG_SIO, __VA_ARGS__)
+#define LOGPIO(...) LOGMASKED(LOG_PIO, __VA_ARGS__)
+
+
+/***************************************************************************
+ DEVICE TYPE DEFINITIONS
+***************************************************************************/
+
DEFINE_DEVICE_TYPE(DSP16, dsp16_device, "dsp16", "DSP16")
+DEFINE_DEVICE_TYPE(DSP16A, dsp16a_device, "dsp16a", "DSP16A")
+
+
+/***************************************************************************
+ DSP16 SERIES BASE IMPLEMENTATION
+***************************************************************************/
+
+ALLOW_SAVE_TYPE(dsp16_device_base::cache);
+ALLOW_SAVE_TYPE(dsp16_device_base::phase);
+ALLOW_SAVE_TYPE(dsp16_device_base::flags);
+ALLOW_SAVE_TYPE(dsp16_device_base::sio_flags);
+
+WRITE_LINE_MEMBER(dsp16_device_base::exm_w)
+{
+ if (bool(state) != bool(m_exm_in))
+ {
+ m_exm_in = state ? 1U : 0U;
+ if (started())
+ external_memory_enable(*m_spaces[AS_PROGRAM], !m_exm_in);
+ }
+}
+
+/***********************************************************************
+ high-level passive parallel I/O handlers
+***********************************************************************/
+
+READ16_MEMBER(dsp16_device_base::pio_r)
+{
+ if (!pio_pods_active())
+ {
+ LOGPIO("DSP16: PIO host read PSEL = %u, PDX = %04X (PC = %04X)\n", m_psel_out, m_pio_pdx_out, m_st_pcbase);
+ if (!pio_pods_status())
+ LOGINT("DSP16: set PODS flag (PC = %04X)\n", m_st_pcbase);
+ m_pio_pioc |= u16(1) << 1;
+ return m_pio_pdx_out;
+ }
+ else
+ {
+ logerror("DSP16: warning: PIO host read in active mode - returning line status (PC = %04X)\n", m_st_pcbase);
+ // TODO: improve this when low-level interface is more complete
+ return !m_pods_out ? m_pio_pdx_out : 0xffffU;
+ }
+}
+
+WRITE16_MEMBER(dsp16_device_base::pio_w)
+{
+ if (!pio_pids_active())
+ {
+ LOGPIO("DSP16: PIO host write PSEL = %u, PDX = %04X (PC = %04X)\n", m_psel_out, data, m_st_pcbase);
+ if (!pio_pids_status())
+ LOGINT("DSP16: set PIDS flag (PC = %04X)\n", m_st_pcbase);
+ m_pio_pioc |= u16(1) << 2;
+ m_pio_pdx_in = data;
+ }
+ else
+ {
+ logerror("DSP16: warning: ignoring PIO host write in active mode (PC = %04X)\n", m_st_pcbase);
+ }
+}
+
+/***********************************************************************
+ construction/destruction
+***********************************************************************/
+
+dsp16_device_base::dsp16_device_base(
+ machine_config const &mconfig,
+ device_type type,
+ char const *tag,
+ device_t *owner,
+ u32 clock,
+ u8 yaau_bits,
+ address_map_constructor &&data_map)
+ : cpu_device(mconfig, type, tag, owner, clock)
+ , m_iack_cb(*this)
+ , m_ick_cb(*this), m_ild_cb(*this)
+ , m_do_cb(*this), m_ock_cb(*this), m_old_cb(*this), m_ose_cb(*this)
+ , m_pio_r_cb(*this), m_pio_w_cb(*this), m_pdb_w_cb(*this), m_psel_cb(*this), m_pids_cb(*this), m_pods_cb(*this)
+ , m_space_config{
+ { "rom", ENDIANNESS_BIG, 16, 16, -1, address_map_constructor(FUNC(dsp16_device_base::program_map), this) },
+ { "ram", ENDIANNESS_BIG, 16, yaau_bits, -1, std::move(data_map) },
+ { "exm", ENDIANNESS_BIG, 16, 16, -1 } }
+ , m_yaau_mask(u16((u32(1) << yaau_bits) - 1)), m_yaau_sign(u16(1) << (yaau_bits - 1))
+ , m_spaces{ nullptr, nullptr, nullptr }, m_direct(nullptr)
+ , m_icount(0), m_cache_mode(cache::NONE), m_phase(phase::PURGE), m_int_enable{ 0U, 0U }, m_flags(FLAGS_NONE), m_cache_ptr(0U), m_cache_limit(0U), m_cache_iterations(0U)
+ , m_exm_in(1U), m_int_in(CLEAR_LINE), m_iack_out(1U)
+ , m_ick_in(1U), m_ild_in(CLEAR_LINE), m_do_out(1U), m_ock_in(1U), m_old_in(CLEAR_LINE), m_ose_out(1U)
+ , m_psel_out(0U), m_pids_out(1U), m_pods_out(1U)
+ , m_xaau_pc(0U), m_xaau_pt(0U), m_xaau_pr(0U), m_xaau_pi(0U), m_xaau_i(0)
+ , m_yaau_r{ 0U, 0U, 0U, 0U }, m_yaau_rb(0U), m_yaau_re(0U), m_yaau_j(0), m_yaau_k(0)
+ , m_dau_x(0), m_dau_y(0), m_dau_p(0), m_dau_a{ 0, 0 }, m_dau_c{ 0, 0, 0 }, m_dau_auc(0U), m_dau_psw(0U), m_dau_temp(0)
+ , m_sio_sioc(0U), m_sio_obuf(0U), m_sio_osr(0U), m_sio_ofsr(0U)
+ , m_sio_clk(1U), m_sio_clk_div(0U), m_sio_ld(1U), m_sio_ld_div(0U), m_sio_flags(SIO_FLAGS_NONE)
+ , m_pio_pioc(0U), m_pio_pdx_in(0U), m_pio_pdx_out(0U), m_pio_pids_cnt(0U), m_pio_pods_cnt(0U)
+ , m_cache_pcbase(0U), m_st_pcbase(0U), m_st_yh(0), m_st_ah{ 0, 0 }, m_st_yl(0U), m_st_al{ 0U, 0U }
+{
+}
+
+/***********************************************************************
+ device_t implementation
+***********************************************************************/
+
+void dsp16_device_base::device_resolve_objects()
+{
+ m_iack_cb.resolve_safe();
+ m_ick_cb.resolve_safe();
+ m_ild_cb.resolve_safe();
+ m_do_cb.resolve_safe();
+ m_ock_cb.resolve_safe();
+ m_old_cb.resolve_safe();
+ m_ose_cb.resolve_safe();
-//-------------------------------------------------
-// dsp16_device - constructor
-//-------------------------------------------------
-
-dsp16_device::dsp16_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : cpu_device(mconfig, DSP16, tag, owner, clock),
- m_program_config("program", ENDIANNESS_LITTLE, 16, 16, -1),
- m_data_config("data", ENDIANNESS_LITTLE, 16, 16, -1),
- m_i(0),
- m_pc(0),
- m_pt(0),
- m_pr(0),
- m_pi(0),
- m_j(0),
- m_k(0),
- m_rb(0),
- m_re(0),
- m_r0(0),
- m_r1(0),
- m_r2(0),
- m_r3(0),
- m_x(0),
- m_y(0),
- m_p(0),
- m_a0(0),
- m_a1(0),
- m_auc(0),
- m_psw(0),
- m_c0(0),
- m_c1(0),
- m_c2(0),
- m_sioc(0),
- m_srta(0),
- m_sdx(0),
- m_pioc(0),
- m_pdx0(0),
- m_pdx1(0),
- m_ppc(0),
- m_cacheStart(CACHE_INVALID),
- m_cacheEnd(CACHE_INVALID),
- m_cacheRedoNextPC(CACHE_INVALID),
- m_cacheIterations(0),
- m_program(nullptr),
- m_data(nullptr),
- m_direct(nullptr),
- m_icount(0)
-{
- // Allocate & setup
-}
-
-
-
-//-------------------------------------------------
-// device_start - start up the device
-//-------------------------------------------------
-
-void dsp16_device::device_start()
-{
- // register state with the debugger
- state_add(STATE_GENPC, "GENPC", m_pc).noshow();
- state_add(STATE_GENPCBASE, "CURPC", m_ppc).noshow();
- state_add(STATE_GENFLAGS, "GENFLAGS", m_psw).callimport().callexport().formatstr("%10s").noshow();
- state_add(DSP16_PC, "PC", m_pc);
- state_add(DSP16_I, "I", m_i);
- state_add(DSP16_PT, "PT", m_pt);
- state_add(DSP16_PR, "PR", m_pr);
- state_add(DSP16_PI, "PI", m_pi);
- state_add(DSP16_J, "J", m_j);
- state_add(DSP16_K, "K", m_k);
- state_add(DSP16_RB, "RB", m_rb);
- state_add(DSP16_RE, "RE", m_re);
- state_add(DSP16_R0, "R0", m_r0);
- state_add(DSP16_R1, "R1", m_r1);
- state_add(DSP16_R2, "R2", m_r2);
- state_add(DSP16_R3, "R3", m_r3);
- state_add(DSP16_X, "X", m_x);
- state_add(DSP16_Y, "Y", m_y);
- state_add(DSP16_P, "P", m_p);
- state_add(DSP16_A0, "A0", m_a0).mask(0xfffffffffU);
- state_add(DSP16_A1, "A1", m_a1).mask(0xfffffffffU);
- state_add(DSP16_AUC, "AUC", m_auc).formatstr("%8s");
- state_add(DSP16_PSW, "PSW", m_psw).formatstr("%16s");
- state_add(DSP16_C0, "C0", m_c0);
- state_add(DSP16_C1, "C1", m_c1);
- state_add(DSP16_C2, "C2", m_c2);
- state_add(DSP16_SIOC, "SIOC", m_sioc).formatstr("%10s");
- state_add(DSP16_SRTA, "SRTA", m_srta);
- state_add(DSP16_SDX, "SDX", m_sdx);
- state_add(DSP16_PIOC, "PIOC", m_pioc).formatstr("%16s");
- state_add(DSP16_PDX0, "PDX0", m_pdx0);
- state_add(DSP16_PDX1, "PDX1", m_pdx1);
-
- // register our state for saving
- save_item(NAME(m_i));
- save_item(NAME(m_pc));
- save_item(NAME(m_pt));
- save_item(NAME(m_pr));
- save_item(NAME(m_pi));
- save_item(NAME(m_j));
- save_item(NAME(m_k));
- save_item(NAME(m_rb));
- save_item(NAME(m_re));
- save_item(NAME(m_r0));
- save_item(NAME(m_r1));
- save_item(NAME(m_r2));
- save_item(NAME(m_r3));
- save_item(NAME(m_x));
- save_item(NAME(m_y));
- save_item(NAME(m_p));
- save_item(NAME(m_a0));
- save_item(NAME(m_a1));
- save_item(NAME(m_auc));
- save_item(NAME(m_psw));
- save_item(NAME(m_c0));
- save_item(NAME(m_c1));
- save_item(NAME(m_c2));
- save_item(NAME(m_sioc));
- save_item(NAME(m_srta));
- save_item(NAME(m_sdx));
- save_item(NAME(m_pioc));
- save_item(NAME(m_pdx0));
- save_item(NAME(m_pdx1));
- save_item(NAME(m_ppc));
- save_item(NAME(m_cacheStart));
- save_item(NAME(m_cacheEnd));
- save_item(NAME(m_cacheRedoNextPC));
- save_item(NAME(m_cacheIterations));
-
- // get our address spaces
- m_program = &space(AS_PROGRAM);
- m_data = &space(AS_DATA);
- m_direct = m_program->direct<-1>();
-
- // set our instruction counter
+ m_pio_r_cb.resolve();
+ m_pio_w_cb.resolve_safe();
+ m_pdb_w_cb.resolve_safe();
+ m_psel_cb.resolve_safe();
+ m_pids_cb.resolve_safe();
+ m_pods_cb.resolve_safe();
+}
+
+void dsp16_device_base::device_start()
+{
m_icountptr = &m_icount;
+
+ m_spaces[AS_PROGRAM] = &space(AS_PROGRAM);
+ m_spaces[AS_DATA] = &space(AS_DATA);
+ m_spaces[AS_IO] = &space(AS_IO);
+ m_direct = m_spaces[AS_PROGRAM]->direct<-1>();
+
+ state_add(STATE_GENPC, "PC", m_xaau_pc);
+ state_add(STATE_GENPCBASE, "CURPC", m_st_pcbase).noshow();
+ state_add(DSP16_PT, "PT", m_xaau_pt);
+ state_add(DSP16_PR, "PR", m_xaau_pr);
+ state_add(DSP16_PI, "PI", m_xaau_pi);
+ state_add(DSP16_I, "I", m_xaau_i).mask((s16(1) << 12) - 1).callimport();
+ state_add(DSP16_R0, "R0", m_yaau_r[0]).mask(m_yaau_mask);
+ state_add(DSP16_R1, "R1", m_yaau_r[1]).mask(m_yaau_mask);
+ state_add(DSP16_R2, "R2", m_yaau_r[2]).mask(m_yaau_mask);
+ state_add(DSP16_R3, "R3", m_yaau_r[3]).mask(m_yaau_mask);
+ state_add(DSP16_RB, "RB", m_yaau_rb).mask(m_yaau_mask);
+ state_add(DSP16_RE, "RE", m_yaau_re).mask(m_yaau_mask);
+ state_add(DSP16_J, "J", m_yaau_j).mask(m_yaau_mask).callimport();
+ state_add(DSP16_K, "K", m_yaau_k).mask(m_yaau_mask).callimport();
+ state_add(DSP16_X, "X", m_dau_x);
+ state_add(DSP16_Y, "Y", m_dau_y);
+ state_add(DSP16_P, "P", m_dau_p);
+ state_add(DSP16_A0, "A0", m_dau_a[0]).mask(DAU_A_MASK).callimport();
+ state_add(DSP16_A1, "A1", m_dau_a[1]).mask(DAU_A_MASK).callimport();
+ state_add(DSP16_C0, "C0", m_dau_c[0]);
+ state_add(DSP16_C1, "C1", m_dau_c[1]);
+ state_add(DSP16_C2, "C2", m_dau_c[2]);
+ state_add(DSP16_AUC, "AUC", m_dau_auc).mask(0x7fU);
+ state_add(DSP16_PSW, "PSW", m_dau_psw).callimport().callexport();
+ state_add(DSP16_YH, "YH", m_st_yh).noshow().callimport().callexport();
+ state_add(DSP16_A0H, "A0H", m_st_ah[0]).noshow().callimport().callexport();
+ state_add(DSP16_A1H, "A1H", m_st_ah[1]).noshow().callimport().callexport();
+ state_add(DSP16_YL, "YL", m_st_yl).noshow().callimport().callexport();
+ state_add(DSP16_A0L, "A0L", m_st_al[0]).noshow().callimport().callexport();
+ state_add(DSP16_A1L, "A1L", m_st_al[1]).noshow().callimport().callexport();
+
+ save_item(NAME(m_cache_mode));
+ save_item(NAME(m_phase));
+ save_item(NAME(m_int_enable));
+ save_item(NAME(m_flags));
+ save_item(NAME(m_cache_ptr));
+ save_item(NAME(m_cache_limit));
+ save_item(NAME(m_cache_iterations));
+ save_item(NAME(m_cache));
+ save_item(NAME(m_rom_data));
+
+ save_item(NAME(m_exm_in));
+ save_item(NAME(m_int_in));
+ save_item(NAME(m_iack_out));
+
+ save_item(NAME(m_ick_in));
+ save_item(NAME(m_ild_in));
+ save_item(NAME(m_do_out));
+ save_item(NAME(m_ock_in));
+ save_item(NAME(m_old_in));
+ save_item(NAME(m_ose_out));
+
+ save_item(NAME(m_psel_out));
+ save_item(NAME(m_pids_out));
+ save_item(NAME(m_pods_out));
+
+ save_item(NAME(m_xaau_pc));
+ save_item(NAME(m_xaau_pt));
+ save_item(NAME(m_xaau_pr));
+ save_item(NAME(m_xaau_pi));
+ save_item(NAME(m_xaau_i));
+
+ save_item(NAME(m_yaau_r));
+ save_item(NAME(m_yaau_rb));
+ save_item(NAME(m_yaau_re));
+ save_item(NAME(m_yaau_j));
+ save_item(NAME(m_yaau_k));
+
+ save_item(NAME(m_dau_x));
+ save_item(NAME(m_dau_y));
+ save_item(NAME(m_dau_p));
+ save_item(NAME(m_dau_a));
+ save_item(NAME(m_dau_c));
+ save_item(NAME(m_dau_auc));
+ save_item(NAME(m_dau_psw));
+ save_item(NAME(m_dau_temp));
+
+ save_item(NAME(m_sio_sioc));
+ save_item(NAME(m_sio_obuf));
+ save_item(NAME(m_sio_osr));
+ save_item(NAME(m_sio_ofsr));
+ save_item(NAME(m_sio_clk));
+ save_item(NAME(m_sio_clk_div));
+ save_item(NAME(m_sio_ld));
+ save_item(NAME(m_sio_ld_div));
+ save_item(NAME(m_sio_flags));
+
+ save_item(NAME(m_pio_pioc));
+ save_item(NAME(m_pio_pdx_in));
+ save_item(NAME(m_pio_pdx_out));
+ save_item(NAME(m_pio_pids_cnt));
+ save_item(NAME(m_pio_pods_cnt));
+
+ save_item(NAME(m_cache_pcbase));
+ save_item(NAME(m_st_pcbase));
+
+ external_memory_enable(*m_spaces[AS_PROGRAM], !m_exm_in);
}
+void dsp16_device_base::device_reset()
+{
+ cpu_device::device_reset();
+
+ m_cache_mode = cache::NONE;
+ m_phase = phase::PURGE;
+ std::fill(std::begin(m_int_enable), std::end(m_int_enable), 0U);
+ m_flags = FLAGS_NONE;
+ m_cache_ptr = 0U;
+ m_xaau_pc = 0U;
+ m_yaau_rb = 0U;
+ m_yaau_re = 0U;
+ m_sio_sioc = 0U;
+ m_sio_ofsr = 0U;
+ m_sio_clk = 1U;
+ m_sio_clk_div = 0U;
+ m_pio_pioc = 0x0008U;
+ m_pio_pids_cnt = 0U;
+ m_pio_pods_cnt = 0U;
+ m_st_pcbase = 0U;
+
+ // interrupt reset outputs
+ if (!m_iack_out)
+ {
+ LOGINT("DSP16: de-asserting IACK for reset\n");
+ m_iack_cb(m_iack_out = 1U);
+ }
-//-------------------------------------------------
-// device_reset - reset the device
-//-------------------------------------------------
+ // SIO reset outputs
+ m_ick_cb(1); // actually high-impedance
+ m_ild_cb(1); // actually high-impedance
+ m_do_cb(m_do_out = 1U); // actually high-impedance
+ m_ock_cb(1); // actually high-impedance
+ m_old_cb(1); // actually high-impedance
+ if (!m_ose_out)
+ m_ose_cb(m_ose_out = 1U);
-void dsp16_device::device_reset()
+ // PIO reset outputs
+ m_pdb_w_cb(machine().dummy_space(), m_psel_out, 0xffffU, 0x0000U);
+ if (!m_pids_out)
+ {
+ LOGPIO("DSP16: de-asserting PIDS for reset\n");
+ m_pids_cb(m_pids_out = 1U); // actually high-impedance
+ }
+ if (!m_pods_out)
+ {
+ LOGPIO("DSP16: de-asserting PODS for reset\n");
+ m_pods_cb(m_pods_out = 1U); // actually high-impedance
+ }
+}
+
+/***********************************************************************
+ device_execute_interface implementation
+***********************************************************************/
+
+void dsp16_device_base::execute_run()
{
- // Page 7-5
- m_pc = 0x0000;
- m_pi = 0x0000;
- m_sioc = 0x0000; // (page 5-4)
+ while (0 < m_icount)
+ {
+ // execute one cycle of an instruction
+ switch (m_cache_mode)
+ {
+ case cache::NONE:
+ case cache::LOAD:
+ execute_one_rom();
+ break;
+ case cache::EXECUTE:
+ execute_one_cache();
+ break;
+ }
+ --m_icount;
- // SRTA is unaltered by reset
- m_pioc = 0x0008;
- m_rb = 0x0000;
- m_re = 0x0000;
+ // step the serial I/O clock divider
+ if (m_sio_clk_div)
+ {
+ --m_sio_clk_div;
+ }
+ else
+ {
+ bool const active(!m_sio_clk);
+ m_sio_clk = active ? 1U : 0U;
+ if (sio_ick_active())
+ {
+ if (active)
+ sio_ick_active_edge();
+ m_ick_cb(m_sio_clk);
+ }
+ if (sio_ock_active())
+ {
+ m_ock_cb(m_sio_clk);
+ if (active)
+ sio_ock_active_edge();
+ }
+ switch ((m_sio_sioc >> 7) & 0x0003U)
+ {
+ case 0x0: m_sio_clk_div = (4 / 4) - 1; break; // CKI÷4
+ case 0x1: m_sio_clk_div = (12 / 4) - 1; break; // CKI÷12
+ case 0x2: m_sio_clk_div = (16 / 4) - 1; break; // CKI÷16
+ case 0x3: m_sio_clk_div = (20 / 4) - 1; break; // CKI÷20
+ }
+ }
- // AUC is not affected by reset
- m_ppc = m_pc;
+ // udpate parallel input strobe
+ if (m_pio_pids_cnt)
+ {
+ assert(!m_pids_out);
+ if (!--m_pio_pids_cnt)
+ {
+ if (!m_pio_r_cb.isnull())
+ m_pio_pdx_in = m_pio_r_cb(machine().dummy_space(), m_psel_out, 0xffffU);
+ m_pids_cb(m_pids_out = 1U);
+ LOGPIO("DSP16: PIO read active edge PSEL = %u, PDX = %04X (PC = %04X)\n", m_psel_out, m_pio_pdx_in, m_st_pcbase);
+ }
+ }
+ else
+ {
+ assert(m_pids_out);
+ }
- // Hacky cache emulation.
- m_cacheStart = CACHE_INVALID;
- m_cacheEnd = CACHE_INVALID;
- m_cacheRedoNextPC = CACHE_INVALID;
- m_cacheIterations = 0;
+ // udpate parallel output strobe
+ if (m_pio_pods_cnt)
+ {
+ assert(!m_pods_out);
+ if (!--m_pio_pods_cnt)
+ {
+ LOGPIO("DSP16: PIO write active edge PSEL = %u, PDX = %04X (PC = %04X)\n", m_psel_out, m_pio_pdx_out, m_st_pcbase);
+ m_pods_cb(1U);
+ m_pio_w_cb(machine().dummy_space(), m_psel_out, m_pio_pdx_out, 0xffffU);
+ m_pods_out = 1U;
+ m_pdb_w_cb(machine().dummy_space(), m_psel_out, 0xffffU, 0x0000U);
+ }
+ }
+ else
+ {
+ assert(m_pods_out);
+ }
+ }
}
+void dsp16_device_base::execute_set_input(int inputnum, int state)
+{
+ switch (inputnum)
+ {
+ case DSP16_INT_LINE:
+ m_int_in = state;
+ break;
+ case DSP16_ICK_LINE:
+ if (((CLEAR_LINE == state) ? 0U : 1U) != m_ick_in)
+ {
+ if (!sio_ick_active())
+ {
+ if (CLEAR_LINE != state)
+ sio_ick_active_edge();
+ }
+ else
+ {
+ logerror("DSP16: warning: ignoring ICK input in active mode (PC = %04X)\n", m_st_pcbase);
+ }
+ m_ick_in = (ASSERT_LINE == state) ? 1U : 0U;
+ }
+ break;
+ case DSP16_ILD_LINE:
+ if (sio_ild_active())
+ logerror("DSP16: warning: ignoring ILD input in active mode (PC = %04X)\n", m_st_pcbase);
+ m_ild_in = state;
+ break;
+ case DSP16_OCK_LINE:
+ if (((CLEAR_LINE == state) ? 0U : 1U) != m_ock_in)
+ {
+ if (!sio_ock_active())
+ {
+ if (CLEAR_LINE != state)
+ sio_ock_active_edge();
+ }
+ else
+ {
+ logerror("DSP16: warning: ignoring OCK input in active mode (PC = %04X)\n", m_st_pcbase);
+ }
+ m_ock_in = (ASSERT_LINE == state) ? 1U : 0U;
+ }
+ break;
+ case DSP16_OLD_LINE:
+ if (sio_old_active())
+ logerror("DSP16: warning: ignoring OLD input in active mode (PC = %04X)\n", m_st_pcbase);
+ m_old_in = state;
+ break;
+ }
+}
-//-------------------------------------------------
-// memory_space_config - return the configuration
-// of the specified address space, or nullptr if
-// the space doesn't exist
-//-------------------------------------------------
+/***********************************************************************
+ device_memory_interface implementation
+***********************************************************************/
-device_memory_interface::space_config_vector dsp16_device::memory_space_config() const
+device_memory_interface::space_config_vector dsp16_device_base::memory_space_config() const
{
- return space_config_vector {
- std::make_pair(AS_PROGRAM, &m_program_config),
- std::make_pair(AS_DATA, &m_data_config)
- };
+ return space_config_vector{
+ std::make_pair(AS_PROGRAM, &m_space_config[AS_PROGRAM]),
+ std::make_pair(AS_DATA, &m_space_config[AS_DATA]),
+ std::make_pair(AS_IO, &m_space_config[AS_IO]) };
}
+/***********************************************************************
+ device_state_interface implementation
+***********************************************************************/
-//-------------------------------------------------
-// state_string_export - export state as a string
-// for the debugger
-//-------------------------------------------------
+void dsp16_device_base::state_import(device_state_entry const &entry)
+{
+ switch (entry.index())
+ {
+ // extend signed registers that aren't a power-of-two size
+ case DSP16_I:
+ m_xaau_i = (m_xaau_i & XAAU_I_MASK) | ((m_xaau_i & XAAU_I_SIGN) ? XAAU_I_EXT : 0);
+ break;
+ case DSP16_J:
+ m_yaau_j = (m_yaau_j & m_yaau_mask) | ((m_yaau_j & m_yaau_sign) ? ~m_yaau_mask : 0);
+ break;
+ case DSP16_K:
+ m_yaau_k = (m_yaau_k & m_yaau_mask) | ((m_yaau_k & m_yaau_sign) ? ~m_yaau_mask : 0);
+ break;
+ case DSP16_A0:
+ m_dau_a[0] = (m_dau_a[0] & DAU_A_MASK) | ((m_dau_a[0] & DAU_A_SIGN) ? DAU_A_EXT : 0);
+ break;
+ case DSP16_A1:
+ m_dau_a[1] = (m_dau_a[1] & DAU_A_MASK) | ((m_dau_a[1] & DAU_A_SIGN) ? DAU_A_EXT : 0);
+ break;
+
+ // guard bits of accumulators are accessible via status word
+ case DSP16_PSW:
+ m_dau_a[0] = u64(u32(m_dau_a[0])) | (u64(m_dau_psw & 0x000fU) << 32) | (BIT(m_dau_psw, 3) ? DAU_A_EXT : 0U);
+ m_dau_a[1] = u64(u32(m_dau_a[1])) | (u64(m_dau_psw & 0x01e0U) << 27) | (BIT(m_dau_psw, 8) ? DAU_A_EXT : 0U);
+ break;
-void dsp16_device::state_string_export(const device_state_entry &entry, std::string &str) const
+ // put partial registers in the appropriate places
+ case DSP16_YH:
+ m_dau_y = (s32(m_st_yh) << 16) | (m_dau_y & ((s32(1) << 16) - 1));
+ break;
+ case DSP16_A0H:
+ m_dau_a[0] = (s32(m_st_ah[0]) << 16) | s32(m_dau_a[0] & ((s32(1) << 16) - 1));
+ break;
+ case DSP16_A1H:
+ m_dau_a[1] = (s32(m_st_ah[1]) << 16) | s32(m_dau_a[1] & ((s32(1) << 16) - 1));
+ break;
+ case DSP16_YL:
+ m_dau_y = (m_dau_y & ~((s32(1) << 16) - 1)) | u32(m_st_yl);
+ break;
+ case DSP16_A0L:
+ m_dau_a[0] = (m_dau_a[0] & ~((s64(1) << 16) - 1)) | u64(m_st_al[0]);
+ break;
+ case DSP16_A1L:
+ m_dau_a[1] = (m_dau_a[1] & ~((s64(1) << 16) - 1)) | u64(m_st_al[1]);
+ break;
+ }
+}
+
+void dsp16_device_base::state_export(device_state_entry const &entry)
{
switch (entry.index())
{
- case STATE_GENFLAGS:
- str = "(below)";
+ // guard bits of accumulators are accessible via status word
+ case DSP16_PSW:
+ m_dau_psw = (m_dau_psw & 0xfe10U) | (u16(u64(m_dau_a[0]) >> 32) & 0x000fU) | (u16(u64(m_dau_a[1]) >> 27) & 0x01e0U);
+ break;
+
+ // put partial registers in the appropriate places
+ case DSP16_YH:
+ m_st_yh = m_dau_y >> 16;
+ break;
+ case DSP16_A0H:
+ m_st_ah[0] = u16(u64(m_dau_a[0]) >> 16);
+ break;
+ case DSP16_A1H:
+ m_st_ah[1] = u16(u64(m_dau_a[1]) >> 16);
+ break;
+ case DSP16_YL:
+ m_st_yl = u16(u32(m_dau_y));
+ break;
+ case DSP16_A0L:
+ m_st_al[0] = u16(u64(m_dau_a[0]));
+ break;
+ case DSP16_A1L:
+ m_st_al[1] = u16(u64(m_dau_a[1]));
+ break;
+ }
+}
+
+/***********************************************************************
+ device_disasm_interface implementation
+***********************************************************************/
+
+util::disasm_interface *dsp16_device_base::create_disassembler()
+{
+ return new dsp16a_disassembler;
+}
+
+template <offs_t Base> READ16_MEMBER(dsp16_device_base::external_memory_r)
+{
+ return m_spaces[AS_IO]->read_word(Base + offset, mem_mask);
+}
+
+/***********************************************************************
+ internal address maps
+***********************************************************************/
+
+void dsp16_device_base::program_map(address_map &map)
+{
+ map.global_mask(0xffff);
+ map.unmap_value_high();
+}
+
+/***********************************************************************
+ instruction execution
+***********************************************************************/
+
+inline void dsp16_device_base::execute_one_rom()
+{
+ u16 const op(m_cache[m_cache_ptr]);
+ bool const cache_load(cache::LOAD == m_cache_mode);
+ bool const last_cache_load(cache_load && (m_cache_ptr == m_cache_limit));
+ u16 const cache_next((m_cache_ptr + (cache_load ? 1 : 0)) & 0x0fU);
+ u16 *fetch_target(&m_cache[cache_next]);
+ u16 fetch_addr(0U);
+ flags predicate(FLAGS_PRED_NONE);
+
+ switch (m_phase)
+ {
+ case phase::PURGE:
+ fetch_addr = m_xaau_pc;
+ m_phase = phase::OP1;
break;
- case DSP16_AUC:
+ case phase::OP1:
+ if (machine().debug_flags & DEBUG_FLAG_ENABLED)
+ debugger_instruction_hook(this, m_st_pcbase);
+
+ // IACK is updated for the next instruction
+ switch (m_flags & FLAGS_IACK_MASK)
+ {
+ case FLAGS_IACK_SET:
+ if (m_iack_out)
+ {
+ LOGINT("DSP16: asserting IACK (PC = %04X)\n", m_st_pcbase);
+ m_iack_cb(m_iack_out = 0U);
+ standard_irq_callback(DSP16_INT_LINE);
+ }
+ break;
+ case FLAGS_IACK_CLEAR:
+ if (!m_iack_out)
+ {
+ LOGINT("DSP16: de-asserting IACK (PC = %04X)\n", m_st_pcbase);
+ m_iack_cb(m_iack_out = 1U);
+ m_pio_pioc &= 0xfffeU;
+ }
+ break;
+ default:
+ break;
+ }
+ set_iack(FLAGS_IACK_NONE);
+
+ // if we're not servicing an interrupt or caching
+ if (m_iack_out && (cache::NONE == m_cache_mode))
{
- std::string alignString;
- const uint8_t align = m_auc & 0x03;
- switch (align)
+ // TODO: is INT sampled on any instruction or only interruptible instructions?
+ // if an unmasked interrupt is pending
+ if ((m_pio_pioc & m_int_enable[0] & 0x001eU) || (BIT(m_int_enable[0], 0) && (CLEAR_LINE != m_int_in)))
{
- case 0x00: alignString = "xy"; break;
- case 0x01: alignString = "/4"; break;
- case 0x02: alignString = "x4"; break;
- case 0x03: alignString = ",,"; break;
+ // if the current instruction is interruptible
+ if (op_interruptible(op))
+ {
+ if (pio_int_enable() && (CLEAR_LINE != m_int_in))
+ {
+ if (ASSERT_LINE != m_int_in)
+ m_int_in = CLEAR_LINE;
+ m_pio_pioc |= 0x0001U;
+ }
+ LOGINT(
+ "DSP16: servicing interrupts%s%s%s%s%s (PC = %04X)\n",
+ (pio_ibf_enable() && pio_ibf_status()) ? " IBF" : "",
+ (pio_obe_enable() && pio_obe_status()) ? " OBE" : "",
+ (pio_pids_enable() && pio_pids_status()) ? " PIDS" : "",
+ (pio_pods_enable() && pio_pods_status()) ? " PODS" : "",
+ (pio_int_enable() && pio_int_status()) ? " INT" : "",
+ m_st_pcbase);
+ set_iack(FLAGS_IACK_SET);
+ fetch_addr = (m_xaau_pc & 0xf000U) | ((m_xaau_pc + 1) & 0x0fffU);
+ m_int_enable[0] = m_int_enable[1];
+ m_xaau_pc = 0x0001U;
+ m_phase = phase::PURGE;
+ break;
+ }
+ }
+ }
+
+ // normal opcode execution
+ fetch_addr = set_xaau_pc_offset(m_xaau_pc + 1);
+ m_int_enable[0] = m_int_enable[1];
+ switch (op >> 11)
+ {
+ case 0x00: // goto JA
+ case 0x01:
+ case 0x10: // call JA
+ case 0x11:
+ if (check_predicate())
+ {
+ if (BIT(op, 15))
+ m_xaau_pr = m_xaau_pc;
+ set_xaau_pc_offset(op_ja(op));
+ }
+ m_phase = phase::PURGE;
+ break;
+
+ case 0x02: // R = M
+ case 0x03:
+ yaau_short_immediate_load(op);
+ break;
+
+ case 0x04: // F1 ; Y = a1[l]
+ case 0x1c: // F1 ; Y = a0[l]
+ fetch_target = nullptr;
+ m_phase = phase::OP2;
+ break;
+
+ case 0x06: // F1 ; Y
+ op_dau_ad(op) = dau_f1(op);
+ yaau_read(op);
+ break;
+
+ case 0x07: // F1 ; aT[l] = Y
+ op_dau_ad(op) = dau_f1(op);
+ set_dau_at(op, yaau_read(op));
+ break;
+
+ case 0x08: // aT = R
+ assert(!(op & 0x000fU)); // reserved field?
+ fetch_target = nullptr;
+ set_dau_at(op, get_r(op));
+ m_phase = phase::OP2;
+ break;
+
+ case 0x09: // R = a0
+ case 0x0b: // R = a1
+ {
+ assert(!(op & 0x040fU)); // reserved fields?
+ fetch_target = nullptr;
+ s64 a(m_dau_a[BIT(op, 12)]);
+ if (!BIT(m_dau_auc, 2 + BIT(op, 12)))
+ a = std::min<s64>(std::max<s64>(a, std::numeric_limits<s32>::min()), std::numeric_limits<s32>::max());
+ set_r(op, u16(u64(a) >> 16));
+ m_phase = phase::OP2;
+ }
+ break;
+
+ case 0x0a: // R = N
+ assert(!(op & 0x040fU)); // reserved fields?
+ m_phase = phase::OP2;
+ fetch_target = &m_rom_data;
+ break;
+
+ case 0x0c: // Y = R
+ assert(!(op & 0x0400U)); // reserved field?
+ fetch_target = nullptr;
+ m_phase = phase::OP2;
+ break;
+
+ case 0x0e: // do K { instr1...instrIN } # redo K
+ {
+ u16 const ni(op_ni(op));
+ if (ni)
+ {
+ m_cache_mode = cache::LOAD;
+ fetch_target = &m_cache[m_cache_ptr = 1U];
+ m_cache_limit = ni;
+ m_cache_pcbase = m_st_pcbase;
+ }
+ else
+ {
+ fetch_target = nullptr;
+ m_cache_mode = cache::EXECUTE;
+ m_phase = phase::PREFETCH;
+ m_cache_ptr = 1U;
+ }
+ m_cache_iterations = op_k(op);
+ assert(m_cache_iterations >= 2U); // p3-25: "The iteration count can be between 2 and 127, inclusive"
+ }
+ break;
+
+ case 0x0f: // R = Y
+ assert(!(op & 0x0400U)); // reserved field?
+ fetch_target = nullptr;
+ set_r(op, yaau_read(op));
+ m_phase = phase::OP2;
+ break;
+
+ case 0x12: // ifc CON F2
+ ++m_dau_c[1];
+ if (op_dau_con(op))
+ {
+ op_dau_ad(op) = dau_f2(op);
+ m_dau_c[2] = m_dau_c[1];
+ }
+ break;
+
+ case 0x13: // if CON F2
+ if (op_dau_con(op))
+ op_dau_ad(op) = dau_f2(op);
+ break;
+
+ case 0x14: // F1 ; Y = y[l]
+ fetch_target = nullptr;
+ op_dau_ad(op) = dau_f1(op);
+ m_phase = phase::OP2;
+ break;
+
+ case 0x15: // F1 ; Z : y[l]
+ fetch_target = nullptr;
+ op_dau_ad(op) = dau_f1(op);
+ m_dau_temp = u16(u32(m_dau_y) >> (op_x(op) ? 16 : 0));
+ set_dau_y(op, yaau_read(op));
+ m_phase = phase::OP2;
+ break;
+
+ case 0x16: // F1 ; x = Y
+ op_dau_ad(op) = dau_f1(op);
+ m_dau_x = yaau_read(op);
+ break;
+
+ case 0x17: // F1 ; y[l] = Y
+ op_dau_ad(op) = dau_f1(op);
+ set_dau_y(op, yaau_read(op));
+ break;
+
+ case 0x18: // goto B
+ assert(!(op & 0x00ffU)); // reserved field?
+ switch (op_b(op))
+ {
+ case 0x0: // return
+ if (check_predicate())
+ m_xaau_pc = m_xaau_pr;
+ break;
+ case 0x1: // ireturn
+ if (m_iack_out)
+ logerror("DSP16: ireturn when not servicing interrupt (PC = %04X)\n", m_st_pcbase);
+ LOGINT("DSP16: return from interrupt (PC = %04X)\n", m_st_pcbase);
+ set_iack(FLAGS_IACK_CLEAR);
+ m_xaau_pc = m_xaau_pi;
+ break;
+ case 0x2: // goto pt
+ if (check_predicate())
+ m_xaau_pc = m_xaau_pt;
+ break;
+ case 0x3: // call pt
+ if (check_predicate())
+ {
+ m_xaau_pr = m_xaau_pc;
+ m_xaau_pc = m_xaau_pt;
+ }
+ break;
+ case 0x4: // Reserved
+ case 0x5:
+ case 0x6:
+ case 0x7:
+ throw emu_fatalerror("DSP16: reserved B value %01X (PC = %04X)\n", op_b(op), m_st_pcbase);
+ }
+ if (m_iack_out)
+ m_xaau_pi = m_xaau_pc;
+ m_phase = phase::PURGE;
+ break;
+
+ case 0x19: // F1 ; y = a0 ; x = *pt++[i]
+ case 0x1b: // F1 ; y = a1 ; x = *pt++[i]
+ {
+ assert(!(op & 0x000fU)); // reserved field?
+ s64 const d(dau_f1(op));
+ s64 a(m_dau_a[BIT(op, 12)]);
+ // FIXME: is saturation applied when transferring a to y?
+ m_dau_y = u32(u64(a));
+ op_dau_ad(op) = d;
+ fetch_target = nullptr;
+ m_rom_data = m_spaces[AS_PROGRAM]->read_word(m_xaau_pt);
+ m_phase = phase::OP2;
+ }
+ break;
+
+ case 0x1a: // if CON # icall
+ // FIXME: icall is a special case of this op
+ assert(!(op & 0x07e0U)); // reserved field?
+ predicate = op_dau_con(op) ? FLAGS_PRED_TRUE : FLAGS_PRED_FALSE;
+ break;
+
+ case 0x1e: // Reserved
+ throw emu_fatalerror("DSP16: reserved op %u (PC = %04X)\n", op >> 11, m_st_pcbase);
+ break;
+
+ case 0x1f: // F1 ; y = Y ; x = *pt++[i]
+ op_dau_ad(op) = dau_f1(op);
+ m_dau_y = (u32(u16(yaau_read(op))) << 16) | u32(BIT(m_dau_psw, 6) ? u16(u32(m_dau_y)) : u16(0));
+ fetch_target = nullptr;
+ m_rom_data = m_spaces[AS_PROGRAM]->read_word(m_xaau_pt);
+ m_phase = phase::OP2;
+ break;
+
+ default:
+ throw emu_fatalerror("DSP16: unimplemented op %u (PC = %04X)\n", op >> 11, m_st_pcbase);
+ }
+
+ if (cache_load && (phase::OP1 == m_phase))
+ {
+ // if the last instruction loaded to the cache completes in a single cycle, there's an extra cycle for a data fetch
+ if (last_cache_load)
+ {
+ fetch_target = nullptr;
+ m_cache_mode = cache::EXECUTE;
+ m_phase = phase::PREFETCH;
+ m_cache_ptr = 1U;
+ --m_cache_iterations;
+ }
+ else
+ {
+ m_cache_ptr = cache_next;
}
- str = string_format("%c%c%c%c%c%s",
- m_auc & 0x40 ? 'Y':'.',
- m_auc & 0x20 ? '1':'.',
- m_auc & 0x10 ? '0':'.',
- m_auc & 0x08 ? '1':'.',
- m_auc & 0x04 ? '0':'.',
- alignString);
}
break;
- case DSP16_PSW:
- str = string_format("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c",
- m_psw & 0x8000 ? 'M':'.',
- m_psw & 0x4000 ? 'E':'.',
- m_psw & 0x2000 ? 'L':'.',
- m_psw & 0x1000 ? 'V':'.',
- m_psw & 0x0800 ? ',':',',
- m_psw & 0x0400 ? ',':',',
- m_psw & 0x0200 ? 'O':'.',
- m_psw & 0x0100 ? '1':'.',
- m_psw & 0x0080 ? '1':'.',
- m_psw & 0x0040 ? '1':'.',
- m_psw & 0x0020 ? '1':'.',
- m_psw & 0x0010 ? 'O':'.',
- m_psw & 0x0008 ? '1':'.',
- m_psw & 0x0004 ? '1':'.',
- m_psw & 0x0002 ? '1':'.',
- m_psw & 0x0001 ? '1':'.');
- break;
-
- case DSP16_PIOC:
+ case phase::OP2:
+ m_phase = phase::OP1;
+ switch (op >> 11)
{
- std::string strobeString;
- const uint8_t strobe = (m_pioc & 0x6000) >> 13;
- switch (strobe)
+ case 0x04: // F1 ; Y = a1[l]
+ case 0x1c: // F1 ; Y = a0[l]
{
- case 0x00: strobeString = "1T"; break;
- case 0x01: strobeString = "2T"; break;
- case 0x02: strobeString = "3T"; break;
- case 0x03: strobeString = "4T"; break;
+ s64 a(m_dau_a[BIT(~op, 14)]);
+ bool const sat(!BIT(m_dau_auc, 2 + BIT(~op, 14)));
+ if (sat)
+ a = std::min<s64>(std::max<s64>(a, std::numeric_limits<s32>::min()), std::numeric_limits<s32>::max());
+ yaau_write(op, u16(u64(a) >> (op_x(op) ? 16 : 0)));
+ op_dau_ad(op) = dau_f1(op);
}
- str = string_format("%c%s%c%c%c%c%c%c%c%c%c%c%c%c%c",
- m_pioc & 0x8000 ? 'I':'.',
- strobeString,
- m_pioc & 0x1000 ? 'O':'I',
- m_pioc & 0x0800 ? 'O':'I',
- m_pioc & 0x0400 ? 'S':'.',
- m_pioc & 0x0200 ? 'I':'.',
- m_pioc & 0x0100 ? 'O':'.',
- m_pioc & 0x0080 ? 'P':'.',
- m_pioc & 0x0040 ? 'P':'.',
- m_pioc & 0x0020 ? 'I':'.',
- m_pioc & 0x0010 ? 'I':'.',
- m_pioc & 0x0008 ? 'O':'.',
- m_pioc & 0x0004 ? 'P':'.',
- m_pioc & 0x0002 ? 'P':'.',
- m_pioc & 0x0001 ? 'I':'.');
+ break;
+
+ case 0x08: // aT = R
+ case 0x09: // R = a0
+ case 0x0b: // R = a1
+ break;
+
+ case 0x0a: // R = N
+ set_xaau_pc_offset(m_xaau_pc + 1);
+ set_r(op, m_rom_data);
+ break;
+
+ case 0x0c: // Y = R
+ yaau_write(op, get_r(op));
+ break;
+
+ case 0x0f: // R = Y
+ break;
+
+ case 0x14: // F1 ; Y = y[l]
+ yaau_write(op, u16(u32(m_dau_y) >> (op_x(op) ? 16 : 0)));
+ break;
+
+ case 0x15: // F1 ; Z : y[l]
+ yaau_write_z(op);
+ break;
+
+ case 0x19: // F1 ; y = a0 ; x = *pt++[i]
+ case 0x1b: // F1 ; y = a1 ; x = *pt++[i]
+ case 0x1f: // F1 ; y = Y ; x = *pt++[i]
+ xaau_increment_pt(op_xaau_increment(op));
+ m_dau_x = m_rom_data;
+ break;
+
+ default:
+ throw emu_fatalerror("DSP16: op %u doesn't take two cycles to run from ROM\n", op >> 11);
+ }
+
+ if (last_cache_load)
+ {
+ fetch_target = nullptr;
+ m_cache_mode = cache::EXECUTE;
+ m_cache_ptr = 1U;
+ --m_cache_iterations;
+ overlap_rom_data_read();
+ }
+ else
+ {
+ fetch_addr = m_xaau_pc;
+ if (cache_load)
+ m_cache_ptr = cache_next;
}
break;
- // Placeholder for a better view later (TODO)
- case DSP16_SIOC:
+ default:
+ throw emu_fatalerror("DSP16: inappropriate phase for ROM execution\n");
+ }
+
+ if (FLAGS_PRED_NONE != (m_flags & FLAGS_PRED_MASK))
+ throw emu_fatalerror("DSP16: predicate applied to ineligible op %u (PC = %04X)\n", op >> 11, m_st_pcbase);
+ set_predicate(predicate);
+
+ if (fetch_target)
+ *fetch_target = m_direct->read_word(fetch_addr);
+
+ if (phase::OP1 == m_phase)
+ {
+ if (cache::EXECUTE != m_cache_mode)
+ m_st_pcbase = m_xaau_pc;
+ else
+ m_st_pcbase = (m_cache_pcbase & 0xf000U) | ((m_cache_pcbase + m_cache_ptr) & 0x0fffU);
+ }
+}
+
+inline void dsp16_device_base::execute_one_cache()
+{
+ u16 const op(m_cache[m_cache_ptr]);
+ bool const at_limit(m_cache_ptr == m_cache_limit);
+ bool const last_instruction(at_limit && (1U == m_cache_iterations));
+ switch (m_phase)
+ {
+ case phase::OP1:
+ if (machine().debug_flags & DEBUG_FLAG_ENABLED)
+ debugger_instruction_hook(this, m_st_pcbase);
+ m_int_enable[0] = m_int_enable[1];
+ switch (op >> 11)
{
- std::string clkString;
- const uint8_t clk = (m_sioc & 0x0180) >> 7;
- switch (clk)
+ case 0x02: // R = M
+ case 0x03:
+ yaau_short_immediate_load(op);
+ break;
+
+ case 0x04: // F1 ; Y = a1[l]
+ case 0x1c: // F1 ; Y = a0[l]
+ m_phase = phase::OP2;
+ break;
+
+ case 0x06: // F1 ; Y
+ op_dau_ad(op) = dau_f1(op);
+ yaau_read(op);
+ break;
+
+ case 0x07: // F1 ; aT[l] = Y
+ op_dau_ad(op) = dau_f1(op);
+ set_dau_at(op, yaau_read(op));
+ break;
+
+ case 0x08: // aT = R
+ assert(!(op & 0x000fU)); // reserved field?
+ set_dau_at(op, get_r(op));
+ m_phase = phase::OP2;
+ break;
+
+ case 0x09: // R = a0
+ case 0x0b: // R = a1
{
- case 0x00: clkString = "/4"; break;
- case 0x01: clkString = "12"; break;
- case 0x02: clkString = "16"; break;
- case 0x03: clkString = "20"; break;
+ assert(!(op & 0x040fU)); // reserved fields?
+ s64 a(m_dau_a[BIT(op, 12)]);
+ if (!BIT(m_dau_auc, 2 + BIT(op, 12)))
+ a = std::min<s64>(std::max<s64>(a, std::numeric_limits<s32>::min()), std::numeric_limits<s32>::max());
+ set_r(op, u16(u64(a) >> 16));
+ m_phase = phase::OP2;
}
- str = string_format("%c%s%c%c%c%c%c%c%c",
- m_sioc & 0x0200 ? 'I':'O',
- clkString,
- m_sioc & 0x0040 ? 'L':'M',
- m_sioc & 0x0020 ? 'I':'O',
- m_sioc & 0x0010 ? 'I':'O',
- m_sioc & 0x0008 ? 'I':'O',
- m_sioc & 0x0004 ? 'I':'O',
- m_sioc & 0x0002 ? '2':'1',
- m_sioc & 0x0001 ? '2':'1');
+ break;
+
+ case 0x0c: // Y = R
+ assert(!(op & 0x0400U)); // reserved field?
+ m_phase = phase::OP2;
+ break;
+
+ case 0x0f: // R = Y
+ assert(!(op & 0x0400U)); // reserved field?
+ set_r(op, yaau_read(op));
+ m_phase = phase::OP2;
+ break;
+
+ case 0x12: // ifc CON F2
+ ++m_dau_c[1];
+ if (op_dau_con(op))
+ {
+ op_dau_ad(op) = dau_f2(op);
+ m_dau_c[2] = m_dau_c[1];
+ }
+ break;
+
+ case 0x13: // if CON F2
+ if (op_dau_con(op))
+ op_dau_ad(op) = dau_f2(op);
+ break;
+
+ case 0x14: // F1 ; Y = y[l]
+ op_dau_ad(op) = dau_f1(op);
+ m_phase = phase::OP2;
+ break;
+
+ case 0x15: // F1 ; Z : y[l]
+ op_dau_ad(op) = dau_f1(op);
+ m_dau_temp = u16(u32(m_dau_y) >> (op_x(op) ? 16 : 0));
+ set_dau_y(op, yaau_read(op));
+ m_phase = phase::OP2;
+ break;
+
+ case 0x16: // F1 ; x = Y
+ op_dau_ad(op) = dau_f1(op);
+ m_dau_x = yaau_read(op);
+ break;
+
+ case 0x17: // F1 ; y[l] = Y
+ op_dau_ad(op) = dau_f1(op);
+ set_dau_y(op, yaau_read(op));
+ break;
+
+ case 0x19: // F1 ; y = a0 ; x = *pt++[i]
+ case 0x1b: // F1 ; y = a1 ; x = *pt++[i]
+ {
+ assert(!(op & 0x000fU)); // reserved field?
+ s64 const d(dau_f1(op));
+ s64 a(m_dau_a[BIT(op, 12)]);
+ // FIXME: is saturation applied when transferring a to y?
+ m_dau_y = u32(u64(a));
+ op_dau_ad(op) = d;
+ if (last_instruction)
+ {
+ m_rom_data = m_direct->read_word(m_xaau_pt);
+ m_phase = phase::OP2;
+ }
+ else
+ {
+ xaau_increment_pt(op_xaau_increment(op));
+ m_dau_x = m_rom_data;
+ }
+ }
+ break;
+
+ case 0x1f: // F1 ; y = Y ; x = *pt++[i]
+ op_dau_ad(op) = dau_f1(op);
+ m_dau_y = (u32(u16(yaau_read(op))) << 16) | u32(BIT(m_dau_psw, 6) ? u16(u32(m_dau_y)) : u16(0));
+ if (last_instruction)
+ {
+ m_rom_data = m_direct->read_word(m_xaau_pt);
+ m_phase = phase::OP2;
+ }
+ else
+ {
+ xaau_increment_pt(op_xaau_increment(op));
+ m_dau_x = m_rom_data;
+ }
+ break;
+
+ default:
+ throw emu_fatalerror("DSP16: inelligible op %u in cache (PC = %04X)\n", op >> 11, m_st_pcbase);
}
break;
+
+ case phase::OP2:
+ m_phase = phase::OP1;
+ switch (op >> 11)
+ {
+ case 0x04: // F1 ; Y = a1[l]
+ case 0x1c: // F1 ; Y = a0[l]
+ {
+ s64 a(m_dau_a[BIT(~op, 14)]);
+ if (!BIT(m_dau_auc, 2 + BIT(~op, 14)))
+ a = std::min<s64>(std::max<s64>(a, std::numeric_limits<s32>::min()), std::numeric_limits<s32>::max());
+ yaau_write(op, u16(u64(a) >> (op_x(op) ? 16 : 0)));
+ op_dau_ad(op) = dau_f1(op);
+ }
+ break;
+
+ case 0x08: // aT = R
+ case 0x09: // R = a0
+ case 0x0b: // R = a1
+ break;
+
+ case 0x0c: // Y = R
+ yaau_write(op, get_r(op));
+ break;
+
+ case 0x0f: // R = Y
+ break;
+
+ case 0x14: // F1 ; Y = y[l]
+ yaau_write(op, u16(u32(m_dau_y) >> (op_x(op) ? 16 : 0)));
+ break;
+
+ case 0x15: // F1 ; Z : y[l]
+ yaau_write_z(op);
+ break;
+
+ case 0x19: // F1 ; y = a0 ; x = *pt++[i]
+ case 0x1b: // F1 ; y = a1 ; x = *pt++[i]
+ case 0x1f: // F1 ; y = Y ; x = *pt++[i]
+ assert(last_instruction);
+ xaau_increment_pt(op_xaau_increment(op));
+ m_dau_x = m_rom_data;
+ break;
+
+ default:
+ throw emu_fatalerror("DSP16: op %u doesn't take two cycles to run from cache\n", op >> 11);
+ }
+ break;
+
+ case phase::PREFETCH:
+ break;
+
+ default:
+ throw emu_fatalerror("DSP16: inappropriate phase for cache execution\n");
+ }
+
+ if (phase::PREFETCH == m_phase)
+ {
+ m_phase = phase::OP1;
+ overlap_rom_data_read();
+ m_st_pcbase = (m_cache_pcbase & 0xf000U) | ((m_cache_pcbase + m_cache_ptr) & 0x0fffU);
+ }
+ else if (phase::OP1 == m_phase)
+ {
+ if (last_instruction)
+ {
+ // overlapped fetch of next instruction from ROM
+ m_cache_mode = cache::NONE;
+ m_cache[m_cache_ptr = 0] = m_direct->read_word(m_xaau_pc);
+ m_st_pcbase = m_xaau_pc;
+ }
+ else
+ {
+ if (at_limit)
+ {
+ // loop to first cached instruction
+ m_cache_ptr = 1U;
+ --m_cache_iterations;
+ }
+ else
+ {
+ // move to next cached instruction
+ m_cache_ptr = (m_cache_ptr + 1) & 0x0fU;
+ }
+ overlap_rom_data_read();
+ m_st_pcbase = (m_cache_pcbase & 0xf000U) | ((m_cache_pcbase + m_cache_ptr) & 0x0fffU);
+ }
}
}
-//-------------------------------------------------
-// disassemble - call the disassembly
-// helper function
-//-------------------------------------------------
+inline void dsp16_device_base::overlap_rom_data_read()
+{
+ assert(cache::EXECUTE == m_cache_mode);
+ assert(phase::OP1 == m_phase);
+ u16 const op(m_cache[m_cache_ptr]);
+ switch (op >> 11)
+ {
+ case 0x19: // F1 ; y = a0 ; x = *pt++[i]
+ case 0x1b: // F1 ; y = a1 ; x = *pt++[i]
+ case 0x1f: // F1 ; y = Y ; x = *pt++[i]
+ m_rom_data = m_direct->read_word(m_xaau_pt);
+ break;
+ }
+}
-util::disasm_interface *dsp16_device::create_disassembler()
+inline void dsp16_device_base::yaau_short_immediate_load(u16 op)
{
- return new dsp16a_disassembler;
+ u16 const r((op >> 9) & 0x0007U);
+ u16 const m(op & 0x01ff);
+ switch (r)
+ {
+ case 0x0: // j
+ m_yaau_j = m | ((m & m_yaau_sign) ? ~m_yaau_mask : 0);
+ break;
+ case 0x1: // k
+ m_yaau_k = m | ((m & m_yaau_sign) ? ~m_yaau_mask : 0);
+ break;
+ case 0x2: // rb
+ m_yaau_rb = m;
+ break;
+ case 0x3: // re
+ m_yaau_re = m;
+ break;
+ case 0x4: // r0
+ case 0x5: // r1
+ case 0x6: // r2
+ case 0x7: // r3
+ m_yaau_r[r & 0x0003U] = m;
+ break;
+ }
}
+inline s16 dsp16_device_base::yaau_read(u16 op)
+{
+ s16 const result(m_spaces[AS_DATA]->read_word(op_yaau_r(op)));
+ yaau_postmodify_r(op);
+ return result;
+}
-/***************************************************************************
- MEMORY ACCESSORS
-***************************************************************************/
+inline void dsp16_device_base::yaau_write(u16 op, s16 value)
+{
+ m_spaces[AS_DATA]->write_word(op_yaau_r(op), value);
+ yaau_postmodify_r(op);
+}
-inline uint32_t dsp16_device::data_read(const uint16_t& addr)
+void dsp16_device_base::yaau_write_z(u16 op)
{
- return m_data->read_word(addr);
+ u16 &r(op_yaau_r(op));
+ m_spaces[AS_DATA]->write_word(r, m_dau_temp);
+ switch (op & 0x0003U)
+ {
+ case 0x0: // *rNzp
+ // TODO: does virtual shift register addressing apply here?
+ if (m_yaau_re && (m_yaau_re == r))
+ r = m_yaau_rb;
+ else
+ ++r;
+ break;
+ case 0x1: // *rNpz
+ break;
+ case 0x2: // *rNm2
+ r += 2;
+ break;
+ case 0x3: // *rNjk;
+ r += m_yaau_k;
+ break;
+ }
+ r &= m_yaau_mask;
}
-inline void dsp16_device::data_write(const uint16_t& addr, const uint16_t& data)
+inline u64 dsp16_device_base::dau_f1(u16 op)
{
- m_data->write_word(addr, data & 0xffff);
+ s64 const &s(op_dau_as(op));
+ s64 d(0);
+ switch (op_f1(op))
+ {
+ case 0x0: // aD = p ; p = x*y
+ d = get_dau_p_aligned();
+ m_dau_p = m_dau_x * (m_dau_y >> 16);
+ break;
+ case 0x1: // aD = aS + p ; p = x*y
+ d = s + get_dau_p_aligned();
+ m_dau_p = m_dau_x * (m_dau_y >> 16);
+ break;
+ case 0x2: // p = x*y
+ m_dau_p = m_dau_x * (m_dau_y >> 16);
+ return op_dau_ad(op);
+ case 0x3: // aD = aS - p ; p = x*y
+ d = s - get_dau_p_aligned();
+ m_dau_p = m_dau_x * (m_dau_y >> 16);
+ break;
+ case 0x4: // aD = p
+ d = get_dau_p_aligned();
+ break;
+ case 0x5: // aD = aS + p
+ d = s + get_dau_p_aligned();
+ break;
+ case 0x6: // NOP
+ return op_dau_ad(op);
+ case 0x7: // aD = aS - p
+ d = s - get_dau_p_aligned();
+ break;
+ case 0x8: // aD = aS | y
+ d = s | m_dau_y;
+ break;
+ case 0x9: // aD = aS ^ y
+ d = s ^ m_dau_y;
+ break;
+ case 0xa: // aS & y
+ set_dau_psw_flags(s & m_dau_y);
+ return op_dau_ad(op);
+ case 0xb: // aS - y
+ set_dau_psw_flags(s - m_dau_y);
+ return op_dau_ad(op);
+ case 0xc: // aD = y
+ d = m_dau_y;
+ break;
+ case 0xd: // aD = aS + y
+ d = s + m_dau_y;
+ break;
+ case 0xe: // aD = aS & y
+ d = s & m_dau_y;
+ break;
+ case 0xf: // aD = aS - y
+ d = s - m_dau_y;
+ break;
+ }
+ return set_dau_psw_flags(d);
}
-inline uint32_t dsp16_device::opcode_read(const uint8_t pcOffset)
+inline u64 dsp16_device_base::dau_f2(u16 op)
{
- const uint16_t readPC = m_pc + pcOffset;
- return m_direct->read_dword(readPC);
+ s64 const &s(op_dau_as(op));
+ s64 d(0);
+ switch (op_f2(op))
+ {
+ case 0x0: // aD = aS >> 1
+ d = s >> 1;
+ break;
+ case 0x1: // aD = aS << 1
+ d = s << 1;
+ break;
+ case 0x2: // aD = aS >> 4
+ d = s >> 4;
+ break;
+ case 0x3: // aD = aS << 4
+ d = s << 4;
+ break;
+ case 0x4: // aD = aS >> 8
+ d = s >> 8;
+ break;
+ case 0x5: // aD = aS << 8
+ d = d << 8;
+ break;
+ case 0x6: // aD = aS >> 16
+ d = s >> 16;
+ break;
+ case 0x7: // aD = aS << 16
+ d = s << 16;
+ break;
+ case 0x8: // aD = p
+ d = get_dau_p_aligned();
+ break;
+ case 0x9: // aDh = aSh + 1
+ d = s64(s32(u32(u64(s)) & ~((u32(1) << 16) - 1))) + (s32(1) << 16);
+ if (BIT(m_dau_auc, op_s(op) + 4))
+ d |= op_dau_ad(op) & ((s64(1) << 16) - 1);
+ break;
+ case 0xa: // Reserved
+ throw emu_fatalerror("DSP16: reserved F2 value %01X (PC = %04X)\n", op_f2(op), m_st_pcbase);
+ case 0xb: // aD = rnd(aS)
+ // FIXME: behaviour is not clear
+ // p 3-13: "Round upper 20 bits of accumulator."
+ // p 3-14: "The contents of the source accumulator, aS, are rounded to 16 bits, and the sign-extended result is placed in aD[35 - 16] with zeroes in aD[15 - 0]."
+ // It presumably rounds to nearest, but does it yield a 16-bit or 20-bit result, and what does it do about overflow?
+ d = (s + ((0 > s) ? -(s16(1) << 15) : (s16(1) << 15))) & ~((u64(1) << 16) - 1);
+ break;
+ case 0xc: // aD = y
+ d = m_dau_y;
+ break;
+ case 0xd: // aD = aS + 1
+ d = s + 1;
+ break;
+ case 0xe: // aD = aS
+ d = s;
+ break;
+ case 0xf: // aD = -aS
+ // FIXME: does this detect negation of largest negative number as overflow?
+ d = -s;
+ break;
+ }
+ return set_dau_psw_flags(d);
}
+/***********************************************************************
+ inline helpers
+***********************************************************************/
-/***************************************************************************
- CORE EXECUTION LOOP
-***************************************************************************/
+inline bool dsp16_device_base::op_interruptible(u16 op)
+{
+ switch (op >> 11)
+ {
+ case 0x02: // R = M
+ case 0x03:
+ case 0x04: // F1 ; Y = a1[l]
+ case 0x05: // F1 ; Z : aT[l]
+ case 0x06: // F1 ; Y
+ case 0x07: // F1 ; aT[l] = Y
+ case 0x08: // aT = R
+ case 0x09: // R = a0
+ case 0x0a: // R = N
+ case 0x0b: // R = a1
+ case 0x0c: // Y = R
+ case 0x0d: // Z : R
+ case 0x0f: // R = Y
+ case 0x12: // ifc CON F2
+ case 0x13: // if CON F2
+ case 0x14: // F1 ; Y = y[l]
+ case 0x15: // F1 ; Z : y[l]
+ case 0x16: // F1 ; x = Y
+ case 0x17: // F1 ; y[l] = Y
+ case 0x19: // y = a0 ; x = *pt++[i]
+ case 0x1b: // y = a1 ; x = *pt++[i]
+ case 0x1c: // F1 ; Y = a0[l]
+ case 0x1d: // F1 ; Z : y ; x = *pt++[i]
+ case 0x1f: // F1 ; y = Y ; x = *pt++[i]
+ return true;
+ case 0x00: // goto JA
+ case 0x01:
+ case 0x0e: // do K { instre1...instrNI } # redo K
+ case 0x10: // call JA
+ case 0x11:
+ case 0x18: // goto B
+ case 0x1a: // if CON # icall
+ case 0x1e: // Reserved
+ default:
+ return false;
+ };
+}
+
+inline bool dsp16_device_base::check_predicate()
+{
+ bool const result(FLAGS_PRED_FALSE != (m_flags & FLAGS_PRED_MASK));
+ set_predicate(FLAGS_PRED_NONE);
+ return result;
+}
+
+inline u16 &dsp16_device_base::set_xaau_pc_offset(u16 offset)
+{
+ m_xaau_pc = (m_xaau_pc & 0xf000U) | (offset & 0x0fffU);
+ if (m_iack_out)
+ m_xaau_pi = m_xaau_pc;
+ return m_xaau_pc;
+}
+
+inline s16 dsp16_device_base::get_r(u16 op)
+{
+ switch (op_r(op))
+ {
+ case 0x00: // r0 (u)
+ case 0x01: // r1 (u)
+ case 0x02: // r2 (u)
+ case 0x03: // r3 (u)
+ return m_yaau_r[op_r(op)];
+ case 0x04: // j (s)
+ return m_yaau_j;
+ case 0x05: // k (s)
+ return m_yaau_k;
+ case 0x06: // rb (u)
+ return m_yaau_rb;
+ case 0x07: // re (u)
+ return m_yaau_re;
+ case 0x08: // pt
+ return m_xaau_pt;
+ case 0x09: // pr
+ return m_xaau_pr;
+ case 0x0a: // pi
+ return m_xaau_pi;
+ case 0x0b: // i (s)
+ return m_xaau_i;
+ case 0x10: // x
+ return m_dau_x;
+ case 0x11: // y
+ return u16(u32(m_dau_y >> 16));
+ case 0x12: // yl
+ return u16(u32(m_dau_y));
+ break;
+ case 0x13: // auc (u)
+ return m_dau_auc;
+ case 0x14: // psw
+ return m_dau_psw = (m_dau_psw & 0xfe10U) | (u16(u64(m_dau_a[0]) >> 32) & 0x000fU) | (u16(u64(m_dau_a[1]) >> 27) & 0x01e0U);
+ case 0x15: // c0 (s)
+ case 0x16: // c1 (s)
+ case 0x17: // c2 (s)
+ return m_dau_c[op_r(op) - 0x15];
+ case 0x18: // sioc
+ return m_sio_sioc;
+ case 0x19: // srta
+ logerror("DSP16: unimplemented get SRTA\n");
+ return 0;
+ case 0x1a: // sdx
+ logerror("DSP16: unimplemented get SDX\n");
+ return 0;
+ case 0x1b: // tdms
+ logerror("DSP16: unimplemented get TDMS\n");
+ return 0;
+ case 0x1c: // pioc
+ return m_pio_pioc;
+ case 0x1d: // pdx0
+ case 0x1e: // pdx1
+ return pio_pdx_read(BIT(op_r(op), 1));
+ default:
+ throw emu_fatalerror("DSP16: invalid R value %02X (PC = %04X)\n", op_r(op), m_st_pcbase);
+ }
+}
+
+inline void dsp16_device_base::set_r(u16 op, s16 value)
+{
+ switch (op_r(op))
+ {
+ case 0x00: // r0 (u)
+ case 0x01: // r1 (u)
+ case 0x02: // r2 (u)
+ case 0x03: // r3 (u)
+ m_yaau_r[op_r(op)] = value & m_yaau_mask;
+ break;
+ case 0x04: // j (s)
+ m_yaau_j = (value & m_yaau_mask) | ((value & m_yaau_sign) ? ~m_yaau_mask : 0);
+ break;
+ case 0x05: // k (s)
+ m_yaau_k = (value & m_yaau_mask) | ((value & m_yaau_sign) ? ~m_yaau_mask : 0);
+ break;
+ case 0x06: // rb (u)
+ m_yaau_rb = value & m_yaau_mask;
+ break;
+ case 0x07: // re (u)
+ m_yaau_re = value & m_yaau_mask;
+ break;
+ case 0x08: // pt
+ m_xaau_pt = value;
+ break;
+ case 0x09: // pr
+ m_xaau_pr = value;
+ break;
+ case 0x0a: // pi
+ // FIXME: reset PRNG
+ if (!m_iack_out)
+ m_xaau_pi = value;
+ break;
+ case 0x0b: // i (s)
+ m_xaau_i = (value & XAAU_I_MASK) | ((value & XAAU_I_SIGN) ? XAAU_I_EXT : 0);
+ break;
+ case 0x10: // x
+ m_dau_x = value;
+ break;
+ case 0x11: // y
+ m_dau_y = (u32(u16(value)) << 16) | u32(BIT(m_dau_auc, 6) ? u16(u32(m_dau_y)) : u16(0));
+ break;
+ case 0x12: // yl
+ m_dau_y = (m_dau_y & 0xffff'0000) | u32(u16(value));
+ break;
+ case 0x13: // auc (u)
+ m_dau_auc = u8(value & 0x007f);
+ break;
+ case 0x14: // psw
+ m_dau_psw = value;
+ m_dau_a[0] = u64(u32(m_dau_a[0])) | (u64(value & 0x000fU) << 32) | (BIT(value, 3) ? DAU_A_EXT : 0U);
+ m_dau_a[1] = u64(u32(m_dau_a[1])) | (u64(value & 0x01e0U) << 27) | (BIT(value, 8) ? DAU_A_EXT : 0U);
+ break;
+ case 0x15: // c0 (s)
+ case 0x16: // c1 (s)
+ case 0x17: // c2 (s)
+ m_dau_c[op_r(op) - 0x15] = u8(u16(value));
+ break;
+ case 0x18: // sioc
+ sio_sioc_write(value);
+ break;
+ case 0x19: // srta
+ logerror("DSP16: unimplemented SRTA = %04X\n", value);
+ break;
+ case 0x1a: // sdx
+ LOGSIO("DSP16: write SDX = %04X%s (PC = %04X)\n", value, pio_obe_status() ? "" : " - overrun", m_st_pcbase);
+ if (pio_obe_status())
+ LOGINT("DSP16 clear OBE flag (PC = %04X)\n", m_st_pcbase);
+ m_sio_obuf = value;
+ m_pio_pioc &= ~(u16(1) << 3);
+ break;
+ case 0x1b: // tdms
+ logerror("DSP16: unimplemented TDMS = %04X\n", value);
+ break;
+ case 0x1c: // pioc
+ pio_pioc_write(value);
+ break;
+ case 0x1d: // pdx0
+ case 0x1e: // pdx1
+ pio_pdx_write(BIT(op_r(op), 1), value);
+ break;
+ default:
+ throw emu_fatalerror("DSP16: invalid R value %02X (PC = %04X)\n", op_r(op), m_st_pcbase);
+ }
+}
-//-------------------------------------------------
-// execute_min_cycles - return minimum number of
-// cycles it takes for one instruction to execute
-//-------------------------------------------------
+inline void dsp16_device_base::yaau_postmodify_r(u16 op)
+{
+ u16 &r(op_yaau_r(op));
+ switch (op & 0x0003U)
+ {
+ case 0x0: // *rN
+ break;
+ case 0x1: // *rN++
+ if (m_yaau_re && (m_yaau_re == r))
+ r = m_yaau_rb;
+ else
+ ++r;
+ break;
+ case 0x2: // *rN--
+ --r;
+ break;
+ case 0x3: // *rN++j
+ r += m_yaau_j;
+ break;
+ }
+ r &= m_yaau_mask;
+}
-uint32_t dsp16_device::execute_min_cycles() const
+inline void dsp16_device_base::set_dau_y(u16 op, s16 value)
{
- return 1;
+ if (op_x(op))
+ {
+ bool const clear(!BIT(m_dau_psw, 6));
+ m_dau_y = (u32(u16(value)) << 16) | u32(clear ? u16(0) : u16(u32(m_dau_y)));
+ }
+ else
+ {
+ m_dau_y = (m_dau_y & ~((s32(1) << 16) - 1)) | u32(u16(value));
+ }
}
+inline void dsp16_device_base::set_dau_at(u16 op, s16 value)
+{
+ s64 &at(op_dau_at(op));
+ if (op_x(op))
+ {
+ bool const clear(!BIT(m_dau_psw, 4 + op_d(~op)));
+ at = s32((u32(u16(value)) << 16) | u32(clear ? u16(0) : u16(u64(at))));
+ }
+ else
+ {
+ at = (at & ~((s64(1) << 16) - 1)) | u64(u16(value));
+ }
+}
-//-------------------------------------------------
-// execute_max_cycles - return maximum number of
-// cycles it takes for one instruction to execute
-//-------------------------------------------------
+inline u64 dsp16_device_base::set_dau_psw_flags(s64 d)
+{
+ m_dau_psw &= 0x0fffU;
+ bool const negative(d & DAU_A_SIGN);
+ if (negative)
+ m_dau_psw |= 0x8000U;
+ if (!(d & DAU_A_MASK))
+ m_dau_psw |= 0x4000U;
+ if ((d >> 36) != (negative ? -1 : 0))
+ m_dau_psw |= 0x2000U;
+ if (((d >> 32) & ((1 << 4) - 1)) != (BIT(d, 31) ? 15 : 0))
+ m_dau_psw |= 0x1000U;
+ if (negative)
+ return d | DAU_A_EXT;
+ else
+ return d & DAU_A_MASK;
+}
-uint32_t dsp16_device::execute_max_cycles() const
+inline u64 dsp16_device_base::get_dau_p_aligned() const
{
- return 1;
+ // TODO: manual is contradictory
+ // p 2-10: "Bits 1 and 2 of the accumulator are not changed by the load of the accumulator with the data in p, since 00 is added to or copied into these accumulator bits as indicated in Figure 2-8."
+ // I'm reading this as copying p to aD clears the low two bits, but adding it leaves them unchanged.
+ switch (dau_auc_align())
+ {
+ case 0x0:
+ return m_dau_p;
+ case 0x1:
+ return m_dau_p >> 2;
+ case 0x2:
+ return u64(m_dau_p) << 2;
+ case 0x3:
+ default:
+ throw emu_fatalerror("DSP16: reserved ALIGN value %01X (PC = %04X)\n", dau_auc_align(), m_st_pcbase);
+ }
}
+inline bool dsp16_device_base::op_dau_con(u16 op)
+{
+ bool result;
+ u16 const con(op_con(op));
+ switch (con >> 1)
+ {
+ case 0x0: // mi/pl
+ result = dau_psw_lmi();
+ break;
+ case 0x1: // eq/ne
+ result = dau_psw_leq();
+ break;
+ case 0x2: // lvs/lvc
+ result = dau_psw_llv();
+ break;
+ case 0x3: // mvs/mvc
+ result = dau_psw_lmv();
+ break;
+ case 0x4: // heads/tails
+ throw emu_fatalerror("DSP16: unimplemented CON value %02X (PC = %04X)\n", con, m_st_pcbase);
+ case 0x5: // c0ge/c0lt
+ case 0x6: // c1ge/c1lt
+ result = m_dau_c[(con >> 1) - 0x5]++ >= 0;
+ break;
+ case 0x7: // true/false
+ result = true;
+ break;
+ case 0x8: // gt/le
+ result = !dau_psw_lmi() && !dau_psw_leq();
+ break;
+ default:
+ throw emu_fatalerror("DSP16: reserved CON value %02X (PC = %04X)\n", con, m_st_pcbase);
+ }
+ return BIT(con, 0) ? !result : result;
+}
-//-------------------------------------------------
-// execute_input_lines - return the number of
-// input/interrupt lines
-//-------------------------------------------------
+/***********************************************************************
+ serial I/O
+***********************************************************************/
-uint32_t dsp16_device::execute_input_lines() const
+void dsp16_device_base::sio_sioc_write(u16 value)
{
- return 1;
+ static constexpr unsigned DIVIDERS[4] = { 4U, 12U, 16U, 20U };
+ value &= 0x03ffU;
+ LOGSIO(
+ "DSP16: write SIOC = %03X: "
+ "LD %cCK/n->%cCK/n, CLK CKI/%u->CKI/%u, %cSB->%cSB first, "
+ "OLD %s->%s, ILD %s->%s, OCK %s->%s, ICK %s->%s, OLEN %u->%u, ILEN %u->%u "
+ "(PC = %04X)\n",
+ value,
+ sio_ld_ock() ? 'O' : 'I', BIT(value, 9) ? 'O' : 'I',
+ DIVIDERS[(m_sio_sioc >> 7) & 0x0003U], DIVIDERS[(value >> 7) & 0x0003U],
+ sio_msb_first() ? 'M' : 'L', BIT(value, 6) ? 'M' : 'L',
+ sio_old_active() ? "active" : "passive", BIT(value, 5) ? "active" : "passive",
+ sio_ild_active() ? "active" : "passive", BIT(value, 4) ? "active" : "passive",
+ sio_ock_active() ? "active" : "passive", BIT(value, 3) ? "active" : "passive",
+ sio_ick_active() ? "active" : "passive", BIT(value, 2) ? "active" : "passive",
+ sio_olen(), BIT(value, 1) ? 8U : 16U,
+ sio_ilen(), BIT(value, 0) ? 8U : 16U,
+ m_st_pcbase);
+
+ //bool const ock_change(BIT(value ^ m_sio_sioc, 5));
+ //bool const ild_change(BIT(value ^ m_sio_sioc, 4));
+ bool const ock_change(BIT(value ^ m_sio_sioc, 3));
+ bool const ick_change(BIT(value ^ m_sio_sioc, 2));
+ m_sio_sioc = value;
+
+ // we're using treating high-impedance and high as the same thing
+ if (!m_sio_clk)
+ {
+ if (ick_change)
+ m_ick_cb(sio_ick_active() ? 0 : 1);
+ if (ock_change)
+ m_ock_cb(sio_ock_active() ? 0 : 1);
+ }
}
+inline void dsp16_device_base::sio_ick_active_edge()
+{
+ // check for start of transaction
+ bool const ild(sio_ild_active() ? !m_sio_ld : (CLEAR_LINE != m_ild_in));
+ if (ASSERT_LINE != m_ild_in)
+ m_ild_in = CLEAR_LINE;
+ if (ild)
+ {
+ // FIXME: implement enough to put something here
+ m_sio_flags |= SIO_FLAGS_ILD;
+ }
+ else
+ {
+ m_sio_flags &= ~SIO_FLAGS_ILD;
+ }
+
+ // update internal LD divider if it's being driven by ICK
+ if (sio_ld_ick())
+ sio_step_ld_div();
+}
-void dsp16_device::execute_set_input(int inputnum, int state)
+inline void dsp16_device_base::sio_ock_active_edge()
{
- // Only has one external IRQ line
+ // check for load opportunity
+ bool const old(sio_old_active() ? !m_sio_ld : (CLEAR_LINE != m_old_in));
+ if (ASSERT_LINE != m_old_in)
+ m_old_in = CLEAR_LINE;
+ if (old)
+ {
+ if ((SIO_FLAGS_NONE == (m_sio_flags & SIO_FLAGS_OLD)) && !m_sio_ofsr && !pio_obe_status())
+ {
+ bool const eight_bit(8U == sio_olen());
+ LOGSIO(
+ "DSP16: load OSR = %04X & %04X %cSB first (PC = %04X)\n",
+ m_sio_obuf, !eight_bit ? 0xffffU : sio_msb_first() ? 0xff00U : 0x00ffU, sio_msb_first() ? 'M' : 'L', m_st_pcbase);
+ LOGINT("DSP16: set OBE flag (PC = %04X)\n", m_st_pcbase);
+
+ m_sio_osr = sio_msb_first() ? bitswap<16>(m_sio_obuf, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) : m_sio_obuf;
+ m_sio_ofsr = 0xffffU;
+ if (eight_bit)
+ {
+ m_sio_osr |= 0xff00U;
+ m_sio_ofsr >>= 8;
+ }
+
+ m_pio_pioc |= u16(1) << 3;
+ }
+ m_sio_flags |= SIO_FLAGS_OLD;
+ }
+ else
+ {
+ m_sio_flags &= ~SIO_FLAGS_OLD;
+ }
+
+ // shift out serial data
+ if (BIT(m_sio_osr, 0) != m_do_out)
+ m_do_cb(m_do_out = BIT(m_sio_osr, 0));
+ if (BIT(~m_sio_ofsr, 0) != m_ose_out)
+ {
+ LOGSIO("DSP16: OSE %u->%u (PC = %04X)\n", m_ose_out, BIT(~m_sio_ofsr, 0), m_st_pcbase);
+ m_ose_cb(m_ose_out = BIT(~m_sio_ofsr, 0));
+ }
+ m_sio_osr = (m_sio_osr >> 1) | 0x8000U;
+ m_sio_ofsr >>= 1;
+
+ // update internal LD divider if it's being driven by OCK
+ if (sio_ld_ock())
+ sio_step_ld_div();
}
+inline void dsp16_device_base::sio_step_ld_div()
+{
+ if (m_sio_ld_div)
+ {
+ --m_sio_ld_div;
+ }
+ else
+ {
+ m_sio_ld = m_sio_ld ? 0U : 1U;
+ m_sio_ld_div = (16 / 2) - 1;
+ if (sio_ild_active())
+ m_ild_cb(m_sio_ld);
+ if (sio_old_active())
+ m_old_cb(m_sio_ld);
+ }
+}
-void dsp16_device::execute_run()
+/***********************************************************************
+ parallel I/O
+***********************************************************************/
+
+void dsp16_device_base::pio_pioc_write(u16 value)
{
- // HACK TO MAKE CPU DO NOTHING.
- // REMOVE IF DEVELOPING CPU CORE.
- m_icount = 0;
- return;
+ LOGPIO(
+ "DSP16: write PIOC = %04X: "
+ "STROBE %uT->%uT, PODS %s->%s, PIDS %s->%s, S/C %u->%u, "
+ "IBF %s->%s, OBE %s->%s, PIDS %s->%s, PODS %s->%s, INT %s->%s "
+ "(PC = %04X)\n",
+ value,
+ pio_strobe(), ((value >> 13) & 0x0003U) + 1,
+ pio_pods_active() ? "active" : "passive", BIT(value, 12) ? "active" : "passive",
+ pio_pids_active() ? "active" : "passive", BIT(value, 11) ? "active" : "passive",
+ BIT(m_pio_pioc, 10), BIT(value, 10),
+ pio_ibf_enable() ? "en" : "dis", BIT(value, 9) ? "en" : "dis",
+ pio_obe_enable() ? "en" : "dis", BIT(value, 8) ? "en" : "dis",
+ pio_pids_enable() ? "en" : "dis", BIT(value, 7) ? "en" : "dis",
+ pio_pods_enable() ? "en" : "dis", BIT(value, 6) ? "en" : "dis",
+ pio_int_enable() ? "en" : "dis", BIT(value, 5) ? "en" : "dis",
+ m_st_pcbase);
- do
+ m_pio_pioc = (m_pio_pioc & 0x801fU) | (value & 0x7fe0U);
+ m_int_enable[1] = (value >> 5) & 0x001fU;
+ if (!pio_pids_active())
{
- // debugging
- m_ppc = m_pc; // copy PC to previous PC
- debugger_instruction_hook(this, m_pc);
+ m_pio_pids_cnt = 0U;
+ if (!m_pids_out)
+ m_pids_cb(m_pids_out = 1U); // actually high-impedance
+ }
+ if (!pio_pods_active())
+ {
+ m_pio_pods_cnt = 0U;
+ if (!m_pods_out)
+ {
+ m_pods_cb(m_pods_out = 1U); // actually high-impedance
+ m_pdb_w_cb(machine().dummy_space(), m_psel_out, 0xffffU, 0x0000U);
+ }
+ }
+}
- // instruction fetch & execute
- uint8_t cycles;
- uint8_t pcAdvance;
- const uint16_t op = opcode_read();
- execute_one(op, cycles, pcAdvance);
+u16 dsp16_device_base::pio_pdx_read(u16 sel)
+{
+ bool const active(pio_pids_active());
+ LOGPIO("DSP16: read PDX%u = %04X %s (PC = %04X)\n", sel, m_pio_pdx_in, active ? "active" : "passive", m_st_pcbase);
+ if (pio_pids_status())
+ LOGINT("DSP16: clear PIDS flag (PC = %04X)\n", m_st_pcbase);
+ m_pio_pioc &= ~(u16(1) << 2);
- // step
- m_pc += pcAdvance;
- m_icount -= cycles;
+ if (sel != m_psel_out)
+ {
+ LOGPIO("DSP16: PSEL %u->%u (PC = %04X)\n", m_psel_out, sel, m_st_pcbase);
+ m_psel_cb(m_psel_out = sel);
+ }
- // The 16 bit PI "shadow" register gets set to PC on each instruction except
- // when an interrupt service routine is active (TODO: Interrupt check) (page 2-4)
- m_pi = m_pc;
+ if (active)
+ {
+ if (m_pio_pids_cnt)
+ {
+ assert(!m_pids_out);
+ logerror("DSP16: PDX%u active read while PIDS still asserted (PC = %04X)\n", sel, m_st_pcbase);
+ }
+ else
+ {
+ assert(m_pids_out);
+ m_pids_cb(m_pids_out = 0U);
+ }
+ m_pio_pids_cnt = pio_strobe() + 1; // decremented this cycle
+ }
- } while (m_icount > 0);
+ return m_pio_pdx_in;
}
-#include "dsp16ops.hxx"
+void dsp16_device_base::pio_pdx_write(u16 sel, u16 value)
+{
+ bool const active(pio_pods_active());
+ LOGPIO("DSP16: write PDX%u = %04X %s (PC = %04X)\n", sel, value, active ? "active" : "passive", m_st_pcbase);
+ if (pio_pods_status())
+ LOGINT("DSP16: clear PODS flag (PC = %04X)\n", m_st_pcbase);
+ m_pio_pioc &= ~(u16(1) << 1);
+ m_pio_pdx_out = value;
+
+ if (sel != m_psel_out)
+ {
+ LOGPIO("DSP16: PSEL %u->%u (PC = %04X)\n", m_psel_out, sel, m_st_pcbase);
+ m_psel_cb(m_psel_out = sel);
+ }
+
+ if (active)
+ {
+ if (m_pio_pods_cnt)
+ {
+ assert(!m_pods_out);
+ logerror("DSP16: PDX%u active write while PODS still asserted (PC = %04X)\n", sel, m_st_pcbase);
+ }
+ else
+ {
+ assert(m_pods_out);
+ m_pods_cb(m_pods_out = 0U);
+ m_pdb_w_cb(machine().dummy_space(), sel, value, 0xffffU);
+ }
+ m_pio_pods_cnt = pio_strobe() + 1; // decremented this cycle
+ }
+}
+
+
+/***************************************************************************
+ DSP16 SPECIALISATION
+***************************************************************************/
+
+dsp16_device::dsp16_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : dsp16_device_base(
+ mconfig, DSP16, tag, owner, clock,
+ 9,
+ address_map_constructor(FUNC(dsp16_device::data_map), this))
+ , m_rom(*this, DEVICE_SELF, 0x0800)
+{
+}
+
+void dsp16_device::external_memory_enable(address_space &space, bool enable)
+{
+ // manual implies the DSP16 doesn't support enabling both internal and external ROM at the same time (page 2-2)
+ // this assumes internal ROM is mirrored above 2KiB, but actual hardware behaviour is unknown
+ space.unmap_read(0x0000, 0xffff);
+ if (enable)
+ space.install_read_handler(0x0000, 0xffff, read16_delegate(FUNC(dsp16_device::external_memory_r<0x0000>), this));
+ else
+ space.install_rom(0x0000, 0x07ff, 0xf800, &m_rom[0]);
+}
+
+void dsp16_device::data_map(address_map &map)
+{
+ map.global_mask(0x01ff);
+ map.unmap_value_high();
+ map(0x0000, 0x01ff).ram();
+}
+
+
+/***************************************************************************
+ DSP16A SPECIALISATION
+***************************************************************************/
+
+dsp16a_device::dsp16a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
+ : dsp16_device_base(
+ mconfig, DSP16A, tag, owner, clock,
+ 16,
+ address_map_constructor(FUNC(dsp16a_device::data_map), this))
+ , m_rom(*this, DEVICE_SELF, 0x1000)
+{
+}
+
+void dsp16a_device::external_memory_enable(address_space &space, bool enable)
+{
+ space.unmap_read(0x0000, 0xffff);
+ if (enable)
+ {
+ space.install_read_handler(0x0000, 0xffff, read16_delegate(FUNC(dsp16a_device::external_memory_r<0x0000>), this));
+ }
+ else
+ {
+ space.install_rom(0x0000, 0x0fff, &m_rom[0]);
+ space.install_read_handler(0x1000, 0xffff, read16_delegate(FUNC(dsp16a_device::external_memory_r<0x1000>), this));
+ }
+}
+
+void dsp16a_device::data_map(address_map &map)
+{
+ map.global_mask(0x07ff);
+ map.unmap_value_high();
+ map(0x0000, 0x07ff).ram();
+}