Cycle-accurate DSP16 core (disabled in QSound for performance reasons)

This commit is contained in:
Vas Crabb 2018-03-15 19:02:43 +11:00
parent 25652d6455
commit 0bf88bda8f
11 changed files with 2496 additions and 1505 deletions

View File

@ -261,7 +261,6 @@ if (CPUS["DSP16A"]~=null) then
files {
MAME_DIR .. "src/devices/cpu/dsp16/dsp16.cpp",
MAME_DIR .. "src/devices/cpu/dsp16/dsp16.h",
MAME_DIR .. "src/devices/cpu/dsp16/dsp16ops.hxx",
}
end

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,8 @@
// 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
***************************************************************************/
@ -13,172 +11,443 @@
#pragma once
#include <utility>
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> dsp16_device
#define MCFG_DSP16_EXM(exm) \
downcast<dsp16_device_base &>(*device).exm_w(exm);
class dsp16_device : public cpu_device
#define MCFG_DSP16_EXM_HIGH() \
downcast<dsp16_device_base &>(*device).exm_w(1);
#define MCFG_DSP16_EXM_LOW() \
downcast<dsp16_device_base &>(*device).exm_w(0);
#define MCFG_DSP16_IACK_CB(obj) \
downcast<dsp16_device_base &>(*device).set_iack_cb(DEVCB_##obj);
#define MCFG_DSP16_ICK_CB(obj) \
downcast<dsp16_device_base &>(*device).set_ick_cb(DEVCB_##obj);
#define MCFG_DSP16_ILD_CB(obj) \
downcast<dsp16_device_base &>(*device).set_ild_cb(DEVCB_##obj);
#define MCFG_DSP16_DO_CB(obj) \
downcast<dsp16_device_base &>(*device).set_do_cb(DEVCB_##obj);
#define MCFG_DSP16_OCK_CB(obj) \
downcast<dsp16_device_base &>(*device).set_ock_cb(DEVCB_##obj);
#define MCFG_DSP16_OLD_CB(obj) \
downcast<dsp16_device_base &>(*device).set_old_cb(DEVCB_##obj);
#define MCFG_DSP16_OSE_CB(obj) \
downcast<dsp16_device_base &>(*device).set_ose_cb(DEVCB_##obj);
#define MCFG_DSP16_PIO_R_CB(obj) \
downcast<dsp16_device_base &>(*device).set_pio_r_cb(DEVCB_##obj);
#define MCFG_DSP16_PIO_W_CB(obj) \
downcast<dsp16_device_base &>(*device).set_pio_w_cb(DEVCB_##obj);
#define MCFG_DSP16_PDB_W_CB(obj) \
downcast<dsp16_device_base &>(*device).set_pdb_w_cb(DEVCB_##obj);
#define MCFG_DSP16_PSEL_CB(obj) \
downcast<dsp16_device_base &>(*device).set_psel_cb(DEVCB_##obj);
#define MCFG_DSP16_PIDS_CB(obj) \
downcast<dsp16_device_base &>(*device).set_pids_cb(DEVCB_##obj);
#define MCFG_DSP16_PODS_CB(obj) \
downcast<dsp16_device_base &>(*device).set_pods_cb(DEVCB_##obj);
class dsp16_device_base : public cpu_device
{
public:
// construction/destruction
dsp16_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
DECLARE_WRITE_LINE_MEMBER(exm_w);
// public interfaces
// interrupt output callbacks
template <typename Obj> devcb_base *set_iack_cb(Obj &&cb) { return &m_iack_cb.set_callback(std::forward<Obj>(cb)); }
// serial output callbacks
template <typename Obj> devcb_base *set_ick_cb(Obj &&cb) { return &m_ick_cb.set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base *set_ild_cb(Obj &&cb) { return &m_ild_cb.set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base *set_do_cb(Obj &&cb) { return &m_do_cb.set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base *set_ock_cb(Obj &&cb) { return &m_ock_cb.set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base *set_old_cb(Obj &&cb) { return &m_old_cb.set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base *set_ose_cb(Obj &&cb) { return &m_ose_cb.set_callback(std::forward<Obj>(cb)); }
// high-level active parallel I/O callbacks
template <typename Obj> devcb_base *set_pio_r_cb(Obj &&cb) { return &m_pio_r_cb.set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base *set_pio_w_cb(Obj &&cb) { return &m_pio_w_cb.set_callback(std::forward<Obj>(cb)); }
// low-level parallel I/O callbacks
template <typename Obj> devcb_base *set_pdb_w_cb(Obj &&cb) { return &m_pdb_w_cb.set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base *set_psel_cb(Obj &&cb) { return &m_psel_cb.set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base *set_pids_cb(Obj &&cb) { return &m_pids_cb.set_callback(std::forward<Obj>(cb)); }
template <typename Obj> devcb_base *set_pods_cb(Obj &&cb) { return &m_pods_cb.set_callback(std::forward<Obj>(cb)); }
// interrupt outputs
DECLARE_READ_LINE_MEMBER(iack_r) { return m_iack_out; }
// serial outputs
DECLARE_READ_LINE_MEMBER(ick_r) { return sio_ick_active() ? m_sio_clk : 1; }
DECLARE_READ_LINE_MEMBER(ild_r) { return sio_ild_active() ? m_sio_ld : 1; }
DECLARE_READ_LINE_MEMBER(do_r) { return m_do_out; }
DECLARE_READ_LINE_MEMBER(ock_r) { return sio_ock_active() ? m_sio_clk : 1; }
DECLARE_READ_LINE_MEMBER(old_r) { return sio_old_active() ? m_sio_ld : 1; }
DECLARE_READ_LINE_MEMBER(ose_r) { return m_ose_out; }
// high-level passive parallel I/O handlers
DECLARE_READ16_MEMBER(pio_r);
DECLARE_WRITE16_MEMBER(pio_w);
// parallel I/O outputs
DECLARE_READ_LINE_MEMBER(psel_r) { return m_psel_out; }
DECLARE_READ_LINE_MEMBER(pids_r) { return m_pids_out; }
DECLARE_READ_LINE_MEMBER(pods_r) { return m_pods_out; }
protected:
enum
{
DSP16_I, // ROM Address Arithmetic Unit (XAAU)
DSP16_PC,
DSP16_PT,
DSP16_PR,
DSP16_PI,
DSP16_J, // RAM Address Arithmetic Unit (YAAU)
DSP16_K,
DSP16_RB,
DSP16_RE,
DSP16_R0,
DSP16_R1,
DSP16_R2,
DSP16_R3,
DSP16_X, // Data Arithmetic Unit (DAU)
DSP16_Y,
DSP16_P,
DSP16_A0,
DSP16_A1,
DSP16_AUC,
DSP16_PSW,
DSP16_C0,
DSP16_C1,
DSP16_C2,
DSP16_SIOC,
DSP16_SRTA,
DSP16_SDX,
DSP16_PIOC,
DSP16_PDX0,
DSP16_PDX1
};
// construction/destruction
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);
// device-level overrides
// device_t implementation
virtual void device_resolve_objects() override;
virtual void device_start() override;
virtual void device_reset() override;
// device_execute_interface overrides
virtual uint64_t execute_clocks_to_cycles(uint64_t clocks) const override { return (clocks + 2 - 1) / 2; } // internal /2 divider
virtual uint64_t execute_cycles_to_clocks(uint64_t cycles) const override { return (cycles * 2); } // internal /2 divider
virtual uint32_t execute_min_cycles() const override;
virtual uint32_t execute_max_cycles() const override;
virtual uint32_t execute_input_lines() const override;
// device_execute_interface implementation
virtual u64 execute_clocks_to_cycles(u64 clocks) const override { return (clocks + 2 - 1) >> 1; }
virtual u64 execute_cycles_to_clocks(u64 cycles) const override { return cycles << 1; }
virtual u32 execute_input_lines() const override { return 5U; }
virtual void execute_run() override;
virtual void execute_set_input(int inputnum, int state) override;
// device_memory_interface overrides
// device_memory_interface implementation
virtual space_config_vector memory_space_config() const override;
// device_state_interface overrides
virtual void state_string_export(const device_state_entry &entry, std::string &str) const override;
// device_state_interface implementation
virtual void state_import(device_state_entry const &entry) override;
virtual void state_export(device_state_entry const &entry) override;
// device_disasm_interface overrides
// device_disasm_interface implementation
virtual util::disasm_interface *create_disassembler() override;
// address spaces
const address_space_config m_program_config;
const address_space_config m_data_config;
// for specialisations to override
virtual void external_memory_enable(address_space &space, bool enable) = 0;
// CPU registers
// ROM Address Arithmetic Unit (XAAU) (page 2-4)
uint16_t m_i; // 12 bits
uint16_t m_pc;
uint16_t m_pt;
uint16_t m_pr;
uint16_t m_pi;
template <offs_t Base> DECLARE_READ16_MEMBER(external_memory_r);
// RAM Address Arithmetic Unit (YAAU) (page 2-6)
uint16_t m_j; // Signed
uint16_t m_k; // Signed
uint16_t m_rb;
uint16_t m_re;
uint16_t m_r0;
uint16_t m_r1;
uint16_t m_r2;
uint16_t m_r3;
private:
// state registration indices
enum
{
DSP16_PT = 1, DSP16_PR, DSP16_PI, DSP16_I,
DSP16_R0, DSP16_R1, DSP16_R2, DSP16_R3, DSP16_RB, DSP16_RE, DSP16_J, DSP16_K,
DSP16_X, DSP16_Y, DSP16_P, DSP16_A0, DSP16_A1, DSP16_C0, DSP16_C1, DSP16_C2, DSP16_AUC, DSP16_PSW,
DSP16_YH, DSP16_A0H, DSP16_A1H, DSP16_YL, DSP16_A0L, DSP16_A1L
};
// Data Arithmetic Unit (DAU) (page 2-6)
uint16_t m_x;
uint32_t m_y;
uint32_t m_p;
uint64_t m_a0; // 36 bits
uint64_t m_a1; // 36 bits
uint8_t m_auc; // 6 bits
uint16_t m_psw;
uint8_t m_c0;
uint8_t m_c1;
uint8_t m_c2;
// masks for registers that aren't power-of-two sizes
enum : s16
{
XAAU_I_SIGN = s16(1) << (12 - 1),
XAAU_I_MASK = (s16(1) << 12) - 1,
XAAU_I_EXT = ~XAAU_I_MASK
};
enum : s64
{
DAU_A_SIGN = s64(1) << (36 - 1),
DAU_A_MASK = (s64(1) << 36) - 1,
DAU_A_EXT = ~DAU_A_MASK
};
// Serial and parallel interfaces (TODO: More here (page 2-13))
uint16_t m_sioc;
uint16_t m_srta;
uint16_t m_sdx;
uint16_t m_pioc;
uint16_t m_pdx0; // pdx0 & pdx1 refer to the same physical register (page 6-1)
uint16_t m_pdx1; // but we keep them separate for logic's sake.
// execution state
enum class cache : u8 { NONE, LOAD, EXECUTE };
enum class phase : u8 { PURGE, OP1, OP2, PREFETCH };
enum flags : u8
{
FLAGS_PRED_NONE = 0x00U,
FLAGS_PRED_TRUE = 0x01U,
FLAGS_PRED_FALSE = 0x02U,
FLAGS_PRED_MASK = 0x03U,
// internal stuff
uint16_t m_ppc;
FLAGS_IACK_NONE = 0x00U,
FLAGS_IACK_SET = 0x04U,
FLAGS_IACK_CLEAR = 0x08U,
FLAGS_IACK_MASK = 0x0cU,
// This CPU core handles the cache as more of a loop than 15 separate memory elements.
// It's a bit of a hack, but it's easier this way (for now).
uint16_t m_cacheStart;
uint16_t m_cacheEnd;
uint16_t m_cacheRedoNextPC;
uint16_t m_cacheIterations;
static const uint16_t CACHE_INVALID = 0xffff;
FLAGS_NONE = FLAGS_PRED_NONE | FLAGS_IACK_NONE
};
friend constexpr flags operator~(flags);
friend constexpr flags operator&(flags, flags);
friend constexpr flags operator|(flags, flags);
friend flags &operator&=(flags &, flags);
friend flags &operator|=(flags &, flags);
// memory access
inline uint32_t data_read(const uint16_t& addr);
inline void data_write(const uint16_t& addr, const uint16_t& data);
inline uint32_t opcode_read(const uint8_t pcOffset=0);
// serial I/O state
enum sio_flags : u8
{
SIO_FLAGS_NONE = 0x00U,
SIO_FLAGS_ILD = 0x01U,
SIO_FLAGS_OLD = 0x02U
};
friend constexpr sio_flags operator~(sio_flags);
friend constexpr sio_flags operator&(sio_flags, sio_flags);
friend constexpr sio_flags operator|(sio_flags, sio_flags);
friend sio_flags &operator&=(sio_flags &, sio_flags);
friend sio_flags &operator|=(sio_flags &, sio_flags);
// address spaces
address_space* m_program;
address_space* m_data;
direct_read_data<-1> *m_direct;
// internal address maps
void program_map(address_map &map);
// other internal states
int m_icount;
// instruction execution
void execute_one_rom();
void execute_one_cache();
void overlap_rom_data_read();
void yaau_short_immediate_load(u16 op);
s16 yaau_read(u16 op);
void yaau_write(u16 op, s16 value);
void yaau_write_z(u16 op);
u64 dau_f1(u16 op);
u64 dau_f2(u16 op);
// operations
void execute_one(const uint16_t& op, uint8_t& cycles, uint8_t& pcAdvance);
// inline helpers
static bool op_interruptible(u16 op);
bool check_predicate();
flags &set_predicate(flags predicate) { return m_flags = (m_flags & ~FLAGS_PRED_MASK) | (predicate & FLAGS_PRED_MASK); }
flags &set_iack(flags iack) { return m_flags = (m_flags & ~FLAGS_IACK_MASK) | (iack & FLAGS_IACK_MASK); }
u16 &set_xaau_pc_offset(u16 offset);
void xaau_increment_pt(s16 increment) { m_xaau_pt = (m_xaau_pt & XAAU_I_EXT) | ((m_xaau_pt + increment) & XAAU_I_MASK); }
s16 get_r(u16 op);
void set_r(u16 op, s16 value);
void yaau_postmodify_r(u16 op);
void set_dau_y(u16 op, s16 value);
void set_dau_at(u16 op, s16 value);
u64 set_dau_psw_flags(s64 d);
u64 get_dau_p_aligned() const;
bool op_dau_con(u16 op);
// table decoders
void* registerFromRImmediateField(const uint8_t& R);
void* registerFromRTable(const uint8_t& R);
uint16_t* registerFromYFieldUpper(const uint8_t& Y);
// flag accessors
u16 dau_auc_align() const { return m_dau_auc & 0x0003U; }
bool dau_psw_lmi() const { return bool(BIT(m_dau_psw, 15)); }
bool dau_psw_leq() const { return bool(BIT(m_dau_psw, 14)); }
bool dau_psw_llv() const { return bool(BIT(m_dau_psw, 13)); }
bool dau_psw_lmv() const { return bool(BIT(m_dau_psw, 12)); }
// execution
void executeF1Field(const uint8_t& F1, const uint8_t& D, const uint8_t& S);
void executeYFieldPost(const uint8_t& Y);
void executeZFieldPartOne(const uint8_t& Z, uint16_t* rN);
void executeZFieldPartTwo(const uint8_t& Z, uint16_t* rN);
// opcode field handling
static constexpr u16 op_ja(u16 op) { return op & 0x0fffU; }
static constexpr u16 op_b(u16 op) { return (op >> 8) & 0x0007U; }
static constexpr u16 op_d(u16 op) { return BIT(op, 10); }
static constexpr u16 op_s(u16 op) { return BIT(op, 9); }
static constexpr u16 op_f1(u16 op) { return (op >> 5) & 0x000fU; }
static constexpr u16 op_f2(u16 op) { return (op >> 5) & 0x000fU; }
static constexpr u16 op_r(u16 op) { return (op >> 4) & 0x003fU; }
static constexpr u16 op_x(u16 op) { return BIT(op, 4); }
static constexpr u16 op_con(u16 op) { return op & 0x001fU; }
static constexpr u16 op_ni(u16 op) { return (op >> 7) & 0x000fU; }
static constexpr u16 op_k(u16 op) { return op & 0x007fU; }
s16 op_xaau_increment(u16 op) const { return op_x(op) ? m_xaau_i : 1; }
u16 &op_yaau_r(u16 op) { return m_yaau_r[(op >> 2) & 0x0003U]; }
s64 &op_dau_as(u16 op) { return m_dau_a[op_s(op)]; }
s64 &op_dau_ad(u16 op) { return m_dau_a[op_d(op)]; }
s64 &op_dau_at(u16 op) { return m_dau_a[op_d(~op)]; }
// helpers
void* addressYL();
void writeRegister(void* reg, const uint16_t& value);
bool conditionTest(const uint8_t& CON);
// serial I/O
bool sio_ld_ick() const { return !BIT(m_sio_sioc, 9); }
bool sio_ld_ock() const { return bool(BIT(m_sio_sioc, 9)); }
bool sio_lsb_first() const { return !BIT(m_sio_sioc, 6); }
bool sio_msb_first() const { return bool(BIT(m_sio_sioc, 6)); }
bool sio_old_active() const { return bool(BIT(m_sio_sioc, 5)); }
bool sio_ild_active() const { return bool(BIT(m_sio_sioc, 4)); }
bool sio_ock_active() const { return bool(BIT(m_sio_sioc, 3)); }
bool sio_ick_active() const { return bool(BIT(m_sio_sioc, 2)); }
unsigned sio_olen() const { return BIT(m_sio_sioc, 1) ? 8U : 16U; }
unsigned sio_ilen() const { return BIT(m_sio_sioc, 0) ? 8U : 16U; }
void sio_sioc_write(u16 value);
void sio_ick_active_edge();
void sio_ock_active_edge();
void sio_step_ld_div();
// flags
bool lmi();
bool leq();
bool llv();
bool lmv();
// parallel I/O
u16 pio_strobe() const { return ((m_pio_pioc >> 13) & 0x0003U) + 1; }
bool pio_pods_active() const { return bool(BIT(m_pio_pioc, 12)); }
bool pio_pids_active() const { return bool(BIT(m_pio_pioc, 11)); }
bool pio_sc_mode() const { return bool(BIT(m_pio_pioc, 10)); }
bool pio_ibf_enable() const { return bool(BIT(m_pio_pioc, 9)); }
bool pio_obe_enable() const { return bool(BIT(m_pio_pioc, 8)); }
bool pio_pids_enable() const { return bool(BIT(m_pio_pioc, 7)); }
bool pio_pods_enable() const { return bool(BIT(m_pio_pioc, 6)); }
bool pio_int_enable() const { return bool(BIT(m_pio_pioc, 5)); }
bool pio_ibf_status() const { return bool(BIT(m_pio_pioc, 4)); }
bool pio_obe_status() const { return bool(BIT(m_pio_pioc, 3)); }
bool pio_pids_status() const { return bool(BIT(m_pio_pioc, 2)); }
bool pio_pods_status() const { return bool(BIT(m_pio_pioc, 1)); }
bool pio_int_status() const { return bool(BIT(m_pio_pioc, 0)); }
void pio_pioc_write(u16 value);
u16 pio_pdx_read(u16 sel);
void pio_pdx_write(u16 sel, u16 value);
// interrupt callbacks
devcb_write_line m_iack_cb;
// serial output callbacks
devcb_write_line m_ick_cb, m_ild_cb;
devcb_write_line m_do_cb, m_ock_cb, m_old_cb, m_ose_cb;
// parallel I/O callbacks
devcb_read16 m_pio_r_cb;
devcb_write16 m_pio_w_cb;
devcb_write16 m_pdb_w_cb;
devcb_write_line m_psel_cb, m_pids_cb, m_pods_cb;
// configuration
address_space_config const m_space_config[3];
u16 const m_yaau_mask;
u16 const m_yaau_sign;
// memory system access
address_space *m_spaces[3];
direct_read_data<-1> *m_direct;
// execution state
int m_icount;
cache m_cache_mode;
phase m_phase;
u8 m_int_enable[2];
flags m_flags;
u8 m_cache_ptr, m_cache_limit, m_cache_iterations;
u16 m_cache[16];
u16 m_rom_data;
// line states
u8 m_exm_in; // internal ROM disabled when low
u8 m_int_in; // external interrupt request
u8 m_iack_out; // asserted (low) while servicing interrupt
// serial I/O line states
u8 m_ick_in; // data input clock - sampled on rising edge
u8 m_ild_in; // data input clock - sampled on rising edge
u8 m_do_out; // serial data output - changes on rising edges of OCK
u8 m_ock_in; // data output clock - output changes on rising edge
u8 m_old_in; // falling edge indicates beginning of output word
u8 m_ose_out; // indicates the end of a serial transmission
// parallel I/O line states
u8 m_psel_out; // last accessed parallel I/O data register
u8 m_pids_out; // parallel input data strobe (sampled on rising edge)
u8 m_pods_out; // parallel output data strobe
// XAAU - ROM Address Arithmetic Unit
u16 m_xaau_pc; // 16 bits unsigned
u16 m_xaau_pt; // 16 bits unsigned
u16 m_xaau_pr; // 16 bits unsigned
u16 m_xaau_pi; // 16 bits unsigned
s16 m_xaau_i; // 12 bits signed
// YAAU - RAM Address Arithmetic Unit
u16 m_yaau_r[4]; // 9/16 bits unsigned
u16 m_yaau_rb; // 9/16 bits unsigned
u16 m_yaau_re; // 9/16 bits unsigned
s16 m_yaau_j; // 9/16 bits signed
s16 m_yaau_k; // 9/16 bits signed
// DAU - Data Arithmetic Unit
s16 m_dau_x; // 16 bits signed
s32 m_dau_y; // 32 bits signed
s32 m_dau_p; // 32 bits signed
s64 m_dau_a[2]; // 36 bits signed
s8 m_dau_c[3]; // 8 bits signed
u8 m_dau_auc; // 7 bits unsigned
u16 m_dau_psw; // 16 bits
s16 m_dau_temp; // 16 bits
// SIO - Serial I/O
u16 m_sio_sioc; // 10 bits
u16 m_sio_obuf; // 16 bits
u16 m_sio_osr; // 16 bits
u16 m_sio_ofsr; // 16 bits
u8 m_sio_clk;
u8 m_sio_clk_div;
u8 m_sio_ld;
u8 m_sio_ld_div;
sio_flags m_sio_flags;
// PIO - Parallel I/O
u16 m_pio_pioc; // 16 bits
u16 m_pio_pdx_in; // 16 bits
u16 m_pio_pdx_out; // 16 bits
u8 m_pio_pids_cnt;
u8 m_pio_pods_cnt;
// fake registers for the debugger
u16 m_cache_pcbase;
u16 m_st_pcbase;
s16 m_st_yh, m_st_ah[2];
u16 m_st_yl, m_st_al[2];
};
// device type definition
class dsp16_device : public dsp16_device_base
{
public:
// construction/destruction
dsp16_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
protected:
// dsp16_device_base implementation
virtual void external_memory_enable(address_space &space, bool enable) override;
// internal address maps
void data_map(address_map &map);
private:
required_region_ptr<u16> m_rom;
};
class dsp16a_device : public dsp16_device_base
{
public:
// construction/destruction
dsp16a_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
protected:
// dsp16_device_base implementation
virtual void external_memory_enable(address_space &space, bool enable) override;
// internal address maps
void data_map(address_map &map);
private:
required_region_ptr<u16> m_rom;
};
enum
{
DSP16_INT_LINE = INPUT_LINE_IRQ0,
DSP16_ICK_LINE,
DSP16_ILD_LINE,
DSP16_OCK_LINE,
DSP16_OLD_LINE
};
DECLARE_ENUM_BITWISE_OPERATORS(dsp16_device_base::flags)
DECLARE_ENUM_BITWISE_OPERATORS(dsp16_device_base::sio_flags)
DECLARE_DEVICE_TYPE(DSP16, dsp16_device)
DECLARE_DEVICE_TYPE(DSP16A, dsp16a_device)
#endif // MAME_CPU_DSP16_DSP16_H

View File

@ -1,937 +0,0 @@
// license:BSD-3-Clause
// copyright-holders:Andrew Gardner
#include "dsp16.h"
#define DSP_LINE(__DSP_DOCLINE__) printf("0x%04x - %d (%s)\n", m_pc, __LINE__, __DSP_DOCLINE__);
// TODO:
// * AUC has a CLR field for writing to A0 & A1 + sign extension + psw + zero lower bits
// implement as a clean function (page 2-7)
// * Implement saturation overflow (SAT on AUC) (page 2-8)
// * Implement p alignment (ALIGN on AUC) (page 2-9)
// * When a register is used as a memory pointer. its value is compared with re. If its value is
// equal to the contents of re and the postincrement is +1, then the value in rb is copied into
// the register after the memory access is complete. See Section 4.2.3.
// * CPU flags go to the PSW & conditionTest() works on that (Page 3-4)
// * Some instructions are not interruptible.
//
// NOTES:
// When y is used in an assembly-language instruction, the DSPI6/DSPI6A device will read
// or write the high half (bits 16-31) of the y register (page 2-7)
// The YL register is the lower half of the 32 bit Y register
void* dsp16_device::addressYL()
{
return (void*)(((uint8_t*)&m_y) + 2);
}
// Flag getters
bool dsp16_device::lmi()
{
return m_psw & 0x8000;
}
bool dsp16_device::leq()
{
return m_psw & 0x4000;
}
bool dsp16_device::llv()
{
return m_psw & 0x2000;
}
bool dsp16_device::lmv()
{
return m_psw & 0x1000;
}
void dsp16_device::writeRegister(void* reg, const uint16_t &value)
{
// Make sure you're not attempting to write somewhere this function doesn't support.
if (reg == &m_p || reg == &m_a0 || reg == &m_a1)
{
logerror("dsp16::writeRegister called on invalid register at PC 0x%04x.\n", m_pc);
return;
}
if (reg == &m_auc || reg == &m_c0 || reg == &m_c1 || reg == &m_c2)
{
// 8 bit registers
*(uint8_t*)reg = value & 0x00ff;
}
else if (reg == &m_psw)
{
// Writes to the a0 & a1 guard bits too
m_a0 &= 0x0ffffffffU;
m_a0 |= u64(m_psw & 0x000fU) << 32;
m_a1 &= 0x0ffffffffU;
m_a1 |= u64(m_psw & 0x01e0U) << 27;
m_psw = value;
}
else if (reg == &m_i)
{
// 12 bit register
m_i = value & 0x0fff;
}
else if (reg == &m_y)
{
// Y register
// TODO - Automatic clearing of yl may be selected (according to the CLR field of the auc register) (page 2-7)
m_y = (value << 16) | (m_y & 0x0000ffff);
}
else if (reg == addressYL())
{
// Yl register (Writes to yl do not change the data in the high half of y)
m_y = value | (m_y & 0xffff0000);
}
else
{
// Everything else
*(uint16_t*)reg = value;
}
}
bool dsp16_device::conditionTest(const uint8_t& CON)
{
switch (CON)
{
case 0x00: return lmi(); // mi (negative result)
case 0x01: return !lmi(); // pl (positive result)
case 0x02: return leq(); // eq (result == 0)
case 0x03: return !leq(); // ne (result != 0)
case 0x04: return llv(); // lvs (logical overflow set)
case 0x05: return !llv(); // lvc (logical overflow clear)
case 0x06: return lmv(); // mvs (math. overflow set)
case 0x07: return !lmv(); // mvc (math. overflow clear)
case 0x08: printf("UNIMPLEMENTED condition check @ PC 0x%04x\n", m_pc); return false; // heads (random bit set)
case 0x09: printf("UNIMPLEMENTED condition check @ PC 0x%04x\n", m_pc); return false; // tails (random bit clear)
case 0x0a: printf("UNIMPLEMENTED condition check @ PC 0x%04x\n", m_pc); return false; // c0ge (counter0 >= 0)*
case 0x0b: printf("UNIMPLEMENTED condition check @ PC 0x%04x\n", m_pc); return false; // c0lt (counter0 < 0)*
case 0x0c: printf("UNIMPLEMENTED condition check @ PC 0x%04x\n", m_pc); return false; // c1ge (counter1 >= 0)*
case 0x0d: printf("UNIMPLEMENTED condition check @ PC 0x%04x\n", m_pc); return false; // c1lt (counter1 < 0)*
case 0x0e: return true; // true (always)
case 0x0f: return false; // false (never)
case 0x10: return (!lmi() && !leq()); // gt (result > 0)
case 0x11: return (lmi() || leq()); // le (result <= 0)
default: logerror("Unrecognized condition at PC=0x%04x\n", m_pc); break;
}
// Testing each of these conditions (*) increments the respective counter being tested (page 3-5)
return false;
}
void* dsp16_device::registerFromRImmediateField(const uint8_t& R)
{
switch (R)
{
case 0x00: return (void*)&m_j;
case 0x01: return (void*)&m_k;
case 0x02: return (void*)&m_rb;
case 0x03: return (void*)&m_re;
case 0x04: return (void*)&m_r0;
case 0x05: return (void*)&m_r1;
case 0x06: return (void*)&m_r2;
case 0x07: return (void*)&m_r3;
default: return nullptr;
}
return nullptr;
}
void* dsp16_device::registerFromRTable(const uint8_t &R)
{
switch (R)
{
case 0x00: return (void*)&m_r0;
case 0x01: return (void*)&m_r1;
case 0x02: return (void*)&m_r2;
case 0x03: return (void*)&m_r3;
case 0x04: return (void*)&m_j;
case 0x05: return (void*)&m_k;
case 0x06: return (void*)&m_rb;
case 0x07: return (void*)&m_re;
case 0x08: return (void*)&m_pt;
case 0x09: return (void*)&m_pr;
case 0x0a: return (void*)&m_pi;
case 0x0b: return (void*)&m_i;
case 0x10: return (void*)&m_x;
case 0x11: return (void*)&m_y;
case 0x12: return (void*)addressYL();
case 0x13: return (void*)&m_auc; // zero extended
case 0x14: return (void*)&m_psw;
case 0x15: return (void*)&m_c0; // sign extended
case 0x16: return (void*)&m_c1; // sign extended
case 0x17: return (void*)&m_c2; // sign extended
case 0x18: return (void*)&m_sioc;
case 0x19: return (void*)&m_srta;
case 0x1a: return (void*)&m_sdx;
case 0x1b: logerror("dsp16::registerFromRTable tdms requested 0x%04x.\n", m_pc); break;
case 0x1c: return (void*)&m_pioc;
case 0x1d: return (void*)&m_pdx0;
case 0x1e: return (void*)&m_pdx1;
default: return nullptr;
}
return nullptr;
}
void dsp16_device::executeF1Field(const uint8_t& F1, const uint8_t& D, const uint8_t& S)
{
// TODO: I'm pretty sure we need to feed X into these as well - Double check
// Note these instructions read right-to-left, so act accordingly (page 3-6)
// y & p are sign extended (page 3-9)
// implementation details (page 3-9)
// Where is are the results going?
uint64_t* destinationReg = nullptr;
switch (D)
{
case 0x00: destinationReg = &m_a0; break;
case 0x01: destinationReg = &m_a1; break;
default: break;
}
// Which source is being used?
uint64_t* sourceReg = nullptr;
switch (S)
{
case 0x00: sourceReg = &m_a0; break;
case 0x01: sourceReg = &m_a1; break;
default: break;
}
// We must compute into an intermediate variable to compute flags on
uint64_t result = 0;
bool justATest = false;
switch (F1)
{
case 0x00:
{
// Ad = p p = x*y
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x01:
{
// Ad = aS+p p = x*y
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x02:
{
// p = x*y
// TODO: What happens to the flags in this operation?
const int16_t y = (m_y & 0xffff0000) >> 16;
m_p = (int32_t)((int16_t)m_x * y);
justATest = true;
break;
}
case 0x03:
{
// Ad = aS-p p = x*y
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x04:
{
// Ad = p
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x05:
{
// Ad = aS+p
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x06:
{
// nop
justATest = true;
break;
}
case 0x07:
{
// Ad = aS-p
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x08:
{
// Ad = aS|y
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x09:
{
// Ad = aS^y
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x0a:
{
// aS&y
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
justATest = true;
break;
}
case 0x0b:
{
// aS-y
int64_t aS = *sourceReg;
if (aS & 0x800000000U)
aS |= 0xfffffff000000000U;
int64_t y = (m_y & 0xffff0000) >> 16;
if (y & 0x8000)
y |= 0xffffffffffff0000U;
result = aS-y;
justATest = true;
break;
}
case 0x0c:
{
// Ad = y
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x0d:
{
// Ad = aS+y
int64_t aS = *sourceReg;
if (aS & 0x800000000U)
aS |= 0xfffffff000000000U;
int64_t y = (m_y & 0xffff0000) >> 16;
if (y & 0x8000)
y |= 0xffffffffffff0000U;
result = aS+y;
break;
}
case 0x0e:
{
// Ad = aS&y
printf("UNIMPLEMENTED F1 operation @ PC 0x%04x (%d)\n", m_pc, __LINE__);
break;
}
case 0x0f:
{
// Ad = aS-y
int64_t aS = *sourceReg;
if (aS & 0x800000000U)
aS |= 0xfffffff000000000U;
int64_t y = (m_y & 0xffff0000) >> 16;
if (y & 0x8000)
y |= 0xffffffffffff0000U;
result = aS-y;
break;
}
}
// CPU Flags (page 3-4)
// LMI (logical minus)
if (result & 0x800000000U)
m_psw |= 0x8000;
else
m_psw &= (~0x8000);
// LEQ (logical equal)
if (result == 0x000000000U)
m_psw |= 0x4000;
else
m_psw &= (~0x4000);
// LLV (logical overflow)
// TODO
// LMV (mathematical overflow)
if ((result & 0xf00000000U) != 0xf00000000U &&
(result & 0xf00000000U) != 0x000000000U)
m_psw |= 0x1000;
else
m_psw &= (~0x1000);
// If it was a real operation, make sure the data goes where it should
if (!justATest)
*destinationReg = (uint64_t)result & 0x0000000fffffffffU;
}
uint16_t* dsp16_device::registerFromYFieldUpper(const uint8_t& Y)
{
uint16_t* destinationReg = nullptr;
const uint8_t N = (Y & 0x0c) >> 2;
switch (N)
{
case 0x00: destinationReg = &m_r0; break;
case 0x01: destinationReg = &m_r1; break;
case 0x02: destinationReg = &m_r2; break;
case 0x03: destinationReg = &m_r3; break;
default: break;
}
return destinationReg;
}
void dsp16_device::executeYFieldPost(const uint8_t& Y)
{
uint16_t* opReg = registerFromYFieldUpper(Y);
const uint8_t lower = Y & 0x03;
switch (lower)
{
case 0x00: /* nop */ break;
case 0x01: (*opReg)++; break;
case 0x02: (*opReg)--; break;
case 0x03: (*opReg) += m_j; break; // TODO: J is signed
}
}
void dsp16_device::executeZFieldPartOne(const uint8_t& Z, uint16_t* rN)
{
const uint8_t lower = Z & 0x03;
switch (lower)
{
case 0x00: /* nop */ break;
case 0x01: (*rN)++; break;
case 0x02: (*rN)--; break;
case 0x03: (*rN) += m_j; break; // TODO: J is signed
}
}
void dsp16_device::executeZFieldPartTwo(const uint8_t& Z, uint16_t* rN)
{
const uint8_t lower = Z & 0x03;
switch (lower)
{
case 0x00: (*rN)++; break;
case 0x01: /* nop */ break;
case 0x02: (*rN) += 2; break;
case 0x03: (*rN) += m_k; break; // TODO: K is signed
}
}
void dsp16_device::execute_one(const uint16_t& op, uint8_t& cycles, uint8_t& pcAdvance)
{
cycles = 1;
pcAdvance = 0;
// NOTE: pages 3-5 through 3-19 are good english descriptions of what's up
const uint8_t opcode = (op >> 11) & 0x1f;
switch(opcode)
{
// Format 1: Multiply/ALU Read/Write Group
case 0x06:
{
DSP_LINE("3-38")
// F1, Y : (page 3-38)
const uint8_t Y = (op & 0x000f);
const uint8_t S = (op & 0x0200) >> 9;
const uint8_t D = (op & 0x0400) >> 10;
const uint8_t F1 = (op & 0x01e0) >> 5;
executeF1Field(F1, D, S);
executeYFieldPost(Y);
cycles = 1;
pcAdvance = 1;
break;
}
case 0x04: case 0x1c:
{
DSP_LINE("3-40")
// F1 Y=a0[1] | F1 Y=a1[1] : (page 3-40)
const uint8_t Y = (op & 0x000f);
//const uint8_t X = (op & 0x0010) >> 4;
const uint8_t S = (op & 0x0200) >> 9;
const uint8_t D = (op & 0x0400) >> 10;
const uint8_t F1 = (op & 0x01e0) >> 5;
uint16_t* destinationReg = registerFromYFieldUpper(Y);
// (page 3-18)
uint16_t aRegValue = 0x0000;
if (op & 0xc000)
{
aRegValue = (m_a0 & 0x0ffff0000U) >> 16;
}
else
{
aRegValue = (m_a1 & 0x0ffff0000U) >> 16;
}
data_write(*destinationReg, aRegValue);
executeYFieldPost(Y);
executeF1Field(F1, D, S);
cycles = 2;
pcAdvance = 1;
break;
}
case 0x16:
{
DSP_LINE("3-42")
// F1, x = Y : (page 3-42)
const uint8_t Y = (op & 0x000f);
const uint8_t S = (op & 0x0200) >> 9;
const uint8_t D = (op & 0x0400) >> 10;
const uint8_t F1 = (op & 0x01e0) >> 5;
executeF1Field(F1, D, S);
uint16_t* sourceReg = registerFromYFieldUpper(Y);
writeRegister(&m_x, data_read(*sourceReg));
executeYFieldPost(Y);
cycles = 1;
pcAdvance = 1;
break;
}
case 0x17:
{
DSP_LINE("3-44")
// F1, y[l] = Y : (page 3-44)
const uint8_t Y = (op & 0x000f);
const uint8_t X = (op & 0x0010) >> 4;
const uint8_t S = (op & 0x0200) >> 9;
const uint8_t D = (op & 0x0400) >> 10;
const uint8_t F1 = (op & 0x01e0) >> 5;
executeF1Field(F1, D, S);
uint16_t* sourceReg = registerFromYFieldUpper(Y);
uint16_t sourceValue = data_read(*sourceReg);
switch (X)
{
case 0x00: writeRegister(addressYL(), sourceValue); break;
case 0x01: writeRegister(&m_y, sourceValue); break;
default: break;
}
executeYFieldPost(Y);
cycles = 1;
pcAdvance = 1;
break;
}
case 0x1f:
{
DSP_LINE("3-46")
// F1, y = Y, x = *pt++[i] : (page 3-46)
const uint8_t Y = (op & 0x000f);
const uint8_t X = (op & 0x0010) >> 4;
const uint8_t S = (op & 0x0200) >> 9;
const uint8_t D = (op & 0x0400) >> 10;
const uint8_t F1 = (op & 0x01e0) >> 5;
executeF1Field(F1, D, S);
uint16_t* sourceRegR = registerFromYFieldUpper(Y);
writeRegister(&m_y, data_read(*sourceRegR));
executeYFieldPost(Y);
writeRegister(&m_x, data_read(m_pt));
switch (X)
{
case 0x00: m_pt++; break;
case 0x01: m_pt += m_i; break;
}
cycles = 2; // TODO: 1 if cached
pcAdvance = 1;
break;
}
case 0x19: case 0x1b:
{
DSP_LINE("3-48")
// F1, y = a0|1, x = *pt++[i] : (page 3-48)
const uint8_t Y = (op & 0x000f);
const uint8_t X = (op & 0x0010) >> 4;
const uint8_t S = (op & 0x0200) >> 9;
const uint8_t D = (op & 0x0400) >> 10;
const uint8_t F1 = (op & 0x01e0) >> 5;
bool useA1 = (opcode == 0x1b);
if (Y != 0x00) printf("Unknown opcode @ PC=0x%04x", m_pc);
m_y = (useA1) ? (m_a1 & 0xffffffff) : (m_a0 & 0xffffffff); // TODO: What happens to Ax when it goes 32 bit (pc=3f & pc=47)?
executeF1Field(F1, D, S);
writeRegister(&m_x, data_read(m_pt)); // TODO: EXM Pin & internal/external ROM? Research.
switch (X)
{
case 0x00: m_pt++; break;
case 0x01: m_pt += m_i; break;
}
cycles = 2; // TODO: 1 if cached
pcAdvance = 1;
break;
}
case 0x14:
{
DSP_LINE("3-53")
// F1, Y = y[l] : (page 3-53)
const uint8_t Y = (op & 0x000f);
const uint8_t X = (op & 0x0010) >> 4;
const uint8_t S = (op & 0x0200) >> 9;
const uint8_t D = (op & 0x0400) >> 10;
const uint8_t F1 = (op & 0x01e0) >> 5;
executeF1Field(F1, D, S);
uint16_t* destinationReg = registerFromYFieldUpper(Y);
uint16_t yRegValue = 0x0000;
switch (X)
{
case 0x00: yRegValue = (m_y & 0x0000ffff); break;
case 0x01: yRegValue = (m_y & 0xffff0000) >> 16; break;
default: break;
}
data_write(*destinationReg, yRegValue);
executeYFieldPost(Y);
cycles = 2;
pcAdvance = 1;
break;
}
// Format 1a: Multiply/ALU Read/Write Group (TODO: Figure out major typo in docs on p3-51)
case 0x07:
{
DSP_LINE("3-50")
// F1, At[1] = Y : (page 3-50)
// TODO: What does the X field do here, exactly?
const uint8_t Y = (op & 0x000f);
const uint8_t S = (op & 0x0200) >> 9;
const uint8_t aT = (op & 0x0400) >> 10;
const uint8_t F1 = (op & 0x01e0) >> 5;
executeF1Field(F1, !aT, S);
uint64_t* destinationReg = nullptr;
switch(aT)
{
case 0: destinationReg = &m_a1; break;
case 1: destinationReg = &m_a0; break;
default: break;
}
uint16_t sourceAddress = *(registerFromYFieldUpper(Y));
int64_t sourceValueSigned = (int16_t)data_read(sourceAddress);
*destinationReg = sourceValueSigned & 0xffffffffffU;
executeYFieldPost(Y);
cycles = 1;
pcAdvance = 1;
break;
}
// Format 2: Multiply/ALU Read/Write Group
case 0x15:
{
DSP_LINE("3-54")
// F1, Z : y[l] : (page 3-54)
const uint8_t Z = (op & 0x000f);
const uint8_t X = (op & 0x0010) >> 4;
const uint8_t S = (op & 0x0200) >> 9;
const uint8_t D = (op & 0x0400) >> 10;
const uint8_t F1 = (op & 0x01e0) >> 5;
executeF1Field(F1, D, S);
uint16_t temp = 0x0000;
uint16_t* rN = registerFromYFieldUpper(Z);
switch (X)
{
case 0x00:
temp = m_y & 0x0000ffff;
m_y &= 0xffff0000;
m_y |= data_read(*rN);
executeZFieldPartOne(Z, rN);
data_write(*rN, temp);
executeZFieldPartTwo(Z, rN);
break;
case 0x01:
temp = (m_y & 0xffff0000) >> 16;
m_y &= 0x0000ffff;
m_y |= (data_read(*rN) << 16);
executeZFieldPartOne(Z, rN);
data_write(*rN, temp);
executeZFieldPartTwo(Z, rN);
break;
}
cycles = 2;
pcAdvance = 1;
break;
}
case 0x1d:
{
DSP_LINE("?")
// F1, Z : y, x=*pt++[i]
//const uint8_t Z = (op & 0x000f);
//const uint8_t X = (op & 0x0010) >> 4;
//const uint8_t S = (op & 0x0200) >> 9;
//const uint8_t D = (op & 0x0400) >> 10;
//const uint8_t F1 = (op & 0x01e0) >> 5;
break;
}
// Format 2a: Multiply/ALU Read/Write Group
case 0x05:
{
DSP_LINE("?")
// F1, Z : aT[1]
//const uint8_t Z = (op & 0x000f);
//const uint8_t X = (op & 0x0010) >> 4;
//const uint8_t S = (op & 0x0200) >> 9;
//const uint8_t aT = (op & 0x0400) >> 10;
//const uint8_t F1 = (op & 0x01e0) >> 5;
break;
}
// Format 3: Special Functions
case 0x12:
case 0x13:
{
DSP_LINE("3-36")
// if|ifc CON F2 (page 3-36)
const uint8_t CON = (op & 0x001f);
//const uint8_t S = (op & 0x0200) >> 9;
//const uint8_t D = (op & 0x0400) >> 10;
//const uint8_t F2 = (op & 0x01e0) >> 5;
bool conditionFulfilled = conditionTest(CON);
if (conditionFulfilled)
{
printf("Fulfilled condition not yet implemented @ PC=0x%04x\n", m_pc);
}
cycles = 1;
pcAdvance = 1;
break;
}
// Format 4: Branch Direct Group
case 0x00: case 0x01:
{
DSP_LINE("3-20")
// goto JA : (page 3-20) (DONE)
const uint16_t JA = (op & 0x0fff) | (m_pc & 0xf000);
m_pc = JA;
cycles = 2;
pcAdvance = 0;
break;
}
case 0x10: case 0x11:
{
DSP_LINE("3-23")
// call JA : (page 3-23)
const uint16_t JA = (op & 0x0fff) | (m_pc & 0xf000);
m_pr = m_pc + 1;
m_pc = JA;
cycles = 2;
pcAdvance = 0;
break;
}
// Format 5: Branch Indirect Group
case 0x18:
{
DSP_LINE("3-21")
// goto B : (page 3-21)
const uint8_t B = (op & 0x0700) >> 8;
switch (B)
{
case 0x00: m_pc = m_pr; break;
case 0x01: printf("UNIMPLEMENTED branch instruction @ PC 0x%04x\n", m_pc); break;
case 0x02: printf("UNIMPLEMENTED branch instruction @ PC 0x%04x\n", m_pc); break;
case 0x03: printf("UNIMPLEMENTED branch instruction @ PC 0x%04x\n", m_pc); break;
default: logerror("DSP16: Invalid branch indirect instruction executed at PC=0x%04x\n.", m_pc); break;
}
cycles = 2;
pcAdvance = 0;
break;
}
// Format 6: Contitional Branch Qualifier/Software Interrupt (icall)
case 0x1a:
{
DSP_LINE("3-22")
// if CON [goto/call/return] : (page 3-22)
const uint8_t CON = (op & 0x001f);
bool conditionFulfilled = conditionTest(CON);
cycles = 3; // TODO: This may need to interact with the next opcode to make sure it doesn't exceed 3?
pcAdvance = 1;
if (!conditionFulfilled)
{
pcAdvance = 2;
}
break;
}
// Format 7: Data Move Group
case 0x09: case 0x0b:
{
DSP_LINE("3-29")
// R = aS : (page 3-29)
// TODO: Fix register pdxX (pc=338)
const uint8_t R = (op & 0x03f0) >> 4;
const uint8_t S = (op & 0x1000) >> 12;
void* destinationReg = registerFromRTable(R);
uint64_t* sourceReg = (S) ? &m_a1 : &m_a0;
uint16_t sourceValue = (*sourceReg & 0x0ffff0000U) >> 16;
writeRegister(destinationReg, sourceValue);
cycles = 2;
pcAdvance = 1;
break;
}
case 0x08:
{
DSP_LINE("3-30")
// aT = R : (page 3-30)
const uint8_t R = (op & 0x03f0) >> 4;
const uint8_t aT = (op & 0x0400) >> 10;
uint64_t* destinationReg = nullptr;
switch(aT)
{
case 0: destinationReg = &m_a1; break;
case 1: destinationReg = &m_a0; break;
default: break;
}
void* sourceReg = registerFromRTable(R);
*destinationReg &= 0x00000ffffU;
*destinationReg |= (*(uint16_t*)sourceReg) << 16; // TODO: Fix for all registers
if (*(uint16_t*)sourceReg & 0x8000)
*destinationReg |= 0xf00000000U;
// TODO: Special function encoding
cycles = 2;
pcAdvance = 1;
break;
}
case 0x0f:
{
DSP_LINE("3-32")
// R = Y : (page 3-32)
const uint8_t Y = (op & 0x000f);
const uint8_t R = (op & 0x03f0) >> 4;
uint16_t* sourceReg = registerFromYFieldUpper(Y);
void* destinationReg = registerFromRTable(R);
writeRegister(destinationReg, data_read(*sourceReg));
executeYFieldPost(Y);
cycles = 2;
pcAdvance = 1;
break;
}
case 0x0c:
{
DSP_LINE("3-33")
// Y = R : (page 3-33)
// TODO: Zero & Sign extend i, c0, c1, c2, and auc
const uint8_t Y = (op & 0x000f);
const uint8_t R = (op & 0x03f0) >> 4;
uint16_t* destinationReg = registerFromYFieldUpper(Y);
uint16_t* sourceReg = (uint16_t*)registerFromRTable(R); // TODO: This won't work for certain registers!
data_write(*destinationReg, *sourceReg); // Fix in data_write() maybe?
executeYFieldPost(Y);
cycles = 2;
pcAdvance = 1;
break;
}
case 0x0d:
{
DSP_LINE("?")
// Z : R
//const uint8_t Z = (op & 0x000f);
//const uint8_t R = (op & 0x03f0) >> 4;
break;
}
// Format 8: Data Move (immediate operand - 2 words)
case 0x0a:
{
DSP_LINE("3-28")
// R = N : (page 3-28) (DONE)
// NOTE: The docs speak of register sources & sign extension, but this is a register
// destination, so, typo? If so, what does one do with the overflow bits?
const uint8_t R = (op & 0x03f0) >> 4;
const uint16_t iVal = opcode_read(1);
void* destinationReg = registerFromRTable(R);
writeRegister(destinationReg, iVal);
cycles = 2;
pcAdvance = 2;
break;
}
// Format 9: Short Immediate Group
case 0x02: case 0x03:
{
DSP_LINE("3-27")
// R = M : (page 3-27)
// TODO: Figure out notes about the DSP16A vs the DSP16. 9 bit is very DSP16...
const uint16_t M = (op & 0x01ff);
const uint8_t R = (op & 0x0e00) >> 9;
void* destinationReg = registerFromRImmediateField(R);
// Sign extend if the destination is j or k
uint16_t mValue = M;
if (destinationReg == &m_j || destinationReg == &m_k)
{
if (mValue & 0x0100) mValue |= 0xfe00;
}
writeRegister(destinationReg, mValue);
cycles = 1;
pcAdvance = 1;
break;
}
// Format 10: do - redo
case 0x0e:
{
DSP_LINE("3-25/3-26")
// do|redo K : (pages 3-25 & 3-26)
// TODO: The timings are intricate to say the least...
const uint8_t K = (op & 0x007f);
const uint8_t NI = (op & 0x0780) >> 7;
if (NI != 0)
{
// Do
m_cacheStart = m_pc + 1;
m_cacheEnd = m_pc + 1 + NI;
m_cacheIterations = K-1; // -1 because we check the counter @ the end
cycles = 1;
pcAdvance = 1;
}
else
{
// Redo
m_cacheIterations = K-1; // -1 because we check the counter @ the end
m_cacheRedoNextPC = m_pc + 1;
m_pc = m_cacheStart;
cycles = 2;
pcAdvance = 0;
}
break;
}
// RESERVED
case 0x1e:
{
DSP_LINE("XXX")
break;
}
// UNKNOWN
default:
{
DSP_LINE("XXX")
break;
}
}
// Handle end-of-cache conditions for do|redos
if (m_cacheIterations == 0 && m_cacheRedoNextPC != CACHE_INVALID)
{
// You've reached the end of a cache loop after a redo opcode.
m_pc = m_cacheRedoNextPC;
m_cacheRedoNextPC = CACHE_INVALID;
pcAdvance = 0;
}
if (m_cacheIterations > 0 && (m_pc+pcAdvance == m_cacheEnd))
{
// A regular iteration on a cached loop.
m_cacheIterations--;
m_pc = m_cacheStart;
pcAdvance = 0;
}
}

View File

@ -2,8 +2,8 @@
// copyright-holders:Paul Leaman, Miguel Angel Horna
/***************************************************************************
Capcom System QSound(tm)
========================
Capcom System QSound
=====================
Driver by Paul Leaman and Miguel Angel Horna
@ -40,28 +40,13 @@ DEFINE_DEVICE_TYPE(QSOUND, qsound_device, "qsound", "Q-Sound")
// chip decapped by siliconpr0n clearly shows 3x as much ROM as that, a total
// of 12288 words of internal ROM.
// The older DSP16 non-a part has 2048 words of ROM.
void qsound_device::dsp16_program_map(address_map &map)
{
map(0x0000, 0x2fff).rom();
}
// data map for the DSP16A; again, Western Electric/AT&T expanded the size of
// the ram over time.
// As originally released, the DSP16A had 1024 words of internal RAM,
// but this was expanded to 2048 words in the DL-1425 decap.
// The older DSP16 non-a part has 512 words of RAM.
void qsound_device::dsp16_data_map(address_map &map)
{
map.unmap_value_high();
map(0x0000, 0x07ff).ram();
}
// ROM definition for the Qsound program ROM
// DSP internal ROM region
ROM_START( qsound )
ROM_REGION( 0x6000, "qsound", 0 )
ROM_LOAD16_WORD( "dl-1425.bin", 0x0000, 0x6000, CRC(d6cf5ef5) SHA1(555f50fe5cdf127619da7d854c03f4a244a0c501) )
ROM_REGION( 0x2000, "dsp", 0 )
ROM_LOAD16_WORD_SWAP( "dl-1425.bin", 0x0000, 0x2000, CRC(d6cf5ef5) SHA1(555f50fe5cdf127619da7d854c03f4a244a0c501) )
ROM_IGNORE( 0x4000 )
ROM_END
@ -74,12 +59,12 @@ ROM_END
//-------------------------------------------------
qsound_device::qsound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, QSOUND, tag, owner, clock),
device_sound_interface(mconfig, *this),
device_rom_interface(mconfig, *this, 24),
m_cpu(*this, "qsound"),
m_data(0),
m_stream(nullptr)
: device_t(mconfig, QSOUND, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, device_rom_interface(mconfig, *this, 24)
, m_dsp(*this, "dsp")
, m_stream(nullptr)
, m_data(0)
{
}
@ -100,9 +85,8 @@ const tiny_rom_entry *qsound_device::device_rom_region() const
//-------------------------------------------------
MACHINE_CONFIG_START(qsound_device::device_add_mconfig)
MCFG_CPU_ADD("qsound", DSP16, DERIVED_CLOCK(1, 1))
MCFG_CPU_PROGRAM_MAP(dsp16_program_map)
MCFG_CPU_DATA_MAP(dsp16_data_map)
MCFG_CPU_ADD("dsp", DSP16A, DERIVED_CLOCK(1, 1))
MCFG_DEVICE_DISABLE()
MACHINE_CONFIG_END
@ -122,7 +106,7 @@ void qsound_device::rom_bank_updated()
void qsound_device::device_start()
{
m_stream = stream_alloc(0, 2, clock() / 166); // /166 clock divider?
m_stream = stream_alloc(0, 2, clock() / 15 / 166); // /166 clock divider?
// create pan table
for (int i = 0; i < 33; i++)
@ -152,6 +136,10 @@ void qsound_device::device_start()
}
}
void qsound_device::device_reset()
{
}
//-------------------------------------------------
// sound_stream_update - handle a stream update

View File

@ -2,10 +2,9 @@
// copyright-holders:Paul Leaman, Miguel Angel Horna
/*********************************************************
Capcom Q-Sound system
Capcom System QSound
*********************************************************/
#ifndef MAME_SOUND_QSOUND_H
#define MAME_SOUND_QSOUND_H
@ -13,46 +12,44 @@
#include "cpu/dsp16/dsp16.h"
#define QSOUND_CLOCK 4000000 /* default 4MHz clock (60MHz/15?) */
// default 60MHz clock (divided by 2 for DSP core clock, and then by 1248 for sample rate)
#define QSOUND_CLOCK 60_MHz_XTAL
//**************************************************************************
// INTERFACE CONFIGURATION MACROS
//**************************************************************************
#define MCFG_QSOUND_ADD(_tag, _clock) \
MCFG_DEVICE_ADD(_tag, QSOUND, _clock)
#define MCFG_QSOUND_REPLACE(_tag, _clock) \
MCFG_DEVICE_REPLACE(_tag, QSOUND, _clock)
// ======================> qsound_device
class qsound_device : public device_t,
public device_sound_interface,
public device_rom_interface
class qsound_device : public device_t, public device_sound_interface, public device_rom_interface
{
public:
qsound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
qsound_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
DECLARE_WRITE8_MEMBER(qsound_w);
DECLARE_READ8_MEMBER(qsound_r);
void dsp16_data_map(address_map &map);
void dsp16_program_map(address_map &map);
protected:
// device-level overrides
const tiny_rom_entry *device_rom_region() const override;
// device_t implementation
tiny_rom_entry const *device_rom_region() const override;
virtual void device_add_mconfig(machine_config &config) override;
virtual void device_start() override;
virtual void device_reset() override;
// sound stream update overrides
// device_sound_interface implementation
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
// device_rom_interface overrides
// device_rom_interface implementation
virtual void rom_bank_updated() override;
private:
// MAME resources
required_device<dsp16_device_base> m_dsp;
sound_stream *m_stream;
struct qsound_channel
{
uint32_t bank; // bank
@ -69,11 +66,8 @@ private:
uint32_t step_ptr; // current offset counter
} m_channel[16];
required_device<dsp16_device> m_cpu;
int m_pan_table[33]; // pan volume table
uint16_t m_data; // register latch data
sound_stream *m_stream; // audio stream
inline int8_t read_sample(uint32_t offset) { return (int8_t)read_byte(offset); }
void write_data(uint8_t address, uint16_t data);

View File

@ -1598,7 +1598,7 @@ void validity_checker::validate_roms(device_t &root)
if (!ROMENTRY_ISREGIONEND(romp) && current_length > 0)
{
items_since_region++;
if (ROM_GETOFFSET(romp) + ROM_GETLENGTH(romp) > current_length)
if (!ROMENTRY_ISIGNORE(romp) && (ROM_GETOFFSET(romp) + ROM_GETLENGTH(romp) > current_length))
osd_printf_error("ROM '%s' extends past the defined memory region\n", last_name);
}
}

View File

@ -3456,7 +3456,7 @@ MACHINE_CONFIG_START(cps_state::qsound)
MCFG_DEVICE_REMOVE("2151")
MCFG_DEVICE_REMOVE("oki")
MCFG_QSOUND_ADD("qsound", QSOUND_CLOCK)
MCFG_DEVICE_ADD("qsound", QSOUND, QSOUND_CLOCK)
MCFG_SOUND_ROUTE(0, "lspeaker", 1.0)
MCFG_SOUND_ROUTE(1, "rspeaker", 1.0)
MACHINE_CONFIG_END

View File

@ -1336,7 +1336,7 @@ MACHINE_CONFIG_START(cps_state::cps2)
/* sound hardware */
MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
MCFG_QSOUND_ADD("qsound", QSOUND_CLOCK)
MCFG_DEVICE_ADD("qsound", QSOUND, QSOUND_CLOCK)
MCFG_SOUND_ROUTE(0, "lspeaker", 1.0)
MCFG_SOUND_ROUTE(1, "rspeaker", 1.0)
MACHINE_CONFIG_END

View File

@ -1757,7 +1757,7 @@ MACHINE_CONFIG_START(vgmplay_state::vgmplay)
MCFG_SOUND_ROUTE(0, "lspeaker", 1)
MCFG_SOUND_ROUTE(1, "rspeaker", 1)
MCFG_QSOUND_ADD("qsound", 4000000)
MCFG_DEVICE_ADD("qsound", QSOUND, QSOUND_CLOCK)
MCFG_DEVICE_ADDRESS_MAP(0, qsound_map)
MCFG_SOUND_ROUTE(0, "lspeaker", 1)
MCFG_SOUND_ROUTE(1, "rspeaker", 1)

View File

@ -661,7 +661,7 @@ MACHINE_CONFIG_START(zn_state::coh1000c)
MCFG_DEVICE_MODIFY("soundlatch")
MCFG_GENERIC_LATCH_DATA_PENDING_CB(INPUTLINE("audiocpu", INPUT_LINE_NMI))
MCFG_QSOUND_ADD("qsound", QSOUND_CLOCK)
MCFG_DEVICE_ADD("qsound", QSOUND, QSOUND_CLOCK)
MCFG_SOUND_ROUTE(0, "lspeaker", 1.0)
MCFG_SOUND_ROUTE(1, "rspeaker", 1.0)
MACHINE_CONFIG_END
@ -687,7 +687,7 @@ MACHINE_CONFIG_START(zn_state::coh1002c)
MCFG_DEVICE_MODIFY("soundlatch")
MCFG_GENERIC_LATCH_DATA_PENDING_CB(INPUTLINE("audiocpu", INPUT_LINE_NMI))
MCFG_QSOUND_ADD("qsound", QSOUND_CLOCK)
MCFG_DEVICE_ADD("qsound", QSOUND, QSOUND_CLOCK)
MCFG_SOUND_ROUTE(0, "lspeaker", 1.0)
MCFG_SOUND_ROUTE(1, "rspeaker", 1.0)
MACHINE_CONFIG_END
@ -849,7 +849,7 @@ MACHINE_CONFIG_START(zn_state::coh3002c)
MCFG_DEVICE_MODIFY("soundlatch")
MCFG_GENERIC_LATCH_DATA_PENDING_CB(INPUTLINE("audiocpu", INPUT_LINE_NMI))
MCFG_QSOUND_ADD("qsound", QSOUND_CLOCK)
MCFG_DEVICE_ADD("qsound", QSOUND, QSOUND_CLOCK)
MCFG_SOUND_ROUTE(0, "lspeaker", 1.0)
MCFG_SOUND_ROUTE(1, "rspeaker", 1.0)
MACHINE_CONFIG_END