2013-04-30 21:20:11 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2004 Stephane Dallongeville
|
|
|
|
* Copyright 2004-2007 Theo Berkau
|
|
|
|
* Copyright 2006 Guillaume Duhamel
|
|
|
|
* Copyright 2012 Chris Lord
|
|
|
|
*
|
|
|
|
* This file is part of Yabause.
|
|
|
|
*
|
|
|
|
* Yabause is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Yabause is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with Yabause; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Custom Sound Processor
|
|
|
|
|
|
|
|
// note: model2 scsp is mapped to 0x100000~0x100ee4 of the space, but seems to
|
|
|
|
// have additional hw ports ($40a~$410)
|
|
|
|
// note: it seems that the user interrupt is used by the sound driver to reset
|
|
|
|
// the subsystem
|
|
|
|
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Common Control Register (CCR)
|
|
|
|
//
|
|
|
|
// $+00 $+01
|
|
|
|
// $400 ---- --12 3333 4444 1:MEM4MB memory size 2:DAC18B dac for digital output 3:VER version number 4:MVOL
|
|
|
|
// $402 ---- ---1 1222 2222 1:RBL ring buffer length 2:RBP lead address
|
|
|
|
// $404 ---1 2345 6666 6666 1:MOFULL out fifo full 2:MOEMP empty 3:MIOVF overflow 4:MIFULL in 5:MIEMP 6:MIBUF
|
|
|
|
// $406 ---- ---- 1111 1111 1:MOBUF midi output data buffer
|
|
|
|
// $408 1111 1222 2334 4444 1:MSLC monitor slot 2:CA call address 3:SGC Slot phase 4:EG Slot envelope
|
|
|
|
// $40a ---- ---- ---- ----
|
|
|
|
// $40c ---- ---- ---- ----
|
|
|
|
// $40e ---- ---- ---- ----
|
|
|
|
// $410 ---- ---- ---- ----
|
|
|
|
// $412 1111 1111 1111 111- 1:DMEAL transfer start address (sound)
|
|
|
|
// $414 1111 2222 2222 222- 1:DMEAH transfer start address hi 2:DRGA start register address (dsp)
|
|
|
|
// $416 -123 4444 4444 444- 1:DGATE transfer gate 0 clear 2:DDIR direction 3:DEXE start 4:DTLG data count
|
|
|
|
// $418 ---- -111 2222 2222 1:TACTL timer a prescalar control 2:TIMA timer a count data
|
|
|
|
// $41a ---- -111 2222 2222 1:TBCTL timer b prescalar control 2:TIMB timer b count data
|
|
|
|
// $41c ---- -111 2222 2222 2:TCCTL timer c prescalar control 2:TIMC timer c count data
|
|
|
|
// $41e ---- -111 1111 1111 1:SCIEB allow sound cpu interrupt
|
|
|
|
// $420 ---- -111 1111 1111 1:SCIPD request sound cpu interrupt
|
|
|
|
// $422 ---- -111 1111 1111 1:SCIRE reset sound cpu interrupt
|
|
|
|
// $424 ---- ---- 1111 1111 1:SCILV0 sound cpu interrupt level bit0
|
|
|
|
// $426 ---- ---- 1111 1111 1:SCILV1 sound cpu interrupt level bit1
|
|
|
|
// $428 ---- ---- 1111 1111 1:SCILV2 sound cpu interrupt level bit2
|
|
|
|
// $42a ---- -111 1111 1111 1:MCIEB allow main cpu interrupt
|
|
|
|
// $42c ---- -111 1111 1111 1:MCIPD request main cpu interrupt
|
|
|
|
// $42e ---- -111 1111 1111 1:MCIRE reset main cpu interrupt
|
|
|
|
//
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// Individual Slot Register (ISR)
|
|
|
|
//
|
|
|
|
// $+00 $+01
|
|
|
|
// $00 ---1 2334 4556 7777 1:KYONEX 2:KYONB 3:SBCTL 4:SSCTL 5:LPCTL 6:PCM8B 7:SA start address
|
|
|
|
// $02 1111 1111 1111 1111 1:SA start address
|
|
|
|
// $04 1111 1111 1111 1111 1:LSA loop start address
|
|
|
|
// $06 1111 1111 1111 1111 1:LEA loop end address
|
|
|
|
// $08 1111 1222 2234 4444 1:D2R decay 2 rate 2:D1R decay 1 rate 3:EGHOLD eg hold mode 4:AR attack rate
|
|
|
|
// $0a -122 2233 3334 4444 1:LPSLNK loop start link 2:KRS key rate scaling 3:DL decay level 4:RR release rate
|
|
|
|
// $0c ---- --12 3333 3333 1:STWINH stack write inhibit 2:SDIR sound direct 3:TL total level
|
|
|
|
// $0e 1111 2222 2233 3333 1:MDL modulation level 2:MDXSL modulation input x 3:MDYSL modulation input y
|
|
|
|
// $10 -111 1-22 2222 2222 1:OCT octave 2:FNS frequency number switch
|
|
|
|
// $12 1222 2233 4445 5666 1:LFORE 2:LFOF 3:PLFOWS 4:PLFOS 5:ALFOWS 6:ALFOS
|
|
|
|
// $14 ---- ---- -111 1222 1:ISEL input select 2:OMXL input mix level
|
|
|
|
// $16 1112 2222 3334 4444 1:DISDL 2:DIPAN 3:EFSDL 4:EFPAN
|
|
|
|
//
|
|
|
|
//--------------------------------------------------------------
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
2013-05-03 19:51:03 +00:00
|
|
|
#include <stddef.h>
|
2013-04-30 21:20:11 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include "c68k/c68k.h"
|
|
|
|
#include "cs2.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "error.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "m68kcore.h"
|
|
|
|
#include "scu.h"
|
|
|
|
#include "yabause.h"
|
|
|
|
#include "scsp.h"
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
#include "windows/aviout.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PSP
|
|
|
|
# include "psp/common.h"
|
|
|
|
|
|
|
|
/* Macro to write a variable's value through the cache to main memory */
|
|
|
|
# define WRITE_THROUGH(var) (*(u32 *)((u32)&(var) | 0x40000000) = (var))
|
|
|
|
|
|
|
|
/* Macro to flush SCSP state so it can be read from the ME */
|
|
|
|
# define FLUSH_SCSP() sceKernelDcacheWritebackRange(&scsp, sizeof(scsp))
|
|
|
|
|
|
|
|
#else // !PSP
|
|
|
|
# define WRITE_THROUGH(var) /*nothing*/
|
|
|
|
# define FLUSH_SCSP() /*nothing*/
|
|
|
|
#endif
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#ifndef PI
|
|
|
|
#define PI 3.14159265358979323846
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define SCSP_FREQ 44100 // SCSP frequency
|
|
|
|
|
|
|
|
#define SCSP_RAM_SIZE 0x080000 // SCSP RAM size
|
|
|
|
#define SCSP_RAM_MASK (SCSP_RAM_SIZE - 1)
|
|
|
|
|
|
|
|
#define SCSP_MIDI_IN_EMP 0x01 // MIDI flags
|
|
|
|
#define SCSP_MIDI_IN_FUL 0x02
|
|
|
|
#define SCSP_MIDI_IN_OVF 0x04
|
|
|
|
#define SCSP_MIDI_OUT_EMP 0x08
|
|
|
|
#define SCSP_MIDI_OUT_FUL 0x10
|
|
|
|
|
|
|
|
#define SCSP_ENV_RELEASE 3 // Envelope phase
|
|
|
|
#define SCSP_ENV_SUSTAIN 2
|
|
|
|
#define SCSP_ENV_DECAY 1
|
|
|
|
#define SCSP_ENV_ATTACK 0
|
|
|
|
|
|
|
|
#define SCSP_FREQ_HB 19 // Freq counter int part
|
|
|
|
#define SCSP_FREQ_LB 10 // Freq counter float part
|
|
|
|
|
|
|
|
#define SCSP_ENV_HB 10 // Env counter int part
|
|
|
|
#define SCSP_ENV_LB 10 // Env counter float part
|
|
|
|
|
|
|
|
#define SCSP_LFO_HB 10 // LFO counter int part
|
|
|
|
#define SCSP_LFO_LB 10 // LFO counter float part
|
|
|
|
|
|
|
|
#define SCSP_ENV_LEN (1 << SCSP_ENV_HB) // Env table len
|
|
|
|
#define SCSP_ENV_MASK (SCSP_ENV_LEN - 1) // Env table mask
|
|
|
|
|
|
|
|
#define SCSP_FREQ_LEN (1 << SCSP_FREQ_HB) // Freq table len
|
|
|
|
#define SCSP_FREQ_MASK (SCSP_FREQ_LEN - 1) // Freq table mask
|
|
|
|
|
|
|
|
#define SCSP_LFO_LEN (1 << SCSP_LFO_HB) // LFO table len
|
|
|
|
#define SCSP_LFO_MASK (SCSP_LFO_LEN - 1) // LFO table mask
|
|
|
|
|
|
|
|
#define SCSP_ENV_AS 0 // Env Attack Start
|
|
|
|
#define SCSP_ENV_DS (SCSP_ENV_LEN << SCSP_ENV_LB) // Env Decay Start
|
|
|
|
#define SCSP_ENV_AE (SCSP_ENV_DS - 1) // Env Attack End
|
|
|
|
#define SCSP_ENV_DE (((2 * SCSP_ENV_LEN) << SCSP_ENV_LB) - 1) // Env Decay End
|
|
|
|
|
|
|
|
#define SCSP_ATTACK_R (u32) (8 * 44100)
|
|
|
|
#define SCSP_DECAY_R (u32) (12 * SCSP_ATTACK_R)
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
typedef struct slot_t
|
|
|
|
{
|
|
|
|
u8 swe; // stack write enable
|
|
|
|
u8 sdir; // sound direct
|
|
|
|
u8 pcm8b; // PCM sound format
|
|
|
|
|
|
|
|
u8 sbctl; // source bit control
|
|
|
|
u8 ssctl; // sound source control
|
|
|
|
u8 lpctl; // loop control
|
|
|
|
|
|
|
|
u8 key; // KEY_ state
|
|
|
|
u8 keyx; // still playing regardless the KEY_ state (hold, decay)
|
|
|
|
|
|
|
|
s8 *buf8; // sample buffer 8 bits
|
|
|
|
s16 *buf16; // sample buffer 16 bits
|
|
|
|
|
|
|
|
u32 fcnt; // phase counter
|
|
|
|
u32 finc; // phase step adder
|
|
|
|
u32 finct; // non adjusted phase step
|
|
|
|
|
|
|
|
s32 ecnt; // envelope counter
|
|
|
|
s32 *einc; // envelope current step adder
|
|
|
|
s32 einca; // envelope step adder for attack
|
|
|
|
s32 eincd; // envelope step adder for decay 1
|
|
|
|
s32 eincs; // envelope step adder for decay 2
|
|
|
|
s32 eincr; // envelope step adder for release
|
|
|
|
s32 ecmp; // envelope compare to raise next phase
|
|
|
|
u32 ecurp; // envelope current phase (attack / decay / release ...)
|
|
|
|
s32 env; // envelope multiplier (at time of last update)
|
|
|
|
|
|
|
|
void (*enxt)(struct slot_t *); // envelope function pointer for next phase event
|
|
|
|
|
|
|
|
u32 lfocnt; // lfo counter
|
|
|
|
s32 lfoinc; // lfo step adder
|
|
|
|
|
|
|
|
u32 sa; // start address
|
|
|
|
u32 lsa; // loop start address
|
|
|
|
u32 lea; // loop end address
|
|
|
|
|
|
|
|
s32 tl; // total level
|
|
|
|
s32 sl; // sustain level
|
|
|
|
|
|
|
|
s32 ar; // attack rate
|
|
|
|
s32 dr; // decay rate
|
|
|
|
s32 sr; // sustain rate
|
|
|
|
s32 rr; // release rate
|
|
|
|
|
|
|
|
s32 *arp; // attack rate table pointer
|
|
|
|
s32 *drp; // decay rate table pointer
|
|
|
|
s32 *srp; // sustain rate table pointer
|
|
|
|
s32 *rrp; // release rate table pointer
|
|
|
|
|
|
|
|
u32 krs; // key rate scale
|
|
|
|
|
|
|
|
s32 *lfofmw; // lfo frequency modulation waveform pointer
|
|
|
|
s32 *lfoemw; // lfo envelope modulation waveform pointer
|
|
|
|
u8 lfofms; // lfo frequency modulation sensitivity
|
|
|
|
u8 lfoems; // lfo envelope modulation sensitivity
|
|
|
|
u8 fsft; // frequency shift (used for freq lfo)
|
|
|
|
|
|
|
|
u8 mdl; // modulation level
|
|
|
|
u8 mdx; // modulation source X
|
|
|
|
u8 mdy; // modulation source Y
|
|
|
|
|
|
|
|
u8 imxl; // input sound level
|
|
|
|
u8 disll; // direct sound level left
|
|
|
|
u8 dislr; // direct sound level right
|
|
|
|
u8 efsll; // effect sound level left
|
|
|
|
u8 efslr; // effect sound level right
|
|
|
|
|
|
|
|
u8 eghold; // eg type envelope hold
|
|
|
|
u8 lslnk; // loop start link (start D1R when start loop adr is reached)
|
|
|
|
|
|
|
|
// NOTE: Previously there were u8 pads here to maintain 4-byte alignment.
|
|
|
|
// There are current 22 u8's in this struct and 1 u16. This makes 24
|
|
|
|
// bytes, so there are no pads at the moment.
|
|
|
|
//
|
|
|
|
// I'm not sure this is at all necessary either, but keeping this note
|
|
|
|
// in case.
|
|
|
|
} slot_t;
|
|
|
|
|
|
|
|
typedef struct scsp_t
|
|
|
|
{
|
|
|
|
u32 mem4b; // 4mbit memory
|
|
|
|
u32 mvol; // master volume
|
|
|
|
|
|
|
|
u32 rbl; // ring buffer lenght
|
|
|
|
u32 rbp; // ring buffer address (pointer)
|
|
|
|
|
|
|
|
u32 mslc; // monitor slot
|
|
|
|
u32 ca; // call address
|
|
|
|
u32 sgc; // phase
|
|
|
|
u32 eg; // envelope
|
|
|
|
|
|
|
|
u32 dmea; // dma memory address start
|
|
|
|
u32 drga; // dma register address start
|
|
|
|
u32 dmfl; // dma flags (direction / gate 0 ...)
|
|
|
|
u32 dmlen; // dma transfer len
|
|
|
|
|
|
|
|
u8 midinbuf[4]; // midi in buffer
|
|
|
|
u8 midoutbuf[4]; // midi out buffer
|
|
|
|
u8 midincnt; // midi in buffer size
|
|
|
|
u8 midoutcnt; // midi out buffer size
|
|
|
|
u8 midflag; // midi flag (empty, full, overflow ...)
|
|
|
|
u8 midflag2; // midi flag 2 (here only for alignement)
|
|
|
|
|
|
|
|
s32 timacnt; // timer A counter
|
|
|
|
u32 timasd; // timer A step diviser
|
|
|
|
s32 timbcnt; // timer B counter
|
|
|
|
u32 timbsd; // timer B step diviser
|
|
|
|
s32 timccnt; // timer C counter
|
|
|
|
u32 timcsd; // timer C step diviser
|
|
|
|
|
|
|
|
u32 scieb; // allow sound cpu interrupt
|
|
|
|
u32 scipd; // pending sound cpu interrupt
|
|
|
|
|
|
|
|
u32 scilv0; // IL0 M68000 interrupt pin state
|
|
|
|
u32 scilv1; // IL1 M68000 interrupt pin state
|
|
|
|
u32 scilv2; // IL2 M68000 interrupt pin state
|
|
|
|
|
|
|
|
u32 mcieb; // allow main cpu interrupt
|
|
|
|
u32 mcipd; // pending main cpu interrupt
|
|
|
|
|
2013-05-03 19:51:03 +00:00
|
|
|
s32 stack[32 * 2]; // two last generation slot output (SCSP STACK)
|
2013-05-03 22:12:35 +00:00
|
|
|
|
|
|
|
u32 slotstart;
|
|
|
|
|
2013-05-03 19:51:03 +00:00
|
|
|
slot_t slot[32]; // 32 slots
|
|
|
|
|
2013-04-30 21:20:11 +00:00
|
|
|
u8 *scsp_ram; // scsp ram pointer
|
|
|
|
void (*mintf)(void); // main cpu interupt function pointer
|
|
|
|
void (*sintf)(u32); // sound cpu interrupt function pointer
|
|
|
|
|
2013-05-03 19:51:03 +00:00
|
|
|
//s32 stack[32 * 2]; // two last generation slot output (SCSP STACK)
|
|
|
|
//slot_t slot[32]; // 32 slots
|
2013-04-30 21:20:11 +00:00
|
|
|
} scsp_t;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static s32 scsp_env_table[SCSP_ENV_LEN * 2]; // envelope curve table (attack & decay)
|
|
|
|
|
|
|
|
static s32 scsp_lfo_sawt_e[SCSP_LFO_LEN]; // lfo sawtooth waveform for envelope
|
|
|
|
static s32 scsp_lfo_squa_e[SCSP_LFO_LEN]; // lfo square waveform for envelope
|
|
|
|
static s32 scsp_lfo_tri_e[SCSP_LFO_LEN]; // lfo triangle waveform for envelope
|
|
|
|
static s32 scsp_lfo_noi_e[SCSP_LFO_LEN]; // lfo noise waveform for envelope
|
|
|
|
|
|
|
|
static s32 scsp_lfo_sawt_f[SCSP_LFO_LEN]; // lfo sawtooth waveform for frequency
|
|
|
|
static s32 scsp_lfo_squa_f[SCSP_LFO_LEN]; // lfo square waveform for frequency
|
|
|
|
static s32 scsp_lfo_tri_f[SCSP_LFO_LEN]; // lfo triangle waveform for frequency
|
|
|
|
static s32 scsp_lfo_noi_f[SCSP_LFO_LEN]; // lfo noise waveform frequency
|
|
|
|
|
|
|
|
static s32 scsp_attack_rate[0x40 + 0x20]; // envelope step for attack
|
|
|
|
static s32 scsp_decay_rate[0x40 + 0x20]; // envelope step for decay
|
|
|
|
static s32 scsp_null_rate[0x20]; // null envelope step
|
|
|
|
|
|
|
|
static s32 scsp_lfo_step[32]; // directly give the lfo counter step
|
|
|
|
|
|
|
|
static s32 scsp_tl_table[256]; // table of values for total level attentuation
|
|
|
|
|
|
|
|
static u8 scsp_reg[0x1000];
|
|
|
|
|
|
|
|
static u8 *scsp_isr;
|
|
|
|
static u8 *scsp_ccr;
|
|
|
|
static u8 *scsp_dcr;
|
|
|
|
|
|
|
|
static s32 *scsp_bufL;
|
|
|
|
static s32 *scsp_bufR;
|
|
|
|
static u32 scsp_buf_len;
|
|
|
|
static u32 scsp_buf_pos;
|
|
|
|
|
|
|
|
static scsp_t scsp; // SCSP structure
|
|
|
|
|
|
|
|
static union {
|
|
|
|
u8 sectors[2][2352];
|
|
|
|
u8 data[2*2352];
|
|
|
|
} cddabuf;
|
|
|
|
static unsigned int cddanextin; // Next sector buffer to receive into (0 or 1)
|
|
|
|
static u32 cddaoutleft; // Bytes of CDDA left to output
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static void scsp_env_null_next(slot_t *slot);
|
|
|
|
static void scsp_release_next(slot_t *slot);
|
|
|
|
static void scsp_sustain_next(slot_t *slot);
|
|
|
|
static void scsp_decay_next(slot_t *slot);
|
|
|
|
static void scsp_attack_next(slot_t *slot);
|
|
|
|
static void scsp_slot_update_keyon(slot_t *slot);
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static int scsp_mute_flags = 0;
|
|
|
|
static int scsp_volume = 100;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Misc
|
|
|
|
|
|
|
|
static int
|
|
|
|
scsp_round (double val)
|
|
|
|
{
|
|
|
|
return (int)(val + 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Interrupts
|
|
|
|
|
|
|
|
static INLINE void
|
|
|
|
scsp_trigger_main_interrupt (u32 id)
|
|
|
|
{
|
|
|
|
SCSPLOG ("scsp main interrupt accepted %.4X\n", id);
|
|
|
|
scsp.mintf ();
|
|
|
|
}
|
|
|
|
|
|
|
|
static INLINE void
|
|
|
|
scsp_trigger_sound_interrupt (u32 id)
|
|
|
|
{
|
|
|
|
u32 level;
|
|
|
|
level = 0;
|
|
|
|
if (id > 0x80) id = 0x80;
|
|
|
|
|
|
|
|
if (scsp.scilv0 & id) level |= 1;
|
|
|
|
if (scsp.scilv1 & id) level |= 2;
|
|
|
|
if (scsp.scilv2 & id) level |= 4;
|
|
|
|
|
|
|
|
#ifdef SCSP_DEBUG
|
|
|
|
if (id == 0x8) SCSPLOG ("scsp sound interrupt accepted %.2X lev=%d\n", id, level);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
scsp.sintf (level);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_main_interrupt (u32 id)
|
|
|
|
{
|
|
|
|
// if (scsp.mcipd & id) return;
|
|
|
|
// if (id != 0x400) SCSPLOG("scsp main interrupt %.4X\n", id);
|
|
|
|
|
|
|
|
scsp.mcipd |= id;
|
|
|
|
WRITE_THROUGH (scsp.mcipd);
|
|
|
|
|
|
|
|
if (scsp.mcieb & id)
|
|
|
|
scsp_trigger_main_interrupt (id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_sound_interrupt (u32 id)
|
|
|
|
{
|
|
|
|
// if (scsp.scipd & id) return;
|
|
|
|
|
|
|
|
// SCSPLOG ("scsp sound interrupt %.4X\n", id);
|
|
|
|
|
|
|
|
scsp.scipd |= id;
|
|
|
|
WRITE_THROUGH (scsp.scipd);
|
|
|
|
|
|
|
|
if (scsp.scieb & id)
|
|
|
|
scsp_trigger_sound_interrupt (id);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Direct Memory Access
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_dma (void)
|
|
|
|
{
|
|
|
|
if (scsp.dmfl & 0x20)
|
|
|
|
{
|
|
|
|
// dsp -> scsp_ram
|
|
|
|
SCSPLOG ("scsp dma: scsp_ram(%08lx) <- reg(%08lx) * %08lx\n",
|
|
|
|
scsp.dmea, scsp.drga, scsp.dmlen);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// scsp_ram -> dsp
|
|
|
|
SCSPLOG ("scsp dma: scsp_ram(%08lx) -> reg(%08lx) * %08lx\n",
|
|
|
|
scsp.dmea, scsp.drga, scsp.dmlen);
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp_ccr[0x16 ^ 3] &= 0xE0;
|
|
|
|
|
|
|
|
scsp_sound_interrupt (0x10);
|
|
|
|
scsp_main_interrupt (0x10);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Key ON/OFF event handler
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_keyon (slot_t *slot)
|
|
|
|
{
|
|
|
|
// key need to be released before being pressed ;)
|
|
|
|
if (slot->ecurp == SCSP_ENV_RELEASE)
|
|
|
|
{
|
|
|
|
SCSPLOG ("key on slot %d. 68K PC = %08X slot->sa = %08X slot->lsa = %08X "
|
|
|
|
"slot->lea = %08X\n", slot - &(scsp.slot[0]), M68K->GetPC(),
|
|
|
|
slot->sa, slot->lsa, slot->lea >> SCSP_FREQ_LB);
|
|
|
|
|
|
|
|
// set buffer, loop start/end address of the slot
|
|
|
|
if (slot->pcm8b)
|
|
|
|
{
|
|
|
|
slot->buf8 = (s8*) &(scsp.scsp_ram[slot->sa]);
|
|
|
|
if ((slot->sa + (slot->lea >> SCSP_FREQ_LB)) > SCSP_RAM_MASK)
|
|
|
|
slot->lea = (SCSP_RAM_MASK - slot->sa) << SCSP_FREQ_LB;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slot->buf16 = (s16*) &(scsp.scsp_ram[slot->sa & ~1]);
|
|
|
|
if ((slot->sa + (slot->lea >> (SCSP_FREQ_LB - 1))) > SCSP_RAM_MASK)
|
|
|
|
slot->lea = (SCSP_RAM_MASK - slot->sa) << (SCSP_FREQ_LB - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
slot->fcnt = 0; // reset frequency counter
|
|
|
|
slot->ecnt = SCSP_ENV_AS; // reset envelope counter (probably wrong,
|
|
|
|
// should convert decay to attack?)
|
|
|
|
slot->env = 0; // reset envelope
|
|
|
|
|
|
|
|
slot->einc = &slot->einca; // envelope counter step is attack step
|
|
|
|
slot->ecurp = SCSP_ENV_ATTACK; // current envelope phase is attack
|
|
|
|
slot->ecmp = SCSP_ENV_AE; // limit reach to next event (Attack End)
|
|
|
|
slot->enxt = scsp_attack_next; // function pointer to next event
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_keyoff (slot_t *slot)
|
|
|
|
{
|
|
|
|
// key need to be pressed before being released ;)
|
|
|
|
if (slot->ecurp != SCSP_ENV_RELEASE)
|
|
|
|
{
|
|
|
|
SCSPLOG ("key off slot %d\n", slot - &(scsp.slot[0]));
|
|
|
|
|
|
|
|
// if we still are in attack phase at release time, convert attack to decay
|
|
|
|
if (slot->ecurp == SCSP_ENV_ATTACK)
|
|
|
|
slot->ecnt = SCSP_ENV_DE - slot->ecnt;
|
|
|
|
slot->einc = &slot->eincr;
|
|
|
|
slot->ecmp = SCSP_ENV_DE;
|
|
|
|
slot->ecurp = SCSP_ENV_RELEASE;
|
|
|
|
slot->enxt = scsp_release_next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_keyonoff (void)
|
|
|
|
{
|
|
|
|
slot_t *slot;
|
|
|
|
|
|
|
|
for(slot = &(scsp.slot[0]); slot < &(scsp.slot[32]); slot++)
|
|
|
|
{
|
|
|
|
if (slot->key)
|
|
|
|
scsp_slot_keyon (slot);
|
|
|
|
else
|
|
|
|
scsp_slot_keyoff (slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Envelope Events Handler
|
|
|
|
|
|
|
|
Max EG level = 0x3FF /|\
|
|
|
|
/ | \
|
|
|
|
/ | \_____
|
|
|
|
Min EG level = 0x000 __/ | | |\___
|
|
|
|
A D1 D2 R
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_env_null_next (UNUSED slot_t *slot)
|
|
|
|
{
|
|
|
|
// only to prevent null call pointer...
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_release_next (slot_t *slot)
|
|
|
|
{
|
|
|
|
// end of release happened, update to process the next phase...
|
|
|
|
|
|
|
|
slot->ecnt = SCSP_ENV_DE;
|
|
|
|
slot->einc = NULL;
|
|
|
|
slot->ecmp = SCSP_ENV_DE + 1;
|
|
|
|
slot->enxt = scsp_env_null_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_sustain_next (slot_t *slot)
|
|
|
|
{
|
|
|
|
// end of sustain happened, update to process the next phase...
|
|
|
|
|
|
|
|
slot->ecnt = SCSP_ENV_DE;
|
|
|
|
slot->einc = NULL;
|
|
|
|
slot->ecmp = SCSP_ENV_DE + 1;
|
|
|
|
slot->enxt = scsp_env_null_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_decay_next (slot_t *slot)
|
|
|
|
{
|
|
|
|
// end of decay happened, update to process the next phase...
|
|
|
|
|
|
|
|
slot->ecnt = slot->sl;
|
|
|
|
slot->einc = &slot->eincs;
|
|
|
|
slot->ecmp = SCSP_ENV_DE;
|
|
|
|
slot->ecurp = SCSP_ENV_SUSTAIN;
|
|
|
|
slot->enxt = scsp_sustain_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_attack_next (slot_t *slot)
|
|
|
|
{
|
|
|
|
// end of attack happened, update to process the next phase...
|
|
|
|
|
|
|
|
slot->ecnt = SCSP_ENV_DS;
|
|
|
|
slot->einc = &slot->eincd;
|
|
|
|
slot->ecmp = slot->sl;
|
|
|
|
slot->ecurp = SCSP_ENV_DECAY;
|
|
|
|
slot->enxt = scsp_decay_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Slot Access
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_refresh_einc (slot_t *slot, u32 adsr_bitmask)
|
|
|
|
{
|
|
|
|
if (slot->arp && (adsr_bitmask & 0x1))
|
|
|
|
slot->einca = slot->arp[(14 - slot->fsft) >> slot->krs];
|
|
|
|
if (slot->drp && (adsr_bitmask & 0x2))
|
|
|
|
slot->eincd = slot->drp[(14 - slot->fsft) >> slot->krs];
|
|
|
|
if (slot->srp && (adsr_bitmask & 0x4))
|
|
|
|
slot->eincs = slot->srp[(14 - slot->fsft) >> slot->krs];
|
|
|
|
if (slot->rrp && (adsr_bitmask & 0x8))
|
|
|
|
slot->eincr = slot->rrp[(14 - slot->fsft) >> slot->krs];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_set_b (u32 s, u32 a, u8 d)
|
|
|
|
{
|
|
|
|
slot_t *slot = &(scsp.slot[s]);
|
|
|
|
|
|
|
|
SCSPLOG("slot %d : reg %.2X = %.2X\n", s, a & 0x1F, d);
|
|
|
|
|
|
|
|
scsp_isr[a ^ 3] = d;
|
|
|
|
|
|
|
|
switch (a & 0x1F)
|
|
|
|
{
|
|
|
|
case 0x00: // KX/KB/SBCTL/SSCTL(high bit)
|
|
|
|
slot->key = (d >> 3) & 1;
|
|
|
|
slot->sbctl = (d >> 1) & 3;
|
|
|
|
slot->ssctl = (slot->ssctl & 1) + ((d & 1) << 1);
|
|
|
|
|
|
|
|
if (d & 0x10) scsp_slot_keyonoff ();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x01: // SSCTL(low bit)/LPCTL/8B/SA(highest 4 bits)
|
|
|
|
slot->ssctl = (slot->ssctl & 2) + ((d >> 7) & 1);
|
|
|
|
slot->lpctl = (d >> 5) & 3;
|
|
|
|
|
|
|
|
slot->pcm8b = d & 0x10;
|
|
|
|
slot->sa = (slot->sa & 0x0FFFF) + ((d & 0xF) << 16);
|
|
|
|
slot->sa &= SCSP_RAM_MASK;
|
|
|
|
|
|
|
|
if (slot->ecnt < SCSP_ENV_DE) scsp_slot_update_keyon (slot);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x02: // SA(next highest byte)
|
|
|
|
slot->sa = (slot->sa & 0xF00FF) + (d << 8);
|
|
|
|
slot->sa &= SCSP_RAM_MASK;
|
|
|
|
|
|
|
|
if (slot->ecnt < SCSP_ENV_DE) scsp_slot_update_keyon (slot);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x03: // SA(low byte)
|
|
|
|
slot->sa = (slot->sa & 0xFFF00) + d;
|
|
|
|
slot->sa &= SCSP_RAM_MASK;
|
|
|
|
|
|
|
|
if (slot->ecnt < SCSP_ENV_DE) scsp_slot_update_keyon (slot);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x04: // LSA(high byte)
|
|
|
|
slot->lsa = (slot->lsa & (0x00FF << SCSP_FREQ_LB)) +
|
|
|
|
(d << (8 + SCSP_FREQ_LB));
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x05: // LSA(low byte)
|
|
|
|
slot->lsa = (slot->lsa & (0xFF00 << SCSP_FREQ_LB)) +
|
|
|
|
(d << SCSP_FREQ_LB);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x06: // LEA(high byte)
|
|
|
|
slot->lea = (slot->lea & (0x00FF << SCSP_FREQ_LB)) +
|
|
|
|
(d << (8 + SCSP_FREQ_LB));
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x07: // LEA(low byte)
|
|
|
|
slot->lea = (slot->lea & (0xFF00 << SCSP_FREQ_LB)) +
|
|
|
|
(d << SCSP_FREQ_LB);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x08: // D2R/D1R(highest 3 bits)
|
|
|
|
slot->sr = (d >> 3) & 0x1F;
|
|
|
|
slot->dr = (slot->dr & 0x03) + ((d & 7) << 2);
|
|
|
|
|
|
|
|
if (slot->sr)
|
|
|
|
slot->srp = &scsp_decay_rate[slot->sr << 1];
|
|
|
|
else
|
|
|
|
slot->srp = &scsp_null_rate[0];
|
|
|
|
|
|
|
|
if (slot->dr)
|
|
|
|
slot->drp = &scsp_decay_rate[slot->dr << 1];
|
|
|
|
else
|
|
|
|
slot->drp = &scsp_null_rate[0];
|
|
|
|
|
|
|
|
scsp_slot_refresh_einc (slot, 0x2 | 0x4);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x09: // D1R(lowest 2 bits)/EGHOLD/AR
|
|
|
|
slot->dr = (slot->dr & 0x1C) + ((d >> 6) & 3);
|
|
|
|
slot->eghold = d & 0x20;
|
|
|
|
slot->ar = d & 0x1F;
|
|
|
|
|
|
|
|
if (slot->dr)
|
|
|
|
slot->drp = &scsp_decay_rate[slot->dr << 1];
|
|
|
|
else
|
|
|
|
slot->drp = &scsp_null_rate[0];
|
|
|
|
|
|
|
|
if (slot->ar)
|
|
|
|
slot->arp = &scsp_attack_rate[slot->ar << 1];
|
|
|
|
else
|
|
|
|
slot->arp = &scsp_null_rate[0];
|
|
|
|
|
|
|
|
scsp_slot_refresh_einc (slot, 0x1 | 0x2);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x0A: // LPSLNK/KRS/DL(highest 2 bits)
|
|
|
|
slot->lslnk = d & 0x40;
|
|
|
|
slot->krs = (d >> 2) & 0xF;
|
|
|
|
|
|
|
|
if (slot->krs == 0xF)
|
|
|
|
slot->krs = 4;
|
|
|
|
else
|
|
|
|
slot->krs >>= 2;
|
|
|
|
|
|
|
|
slot->sl &= 0xE0 << SCSP_ENV_LB;
|
|
|
|
slot->sl += (d & 3) << (8 + SCSP_ENV_LB);
|
|
|
|
slot->sl += SCSP_ENV_DS; // adjusted for envelope compare (ecmp)
|
|
|
|
|
|
|
|
scsp_slot_refresh_einc (slot, 0xF);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x0B: // DL(lowest 3 bits)/RR
|
|
|
|
slot->sl &= 0x300 << SCSP_ENV_LB;
|
|
|
|
slot->sl += (d & 0xE0) << SCSP_ENV_LB;
|
|
|
|
slot->sl += SCSP_ENV_DS; // adjusted for envelope compare (ecmp)
|
|
|
|
slot->rr = d & 0x1F;
|
|
|
|
|
|
|
|
if (slot->rr)
|
|
|
|
slot->rrp = &scsp_decay_rate[slot->rr << 1];
|
|
|
|
else
|
|
|
|
slot->rrp = &scsp_null_rate[0];
|
|
|
|
|
|
|
|
scsp_slot_refresh_einc (slot, 0x8);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x0C: // STWINH/SDIR
|
|
|
|
slot->sdir = d & 2;
|
|
|
|
slot->swe = d & 1;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x0D: // TL
|
|
|
|
slot->tl = scsp_tl_table[(d & 0xFF)];
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x0E: // MDL/MDXSL(highest 4 bits)
|
|
|
|
slot->mdl = (d >> 4) & 0xF; // need to adjust for correct shift
|
|
|
|
slot->mdx = (slot->mdx & 3) + ((d & 0xF) << 2);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x0F: // MDXSL(lowest 2 bits)/MDYSL
|
|
|
|
slot->mdx = (slot->mdx & 0x3C) + ((d >> 6) & 3);
|
|
|
|
slot->mdy = d & 0x3F;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x10: // OCT/FNS(highest 2 bits)
|
|
|
|
if (d & 0x40)
|
|
|
|
slot->fsft = 23 - ((d >> 3) & 0xF);
|
|
|
|
else
|
|
|
|
slot->fsft = ((d >> 3) & 7) ^ 7;
|
|
|
|
|
|
|
|
slot->finct = (slot->finct & 0x7F80) + ((d & 3) << (8 + 7));
|
|
|
|
slot->finc = (0x20000 + slot->finct) >> slot->fsft;
|
|
|
|
|
|
|
|
scsp_slot_refresh_einc (slot, 0xF);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x11: // FNS(low byte)
|
|
|
|
slot->finct = (slot->finct & 0x18000) + (d << 7);
|
|
|
|
slot->finc = (0x20000 + slot->finct) >> slot->fsft;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x12: // LFORE/LFOF/PLFOWS
|
|
|
|
if (d & 0x80)
|
|
|
|
{
|
|
|
|
slot->lfoinc = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (slot->lfoinc == -1)
|
|
|
|
{
|
|
|
|
slot->lfocnt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
slot->lfoinc = scsp_lfo_step[(d >> 2) & 0x1F];
|
|
|
|
|
|
|
|
switch (d & 3)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
slot->lfofmw = scsp_lfo_sawt_f;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
slot->lfofmw = scsp_lfo_squa_f;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
slot->lfofmw = scsp_lfo_tri_f;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
slot->lfofmw = scsp_lfo_noi_f;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x13: // PLFOS/ALFOWS/ALFOS
|
|
|
|
if ((d >> 5) & 7)
|
|
|
|
slot->lfofms = ((d >> 5) & 7) + 7;
|
|
|
|
else
|
|
|
|
slot->lfofms = 31;
|
|
|
|
|
|
|
|
if (d & 7)
|
|
|
|
slot->lfoems = ((d & 7) ^ 7) + 4;
|
|
|
|
else
|
|
|
|
slot->lfoems = 31;
|
|
|
|
|
|
|
|
switch ((d >> 3) & 3)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
slot->lfoemw = scsp_lfo_sawt_e;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
slot->lfoemw = scsp_lfo_squa_e;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
slot->lfoemw = scsp_lfo_tri_e;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
slot->lfoemw = scsp_lfo_noi_e;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x15: // ISEL/OMXL
|
|
|
|
if (d & 7)
|
|
|
|
slot->imxl = ((d & 7) ^ 7) + SCSP_ENV_HB;
|
|
|
|
else
|
|
|
|
slot->imxl = 31;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x16: // DISDL/DIPAN
|
|
|
|
if (d & 0xE0)
|
|
|
|
{
|
|
|
|
// adjusted for envelope calculation
|
|
|
|
// some inaccuracy in panning though...
|
|
|
|
slot->dislr = slot->disll = (((d >> 5) & 7) ^ 7) + SCSP_ENV_HB;
|
|
|
|
if (d & 0x10)
|
|
|
|
{
|
|
|
|
// Panning Left
|
|
|
|
if ((d & 0xF) == 0xF)
|
|
|
|
slot->dislr = 31;
|
|
|
|
else
|
|
|
|
slot->dislr += (d >> 1) & 7;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Panning Right
|
|
|
|
if ((d & 0xF) == 0xF)
|
|
|
|
slot->disll = 31;
|
|
|
|
else
|
|
|
|
slot->disll += (d >> 1) & 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slot->dislr = slot->disll = 31; // muted
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x17: // EFSDL/EFPAN
|
|
|
|
if (d & 0xE0)
|
|
|
|
{
|
|
|
|
slot->efslr = slot->efsll = (((d >> 5) & 7) ^ 7) + SCSP_ENV_HB;
|
|
|
|
if (d & 0x10)
|
|
|
|
{
|
|
|
|
// Panning Left
|
|
|
|
if ((d & 0xF) == 0xF)
|
|
|
|
slot->efslr = 31;
|
|
|
|
else
|
|
|
|
slot->efslr += (d >> 1) & 7;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Panning Right
|
|
|
|
if ((d & 0xF) == 0xF)
|
|
|
|
slot->efsll = 31;
|
|
|
|
else
|
|
|
|
slot->efsll += (d >> 1) & 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slot->efslr = slot->efsll = 31; // muted
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_set_w (u32 s, s32 a, u16 d)
|
|
|
|
{
|
|
|
|
slot_t *slot = &(scsp.slot[s]);
|
|
|
|
|
|
|
|
SCSPLOG ("slot %d : reg %.2X = %.4X\n", s, a & 0x1E, d);
|
|
|
|
|
|
|
|
*(u16 *)&scsp_isr[a ^ 2] = d;
|
|
|
|
|
|
|
|
switch (a & 0x1E)
|
|
|
|
{
|
|
|
|
case 0x00: // KYONEX/KYONB/SBCTL/SSCTL/LPCTL/PCM8B/SA(highest 4 bits)
|
|
|
|
slot->key = (d >> 11) & 1;
|
|
|
|
slot->sbctl = (d >> 9) & 3;
|
|
|
|
slot->ssctl = (d >> 7) & 3;
|
|
|
|
slot->lpctl = (d >> 5) & 3;
|
|
|
|
|
|
|
|
slot->pcm8b = d & 0x10;
|
|
|
|
slot->sa = (slot->sa & 0x0FFFF) | ((d & 0xF) << 16);
|
|
|
|
slot->sa &= SCSP_RAM_MASK;
|
|
|
|
|
|
|
|
if (slot->ecnt < SCSP_ENV_DE)
|
|
|
|
scsp_slot_update_keyon(slot);
|
|
|
|
|
|
|
|
if (d & 0x1000)
|
|
|
|
scsp_slot_keyonoff();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x02: // SA(low word)
|
|
|
|
slot->sa = (slot->sa & 0xF0000) | d;
|
|
|
|
slot->sa &= SCSP_RAM_MASK;
|
|
|
|
|
|
|
|
if (slot->ecnt < SCSP_ENV_DE)
|
|
|
|
scsp_slot_update_keyon(slot);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x04: // LSA
|
|
|
|
slot->lsa = d << SCSP_FREQ_LB;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x06: // LEA
|
|
|
|
slot->lea = d << SCSP_FREQ_LB;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x08: // D2R/D1R/EGHOLD/AR
|
|
|
|
slot->sr = (d >> 11) & 0x1F;
|
|
|
|
slot->dr = (d >> 6) & 0x1F;
|
|
|
|
slot->eghold = d & 0x20;
|
|
|
|
slot->ar = d & 0x1F;
|
|
|
|
|
|
|
|
if (slot->sr)
|
|
|
|
slot->srp = &scsp_decay_rate[slot->sr << 1];
|
|
|
|
else
|
|
|
|
slot->srp = &scsp_null_rate[0];
|
|
|
|
|
|
|
|
if (slot->dr)
|
|
|
|
slot->drp = &scsp_decay_rate[slot->dr << 1];
|
|
|
|
else
|
|
|
|
slot->drp = &scsp_null_rate[0];
|
|
|
|
|
|
|
|
if (slot->ar)
|
|
|
|
slot->arp = &scsp_attack_rate[slot->ar << 1];
|
|
|
|
else
|
|
|
|
slot->arp = &scsp_null_rate[0];
|
|
|
|
|
|
|
|
scsp_slot_refresh_einc (slot, 0x1 | 0x2 | 0x4);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x0A: // LPSLNK/KRS/DL/RR
|
|
|
|
slot->lslnk = (d >> 8) & 0x40;
|
|
|
|
slot->krs = (d >> 10) & 0xF;
|
|
|
|
|
|
|
|
if (slot->krs == 0xF)
|
|
|
|
slot->krs = 4;
|
|
|
|
else
|
|
|
|
slot->krs >>= 2;
|
|
|
|
|
|
|
|
slot->sl = ((d & 0x3E0) << SCSP_ENV_LB) + SCSP_ENV_DS; // adjusted for envelope compare (ecmp)
|
|
|
|
slot->rr = d & 0x1F;
|
|
|
|
|
|
|
|
if (slot->rr)
|
|
|
|
slot->rrp = &scsp_decay_rate[slot->rr << 1];
|
|
|
|
else
|
|
|
|
slot->rrp = &scsp_null_rate[0];
|
|
|
|
|
|
|
|
scsp_slot_refresh_einc (slot, 0xF);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x0C: // STWINH/SDIR
|
|
|
|
slot->sdir = (d >> 8) & 2;
|
|
|
|
slot->swe = (d >> 8) & 1;
|
|
|
|
slot->tl = scsp_tl_table[(d & 0xFF)];
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x0E: // MDL/MDXSL/MDYSL
|
|
|
|
slot->mdl = (d >> 12) & 0xF; // need to adjust for correct shift
|
|
|
|
slot->mdx = (d >> 6) & 0x3F;
|
|
|
|
slot->mdy = d & 0x3F;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x10: // OCT/FNS
|
|
|
|
if (d & 0x4000)
|
|
|
|
slot->fsft = 23 - ((d >> 11) & 0xF);
|
|
|
|
else
|
|
|
|
slot->fsft = (((d >> 11) & 7) ^ 7);
|
|
|
|
|
|
|
|
slot->finc = ((0x400 + (d & 0x3FF)) << 7) >> slot->fsft;
|
|
|
|
|
|
|
|
scsp_slot_refresh_einc (slot, 0xF);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x12: // LFORE/LFOF/PLFOWS/PLFOS/ALFOWS/ALFOS
|
|
|
|
if (d & 0x8000)
|
|
|
|
{
|
|
|
|
slot->lfoinc = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (slot->lfoinc == -1)
|
|
|
|
{
|
|
|
|
slot->lfocnt = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
slot->lfoinc = scsp_lfo_step[(d >> 10) & 0x1F];
|
|
|
|
if ((d >> 5) & 7)
|
|
|
|
slot->lfofms = ((d >> 5) & 7) + 7;
|
|
|
|
else
|
|
|
|
slot->lfofms = 31;
|
|
|
|
|
|
|
|
if (d & 7)
|
|
|
|
slot->lfoems = ((d & 7) ^ 7) + 4;
|
|
|
|
else
|
|
|
|
slot->lfoems = 31;
|
|
|
|
|
|
|
|
switch ((d >> 8) & 3)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
slot->lfofmw = scsp_lfo_sawt_f;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
slot->lfofmw = scsp_lfo_squa_f;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
slot->lfofmw = scsp_lfo_tri_f;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
slot->lfofmw = scsp_lfo_noi_f;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ((d >> 3) & 3)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
slot->lfoemw = scsp_lfo_sawt_e;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
slot->lfoemw = scsp_lfo_squa_e;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
slot->lfoemw = scsp_lfo_tri_e;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
slot->lfoemw = scsp_lfo_noi_e;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x14: // ISEL/OMXL
|
|
|
|
if (d & 7)
|
|
|
|
slot->imxl = ((d & 7) ^ 7) + SCSP_ENV_HB;
|
|
|
|
else
|
|
|
|
slot->imxl = 31;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x16: // DISDL/DIPAN/EFSDL/EFPAN
|
|
|
|
if (d & 0xE000)
|
|
|
|
{
|
|
|
|
// adjusted fr enveloppe calculation
|
|
|
|
// some accuracy lose for panning here...
|
|
|
|
slot->dislr = slot->disll = (((d >> 13) & 7) ^ 7) + SCSP_ENV_HB;
|
|
|
|
if (d & 0x1000)
|
|
|
|
{
|
|
|
|
// Panning Left
|
|
|
|
if ((d & 0xF00) == 0xF00)
|
|
|
|
slot->dislr = 31;
|
|
|
|
else
|
|
|
|
slot->dislr += (d >> 9) & 7;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Panning Right
|
|
|
|
if ((d & 0xF00) == 0xF00)
|
|
|
|
slot->disll = 31;
|
|
|
|
else
|
|
|
|
slot->disll += (d >> 9) & 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slot->dislr = slot->disll = 31; // muted
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d & 0xE0)
|
|
|
|
{
|
|
|
|
slot->efslr = slot->efsll = (((d >> 5) & 7) ^ 7) + SCSP_ENV_HB;
|
|
|
|
if (d & 0x10)
|
|
|
|
{
|
|
|
|
// Panning Left
|
|
|
|
if ((d & 0xF) == 0xF)
|
|
|
|
slot->efslr = 31;
|
|
|
|
else
|
|
|
|
slot->efslr += (d >> 1) & 7;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Panning Right
|
|
|
|
if ((d & 0xF) == 0xF)
|
|
|
|
slot->efsll = 31;
|
|
|
|
else
|
|
|
|
slot->efsll += (d >> 1) & 7;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slot->efslr = slot->efsll = 31; // muted
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8
|
|
|
|
scsp_slot_get_b (u32 s, u32 a)
|
|
|
|
{
|
|
|
|
u8 val = scsp_isr[a ^ 3];
|
|
|
|
|
|
|
|
// Mask out keyonx
|
|
|
|
if ((a & 0x1F) == 0x00) val &= 0xEF;
|
|
|
|
|
|
|
|
SCSPLOG ("r_b slot %d (%.2X) : reg %.2X = %.2X\n", s, a, a & 0x1F, val);
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16 scsp_slot_get_w(u32 s, u32 a)
|
|
|
|
{
|
|
|
|
u16 val = *(u16 *)&scsp_isr[a ^ 2];
|
|
|
|
|
|
|
|
if ((a & 0x1E) == 0x00) return val &= 0xEFFF;
|
|
|
|
|
|
|
|
SCSPLOG ("r_w slot %d (%.2X) : reg %.2X = %.4X\n", s, a, a & 0x1E, val);
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// SCSP Access
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_set_b (u32 a, u8 d)
|
|
|
|
{
|
|
|
|
if ((a != 0x408) && (a != 0x41D))
|
|
|
|
{
|
|
|
|
SCSPLOG("scsp : reg %.2X = %.2X\n", a & 0x3F, d);
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp_ccr[a ^ 3] = d;
|
|
|
|
|
|
|
|
switch (a & 0x3F)
|
|
|
|
{
|
|
|
|
case 0x00: // MEM4MB/DAC18B
|
|
|
|
scsp.mem4b = (d >> 1) & 0x1;
|
|
|
|
if (scsp.mem4b)
|
|
|
|
{
|
|
|
|
M68K->SetFetch(0x000000, 0x080000, (pointer)SoundRam);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
M68K->SetFetch(0x000000, 0x040000, (pointer)SoundRam);
|
|
|
|
M68K->SetFetch(0x040000, 0x080000, (pointer)SoundRam);
|
|
|
|
M68K->SetFetch(0x080000, 0x0C0000, (pointer)SoundRam);
|
|
|
|
M68K->SetFetch(0x0C0000, 0x100000, (pointer)SoundRam);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x01: // VER/MVOL
|
|
|
|
scsp.mvol = d & 0xF;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x02: // RBL(high bit)
|
|
|
|
scsp.rbl = (scsp.rbl & 1) + ((d & 1) << 1);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x03: // RBL(low bit)/RBP
|
|
|
|
scsp.rbl = (scsp.rbl & 2) + ((d >> 7) & 1);
|
|
|
|
scsp.rbp = (d & 0x7F) * (4 * 1024 * 2);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x07: // MOBUF
|
|
|
|
scsp_midi_out_send(d);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x08: // MSLC
|
|
|
|
scsp.mslc = (d >> 3) & 0x1F;
|
|
|
|
scsp_update_monitor ();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x12: // DMEAL(high byte)
|
|
|
|
scsp.dmea = (scsp.dmea & 0x700FE) + (d << 8);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x13: // DMEAL(low byte)
|
|
|
|
scsp.dmea = (scsp.dmea & 0x7FF00) + (d & 0xFE);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x14: // DMEAH(high byte)
|
|
|
|
scsp.dmea = (scsp.dmea & 0xFFFE) + ((d & 0x70) << 12);
|
|
|
|
scsp.drga = (scsp.drga & 0xFE) + ((d & 0xF) << 8);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x15: // DMEAH(low byte)
|
|
|
|
scsp.drga = (scsp.drga & 0xF00) + (d & 0xFE);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x16: // DGATE/DDIR/DEXE/DTLG(upper 4 bits)
|
|
|
|
scsp.dmlen = (scsp.dmlen & 0xFE) + ((d & 0xF) << 8);
|
|
|
|
if ((scsp.dmfl = d & 0xF0) & 0x10) scsp_dma ();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x17: // DTLG(lower byte)
|
|
|
|
scsp.dmlen = (scsp.dmlen & 0xF00) + (d & 0xFE);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x18: // TACTL
|
|
|
|
scsp.timasd = d & 7;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x19: // TIMA
|
|
|
|
scsp.timacnt = d << 8;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x1A: // TBCTL
|
|
|
|
scsp.timbsd = d & 7;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x1B: // TIMB
|
|
|
|
scsp.timbcnt = d << 8;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x1C: // TCCTL
|
|
|
|
scsp.timcsd = d & 7;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x1D: // TIMC
|
|
|
|
scsp.timccnt = d << 8;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x1E: // SCIEB(high byte)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
scsp.scieb = (scsp.scieb & 0xFF) + (d << 8);
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
if (scsp.scieb & (1 << i) && scsp.scipd & (1 << i))
|
|
|
|
scsp_trigger_sound_interrupt ((1 << (i+8)));
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case 0x1F: // SCIEB(low byte)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
scsp.scieb = (scsp.scieb & 0x700) + d;
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
if (scsp.scieb & (1 << i) && scsp.scipd & (1 << i))
|
|
|
|
scsp_trigger_sound_interrupt ((1 << i));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case 0x21: // SCIPD(low byte)
|
|
|
|
if (d & 0x20) scsp_sound_interrupt (0x20);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x22: // SCIRE(high byte)
|
|
|
|
scsp.scipd &= ~(d << 8);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x23: // SCIRE(low byte)
|
|
|
|
scsp.scipd &= ~(u32)d;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x25: // SCILV0
|
|
|
|
scsp.scilv0 = d;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x27: // SCILV1
|
|
|
|
scsp.scilv1 = d;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x29: // SCILV2
|
|
|
|
scsp.scilv2 = d;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x2A: // MCIEB(high byte)
|
|
|
|
scsp.mcieb = (scsp.mcieb & 0xFF) + (d << 8);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x2B: // MCIEB(low byte)
|
|
|
|
scsp.mcieb = (scsp.mcieb & 0x700) + d;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x2D: // MCIPD(low byte)
|
|
|
|
if (d & 0x20)
|
|
|
|
scsp_main_interrupt(0x20);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x2E: // MCIRE(high byte)
|
|
|
|
scsp.mcipd &= ~(d << 8);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x2F: // MCIRE(low byte)
|
|
|
|
scsp.mcipd &= ~(u32)d;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_set_w (u32 a, u16 d)
|
|
|
|
{
|
|
|
|
if ((a != 0x418) && (a != 0x41A) && (a != 0x422))
|
|
|
|
{
|
|
|
|
SCSPLOG("scsp : reg %.2X = %.4X\n", a & 0x3E, d);
|
|
|
|
}
|
|
|
|
|
|
|
|
*(u16 *)&scsp_ccr[a ^ 2] = d;
|
|
|
|
|
|
|
|
switch (a & 0x3E)
|
|
|
|
{
|
|
|
|
case 0x00: // MEM4MB/DAC18B/VER/MVOL
|
|
|
|
scsp.mem4b = (d >> 9) & 0x1;
|
|
|
|
scsp.mvol = d & 0xF;
|
|
|
|
if (scsp.mem4b)
|
|
|
|
{
|
|
|
|
M68K->SetFetch(0x000000, 0x080000, (pointer)SoundRam);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
M68K->SetFetch(0x000000, 0x040000, (pointer)SoundRam);
|
|
|
|
M68K->SetFetch(0x040000, 0x080000, (pointer)SoundRam);
|
|
|
|
M68K->SetFetch(0x080000, 0x0C0000, (pointer)SoundRam);
|
|
|
|
M68K->SetFetch(0x0C0000, 0x100000, (pointer)SoundRam);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x02: // RBL/RBP
|
|
|
|
scsp.rbl = (d >> 7) & 3;
|
|
|
|
scsp.rbp = (d & 0x7F) * (4 * 1024 * 2);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x06: // MOBUF
|
|
|
|
scsp_midi_out_send(d & 0xFF);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x08: // MSLC
|
|
|
|
scsp.mslc = (d >> 11) & 0x1F;
|
|
|
|
scsp_update_monitor();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x12: // DMEAL
|
|
|
|
scsp.dmea = (scsp.dmea & 0x70000) + (d & 0xFFFE);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x14: // DMEAH/DRGA
|
|
|
|
scsp.dmea = (scsp.dmea & 0xFFFE) + ((d & 0x7000) << 4);
|
|
|
|
scsp.drga = d & 0xFFE;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x16: // DGATE/DDIR/DEXE/DTLG
|
|
|
|
scsp.dmlen = d & 0xFFE;
|
|
|
|
if ((scsp.dmfl = ((d >> 8) & 0xF0)) & 0x10) scsp_dma ();
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x18: // TACTL/TIMA
|
|
|
|
scsp.timasd = (d >> 8) & 7;
|
|
|
|
scsp.timacnt = (d & 0xFF) << 8;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x1A: // TBCTL/TIMB
|
|
|
|
scsp.timbsd = (d >> 8) & 7;
|
|
|
|
scsp.timbcnt = (d & 0xFF) << 8;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x1C: // TCCTL/TIMC
|
|
|
|
scsp.timcsd = (d >> 8) & 7;
|
|
|
|
scsp.timccnt = (d & 0xFF) << 8;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x1E: // SCIEB
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
scsp.scieb = d;
|
|
|
|
for (i = 0; i < 11; i++)
|
|
|
|
{
|
|
|
|
if (scsp.scieb & (1 << i) && scsp.scipd & (1 << i))
|
|
|
|
scsp_trigger_sound_interrupt ((1 << i));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
case 0x20: // SCIPD
|
|
|
|
if (d & 0x20) scsp_sound_interrupt (0x20);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x22: // SCIRE
|
|
|
|
scsp.scipd &= ~d;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x24: // SCILV0
|
|
|
|
scsp.scilv0 = d;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x26: // SCILV1
|
|
|
|
scsp.scilv1 = d;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x28: // SCILV2
|
|
|
|
scsp.scilv2 = d;
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x2A: // MCIEB
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
scsp.mcieb = d;
|
|
|
|
for (i = 0; i < 11; i++)
|
|
|
|
{
|
|
|
|
if (scsp.mcieb & (1 << i) && scsp.mcipd & (1 << i))
|
|
|
|
scsp_trigger_main_interrupt ((1 << i));
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 0x2C: // MCIPD
|
|
|
|
if (d & 0x20) scsp_main_interrupt (0x20);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case 0x2E: // MCIRE
|
|
|
|
scsp.mcipd &= ~d;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8
|
|
|
|
scsp_get_b (u32 a)
|
|
|
|
{
|
|
|
|
a &= 0x3F;
|
|
|
|
|
|
|
|
if ((a != 0x09) && (a != 0x21))
|
|
|
|
{
|
|
|
|
SCSPLOG("r_b scsp : reg %.2X\n", a);
|
|
|
|
}
|
|
|
|
// if (a == 0x09) SCSPLOG("r_b scsp 09 = %.2X\n", ((scsp.slot[scsp.mslc].fcnt >> (SCSP_FREQ_LB + 12)) & 0x1) << 7);
|
|
|
|
|
|
|
|
switch (a)
|
|
|
|
{
|
|
|
|
case 0x01: // VER/MVOL
|
|
|
|
scsp_ccr[a ^ 3] &= 0x0F;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x04: // Midi flags register
|
|
|
|
return scsp.midflag;
|
|
|
|
|
|
|
|
case 0x05: // MIBUF
|
|
|
|
return scsp_midi_in_read();
|
|
|
|
|
|
|
|
case 0x07: // MOBUF
|
|
|
|
return scsp_midi_out_read();
|
|
|
|
|
|
|
|
case 0x08: // CA(highest 3 bits)
|
|
|
|
return (scsp.ca >> 8);
|
|
|
|
|
|
|
|
case 0x09: // CA(lowest bit)/SGC/EG
|
|
|
|
return (scsp.ca & 0xE0) | (scsp.sgc << 5) | scsp.eg;
|
|
|
|
|
|
|
|
case 0x1E: // SCIEB(high byte)
|
|
|
|
return (scsp.scieb >> 8);
|
|
|
|
|
|
|
|
case 0x1F: // SCIEB(low byte)
|
|
|
|
return scsp.scieb;
|
|
|
|
|
|
|
|
case 0x20: // SCIPD(high byte)
|
|
|
|
return (scsp.scipd >> 8);
|
|
|
|
|
|
|
|
case 0x21: // SCIPD(low byte)
|
|
|
|
return scsp.scipd;
|
|
|
|
|
|
|
|
case 0x2C: // MCIPD(high byte)
|
|
|
|
return (scsp.mcipd >> 8);
|
|
|
|
|
|
|
|
case 0x2D: // MCIPD(low byte)
|
|
|
|
return scsp.mcipd;
|
|
|
|
}
|
|
|
|
|
|
|
|
return scsp_ccr[a ^ 3];
|
|
|
|
}
|
|
|
|
|
|
|
|
static u16
|
|
|
|
scsp_get_w (u32 a)
|
|
|
|
{
|
|
|
|
a &= 0x3E;
|
|
|
|
|
|
|
|
if ((a != 0x20) && (a != 0x08))
|
|
|
|
{
|
|
|
|
SCSPLOG("r_w scsp : reg %.2X\n", a * 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (a)
|
|
|
|
{
|
|
|
|
case 0x00: // MEM4MB/DAC18B/VER/MVOL
|
|
|
|
*(u16 *)&scsp_ccr[a ^ 2] &= 0xFF0F;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 0x04: // Midi flags/MIBUF
|
|
|
|
return (scsp.midflag << 8) | scsp_midi_in_read();
|
|
|
|
|
|
|
|
case 0x06: // MOBUF
|
|
|
|
return scsp_midi_out_read();
|
|
|
|
|
|
|
|
case 0x08: // CA/SGC/EG
|
|
|
|
return (scsp.ca & 0x780) | (scsp.sgc << 5) | scsp.eg;
|
|
|
|
|
|
|
|
case 0x18: // TACTL
|
|
|
|
return (scsp.timasd << 8);
|
|
|
|
|
|
|
|
case 0x1A: // TBCTL
|
|
|
|
return (scsp.timbsd << 8);
|
|
|
|
|
|
|
|
case 0x1C: // TCCTL
|
|
|
|
return (scsp.timcsd << 8);
|
|
|
|
|
|
|
|
case 0x1E: // SCIEB
|
|
|
|
return scsp.scieb;
|
|
|
|
|
|
|
|
case 0x20: // SCIPD
|
|
|
|
return scsp.scipd;
|
|
|
|
|
|
|
|
case 0x2C: // MCIPD
|
|
|
|
return scsp.mcipd;
|
|
|
|
}
|
|
|
|
|
|
|
|
return *(u16 *)&scsp_ccr[a ^ 2];
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Synth Slot
|
|
|
|
//
|
|
|
|
// SCSPLOG("outL=%.8X bufL=%.8X disll=%d\n", outL, scsp_bufL[scsp_buf_pos], slot->disll);
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
#define SCSP_GET_OUT_8B \
|
|
|
|
out = (s32) slot->buf8[(slot->fcnt >> SCSP_FREQ_LB)];
|
|
|
|
#else
|
|
|
|
#define SCSP_GET_OUT_8B \
|
|
|
|
out = (s32) slot->buf8[(slot->fcnt >> SCSP_FREQ_LB) ^ 1];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define SCSP_GET_OUT_16B \
|
|
|
|
out = (s32) slot->buf16[slot->fcnt >> SCSP_FREQ_LB];
|
|
|
|
|
|
|
|
#define SCSP_GET_ENV \
|
|
|
|
slot->env = scsp_env_table[slot->ecnt >> SCSP_ENV_LB] * slot->tl / 1024;
|
|
|
|
|
|
|
|
#define SCSP_GET_ENV_LFO \
|
|
|
|
slot->env = (scsp_env_table[slot->ecnt >> SCSP_ENV_LB] * slot->tl / 1024) - \
|
|
|
|
(slot->lfoemw[(slot->lfocnt >> SCSP_LFO_LB) & SCSP_LFO_MASK] >> \
|
|
|
|
slot->lfoems);
|
|
|
|
|
|
|
|
#define SCSP_OUT_8B_L \
|
|
|
|
if ((out) && (slot->env > 0)) \
|
|
|
|
{ \
|
|
|
|
out *= slot->env; \
|
|
|
|
scsp_bufL[scsp_buf_pos] += out >> (slot->disll - 8); \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCSP_OUT_8B_R \
|
|
|
|
if ((out) && (slot->env > 0)) \
|
|
|
|
{ \
|
|
|
|
out *= slot->env; \
|
|
|
|
scsp_bufR[scsp_buf_pos] += out >> (slot->dislr - 8); \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCSP_OUT_8B_LR \
|
|
|
|
if ((out) && (slot->env > 0)) \
|
|
|
|
{ \
|
|
|
|
out *= slot->env; \
|
|
|
|
scsp_bufL[scsp_buf_pos] += out >> (slot->disll - 8); \
|
|
|
|
scsp_bufR[scsp_buf_pos] += out >> (slot->dislr - 8); \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCSP_OUT_16B_L \
|
|
|
|
if ((out) && (slot->env > 0)) \
|
|
|
|
{ \
|
|
|
|
out *= slot->env; \
|
|
|
|
scsp_bufL[scsp_buf_pos] += out >> slot->disll; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCSP_OUT_16B_R \
|
|
|
|
if ((out) && (slot->env > 0)) \
|
|
|
|
{ \
|
|
|
|
out *= slot->env; \
|
|
|
|
scsp_bufR[scsp_buf_pos] += out >> slot->dislr; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCSP_OUT_16B_LR \
|
|
|
|
if ((out) && (slot->env > 0)) \
|
|
|
|
{ \
|
|
|
|
out *= slot->env; \
|
|
|
|
scsp_bufL[scsp_buf_pos] += out >> slot->disll; \
|
|
|
|
scsp_bufR[scsp_buf_pos] += out >> slot->dislr; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCSP_UPDATE_PHASE \
|
|
|
|
if ((slot->fcnt += slot->finc) > slot->lea) \
|
|
|
|
{ \
|
|
|
|
if (slot->lpctl) \
|
|
|
|
{ \
|
|
|
|
slot->fcnt = slot->lsa; \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
slot->ecnt = SCSP_ENV_DE; \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCSP_UPDATE_PHASE_LFO \
|
|
|
|
slot->fcnt += \
|
|
|
|
((slot->lfofmw[(slot->lfocnt >> SCSP_LFO_LB) & SCSP_LFO_MASK] << \
|
|
|
|
(slot->lfofms-7)) >> (slot->fsft+1)); \
|
|
|
|
if ((slot->fcnt += slot->finc) > slot->lea) \
|
|
|
|
{ \
|
|
|
|
if (slot->lpctl) \
|
|
|
|
{ \
|
|
|
|
slot->fcnt = slot->lsa; \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
slot->ecnt = SCSP_ENV_DE; \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCSP_UPDATE_ENV \
|
|
|
|
if (slot->einc) slot->ecnt += *slot->einc; \
|
|
|
|
if (slot->ecnt >= slot->ecmp) \
|
|
|
|
{ \
|
|
|
|
slot->enxt(slot); \
|
|
|
|
if (slot->ecnt >= SCSP_ENV_DE) return; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCSP_UPDATE_LFO \
|
|
|
|
slot->lfocnt += slot->lfoinc;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_keyon (slot_t *slot)
|
|
|
|
{
|
|
|
|
// set buffer, loop start/end address of the slot
|
|
|
|
if (slot->pcm8b)
|
|
|
|
{
|
|
|
|
slot->buf8 = (s8*)&(scsp.scsp_ram[slot->sa]);
|
|
|
|
|
|
|
|
if ((slot->sa + (slot->lea >> SCSP_FREQ_LB)) > SCSP_RAM_MASK)
|
|
|
|
slot->lea = (SCSP_RAM_MASK - slot->sa) << SCSP_FREQ_LB;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slot->buf16 = (s16*)&(scsp.scsp_ram[slot->sa & ~1]);
|
|
|
|
|
|
|
|
if ((slot->sa + (slot->lea >> (SCSP_FREQ_LB - 1))) > SCSP_RAM_MASK)
|
|
|
|
slot->lea = (SCSP_RAM_MASK - slot->sa) << (SCSP_FREQ_LB - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_null (slot_t *slot)
|
|
|
|
{
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Normal 8 bits
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_8B_L (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
// env = [0..0x3FF] - slot->tl
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
// don't waste time if no sound...
|
|
|
|
SCSP_OUT_8B_L
|
|
|
|
|
|
|
|
// calculate new frequency (phase) counter and enveloppe counter
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_8B_R (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_8B_R
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_8B_LR(slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_8B_LR
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Envelope LFO modulation 8 bits
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_E_8B_L (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_8B_L
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_E_8B_R (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_8B_R
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_E_8B_LR (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_8B_LR
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Frequency LFO modulation 8 bits
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_8B_L (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_8B_L
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_8B_R (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_8B_R
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_8B_LR (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_8B_LR
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Enveloppe & Frequency LFO modulation 8 bits
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_E_8B_L (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_8B_L
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_E_8B_R (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_8B_R
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void scsp_slot_update_F_E_8B_LR(slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_8B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_8B_LR
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Normal 16 bits
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_16B_L (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_16B_L
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_16B_R (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_16B_R
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_16B_LR (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_16B_LR
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Envelope LFO modulation 16 bits
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_E_16B_L (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_16B_L
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_E_16B_R (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_16B_R
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_E_16B_LR (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_16B_LR
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Frequency LFO modulation 16 bits
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_16B_L (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_16B_L
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_16B_R (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_16B_R
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_16B_LR (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV
|
|
|
|
|
|
|
|
SCSP_OUT_16B_LR
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Envelope & Frequency LFO modulation 16 bits
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_E_16B_L (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_16B_L
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_E_16B_R (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_16B_R
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
scsp_slot_update_F_E_16B_LR (slot_t *slot)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
SCSP_GET_OUT_16B
|
|
|
|
SCSP_GET_ENV_LFO
|
|
|
|
|
|
|
|
SCSP_OUT_16B_LR
|
|
|
|
|
|
|
|
SCSP_UPDATE_PHASE_LFO
|
|
|
|
SCSP_UPDATE_ENV
|
|
|
|
SCSP_UPDATE_LFO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Update functions
|
|
|
|
|
|
|
|
static void (*scsp_slot_update_p[2][2][2][2][2])(slot_t *slot) =
|
|
|
|
{
|
|
|
|
// NO FMS
|
|
|
|
{ // NO EMS
|
|
|
|
{ // 8 BITS
|
|
|
|
{ // NO LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_null,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_8B_R
|
|
|
|
},
|
|
|
|
// LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_8B_L,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_8B_LR
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 16 BITS
|
|
|
|
{ // NO LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_null,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_16B_R
|
|
|
|
},
|
|
|
|
// LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_16B_L,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_16B_LR
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// EMS
|
|
|
|
{ // 8 BITS
|
|
|
|
{ // NO LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_null,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_E_8B_R
|
|
|
|
},
|
|
|
|
// LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_E_8B_L,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_E_8B_LR
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 16 BITS
|
|
|
|
{ // NO LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_null,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_E_16B_R
|
|
|
|
},
|
|
|
|
// LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_E_16B_L,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_E_16B_LR
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// FMS
|
|
|
|
{ // NO EMS
|
|
|
|
{ // 8 BITS
|
|
|
|
{ // NO LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_null,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_F_8B_R
|
|
|
|
},
|
|
|
|
// LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_F_8B_L,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_F_8B_LR
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 16 BITS
|
|
|
|
{ // NO LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_null,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_F_16B_R
|
|
|
|
},
|
|
|
|
// LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_F_16B_L,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_F_16B_LR
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// EMS
|
|
|
|
{ // 8 BITS
|
|
|
|
{ // NO LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_null,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_F_E_8B_R
|
|
|
|
},
|
|
|
|
// LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_F_E_8B_L,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_F_E_8B_LR
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// 16 BITS
|
|
|
|
{ // NO LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_null,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_F_E_16B_R
|
|
|
|
},
|
|
|
|
// LEFT
|
|
|
|
{ // NO RIGHT
|
|
|
|
scsp_slot_update_F_E_16B_L,
|
|
|
|
// RIGHT
|
|
|
|
scsp_slot_update_F_E_16B_LR
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
scsp_update (s32 *bufL, s32 *bufR, u32 len)
|
|
|
|
{
|
|
|
|
slot_t *slot;
|
|
|
|
|
|
|
|
scsp_bufL = bufL;
|
|
|
|
scsp_bufR = bufR;
|
|
|
|
|
|
|
|
for (slot = &(scsp.slot[0]); slot < &(scsp.slot[32]); slot++)
|
|
|
|
{
|
|
|
|
if (slot->ecnt >= SCSP_ENV_DE) continue; // enveloppe null...
|
|
|
|
|
|
|
|
if (slot->ssctl)
|
|
|
|
{
|
|
|
|
// Still not correct, but at least this fixes games
|
|
|
|
// that rely on Call Address information
|
|
|
|
scsp_buf_len = len;
|
|
|
|
scsp_buf_pos = 0;
|
|
|
|
|
|
|
|
for(; scsp_buf_pos < scsp_buf_len; scsp_buf_pos++)
|
|
|
|
{
|
|
|
|
if ((slot->fcnt += slot->finc) > slot->lea)
|
|
|
|
{
|
|
|
|
if (slot->lpctl) slot->fcnt = slot->lsa;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
slot->ecnt = SCSP_ENV_DE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
continue; // not yet supported!
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp_buf_len = len;
|
|
|
|
scsp_buf_pos = 0;
|
|
|
|
|
|
|
|
// take effect sound volume if no direct sound volume...
|
|
|
|
if ((slot->disll == 31) && (slot->dislr == 31))
|
|
|
|
{
|
|
|
|
slot->disll = slot->efsll;
|
|
|
|
slot->dislr = slot->efslr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// SCSPLOG("update : VL=%d VR=%d CNT=%.8X STEP=%.8X\n", slot->disll, slot->dislr, slot->fcnt, slot->finc);
|
|
|
|
|
|
|
|
scsp_slot_update_p[(slot->lfofms == 31) ? 0 : 1]
|
|
|
|
[(slot->lfoems == 31) ? 0 : 1]
|
|
|
|
[(slot->pcm8b == 0) ? 1 : 0]
|
|
|
|
[(slot->disll == 31) ? 0 : 1]
|
|
|
|
[(slot->dislr == 31) ? 0 : 1](slot);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cddaoutleft > 0)
|
|
|
|
{
|
|
|
|
if (len > cddaoutleft / 4)
|
|
|
|
scsp_buf_len = cddaoutleft / 4;
|
|
|
|
else
|
|
|
|
scsp_buf_len = len;
|
|
|
|
|
|
|
|
scsp_buf_pos = 0;
|
|
|
|
|
|
|
|
/* May need to wrap around the buffer, so use nested loops */
|
|
|
|
while (scsp_buf_pos < scsp_buf_len)
|
|
|
|
{
|
|
|
|
s32 temp = cddanextin*2352 - cddaoutleft;
|
|
|
|
s32 outpos = (temp < 0) ? temp + sizeof(cddabuf.data) : temp;
|
|
|
|
u8 *buf = &cddabuf.data[outpos];
|
|
|
|
|
|
|
|
u32 scsp_buf_target;
|
|
|
|
u32 this_len = scsp_buf_len - scsp_buf_pos;
|
|
|
|
if (this_len > (sizeof(cddabuf.data) - outpos) / 4)
|
|
|
|
this_len = (sizeof(cddabuf.data) - outpos) / 4;
|
|
|
|
scsp_buf_target = scsp_buf_pos + this_len;
|
|
|
|
|
|
|
|
for (; scsp_buf_pos < scsp_buf_target; scsp_buf_pos++, buf += 4)
|
|
|
|
{
|
|
|
|
s32 out;
|
|
|
|
|
|
|
|
out = (s32)(s16)((buf[1] << 8) | buf[0]);
|
|
|
|
|
|
|
|
if (out)
|
|
|
|
scsp_bufL[scsp_buf_pos] += out;
|
|
|
|
|
|
|
|
out = (s32)(s16)((buf[3] << 8) | buf[2]);
|
|
|
|
|
|
|
|
if (out)
|
|
|
|
scsp_bufR[scsp_buf_pos] += out;
|
|
|
|
}
|
|
|
|
|
|
|
|
cddaoutleft -= this_len * 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
scsp_update_monitor(void)
|
|
|
|
{
|
|
|
|
slot_t *slot = &scsp.slot[scsp.mslc];
|
|
|
|
scsp.ca = ((slot->fcnt >> (SCSP_FREQ_LB + 12)) & 0xF) << 7;
|
|
|
|
scsp.sgc = slot->ecurp;
|
|
|
|
scsp.eg = 0x1f - (slot->env >> (SCSP_ENV_HB - 5));
|
|
|
|
#ifdef PSP
|
|
|
|
WRITE_THROUGH(scsp.ca);
|
|
|
|
WRITE_THROUGH(scsp.sgc);
|
|
|
|
WRITE_THROUGH(scsp.eg);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
scsp_update_timer (u32 len)
|
|
|
|
{
|
|
|
|
scsp.timacnt += len << (8 - scsp.timasd);
|
|
|
|
|
|
|
|
if (scsp.timacnt >= 0xFF00)
|
|
|
|
{
|
|
|
|
if (!(scsp.scipd & 0x40))
|
|
|
|
scsp_sound_interrupt(0x40);
|
|
|
|
if (!(scsp.mcipd & 0x40))
|
|
|
|
scsp_main_interrupt(0x40);
|
|
|
|
scsp.timacnt -= 0xFF00;
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp.timbcnt += len << (8 - scsp.timbsd);
|
|
|
|
|
|
|
|
if (scsp.timbcnt >= 0xFF00)
|
|
|
|
{
|
|
|
|
if (!(scsp.scipd & 0x80))
|
|
|
|
scsp_sound_interrupt(0x80);
|
|
|
|
if (!(scsp.mcipd & 0x80))
|
|
|
|
scsp_main_interrupt(0x80);
|
|
|
|
scsp.timbcnt -= 0xFF00;
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp.timccnt += len << (8 - scsp.timcsd);
|
|
|
|
|
|
|
|
if (scsp.timccnt >= 0xFF00)
|
|
|
|
{
|
|
|
|
if (!(scsp.scipd & 0x100))
|
|
|
|
scsp_sound_interrupt(0x100);
|
|
|
|
if (!(scsp.mcipd & 0x100))
|
|
|
|
scsp_main_interrupt(0x100);
|
|
|
|
scsp.timccnt -= 0xFF00;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 1F interrupt can't be accurate here...
|
|
|
|
if (len)
|
|
|
|
{
|
|
|
|
if (!(scsp.scipd & 0x400))
|
|
|
|
scsp_sound_interrupt(0x400);
|
|
|
|
if (!(scsp.mcipd & 0x400))
|
|
|
|
scsp_main_interrupt(0x400);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// MIDI
|
|
|
|
|
|
|
|
void
|
|
|
|
scsp_midi_in_send (u8 data)
|
|
|
|
{
|
|
|
|
if (scsp.midflag & SCSP_MIDI_IN_EMP)
|
|
|
|
{
|
|
|
|
scsp_sound_interrupt(0x8);
|
|
|
|
scsp_main_interrupt(0x8);
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp.midflag &= ~SCSP_MIDI_IN_EMP;
|
|
|
|
|
|
|
|
if (scsp.midincnt > 3)
|
|
|
|
{
|
|
|
|
scsp.midflag |= SCSP_MIDI_IN_OVF;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp.midinbuf[scsp.midincnt++] = data;
|
|
|
|
|
|
|
|
if (scsp.midincnt > 3) scsp.midflag |= SCSP_MIDI_IN_FUL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
scsp_midi_out_send (u8 data)
|
|
|
|
{
|
|
|
|
scsp.midflag &= ~SCSP_MIDI_OUT_EMP;
|
|
|
|
|
|
|
|
if (scsp.midoutcnt > 3) return;
|
|
|
|
|
|
|
|
scsp.midoutbuf[scsp.midoutcnt++] = data;
|
|
|
|
|
|
|
|
if (scsp.midoutcnt > 3) scsp.midflag |= SCSP_MIDI_OUT_FUL;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8
|
|
|
|
scsp_midi_in_read (void)
|
|
|
|
{
|
|
|
|
u8 data;
|
|
|
|
|
|
|
|
scsp.midflag &= ~(SCSP_MIDI_IN_OVF | SCSP_MIDI_IN_FUL);
|
|
|
|
|
|
|
|
if (scsp.midincnt > 0)
|
|
|
|
{
|
|
|
|
if (scsp.midincnt > 1)
|
|
|
|
{
|
|
|
|
scsp_sound_interrupt(0x8);
|
|
|
|
scsp_main_interrupt(0x8);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
scsp.midflag |= SCSP_MIDI_IN_EMP;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = scsp.midinbuf[0];
|
|
|
|
|
|
|
|
switch ((--scsp.midincnt) & 3)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
scsp.midinbuf[0] = scsp.midinbuf[1];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
scsp.midinbuf[0] = scsp.midinbuf[1];
|
|
|
|
scsp.midinbuf[1] = scsp.midinbuf[2];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
scsp.midinbuf[0] = scsp.midinbuf[1];
|
|
|
|
scsp.midinbuf[1] = scsp.midinbuf[2];
|
|
|
|
scsp.midinbuf[2] = scsp.midinbuf[3];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8
|
|
|
|
scsp_midi_out_read (void)
|
|
|
|
{
|
|
|
|
u8 data;
|
|
|
|
|
|
|
|
scsp.midflag &= ~SCSP_MIDI_OUT_FUL;
|
|
|
|
|
|
|
|
if (scsp.midoutcnt > 0)
|
|
|
|
{
|
|
|
|
if (scsp.midoutcnt == 1)
|
|
|
|
{
|
|
|
|
scsp.midflag |= SCSP_MIDI_OUT_EMP;
|
|
|
|
scsp_sound_interrupt(0x200);
|
|
|
|
scsp_main_interrupt(0x200);
|
|
|
|
}
|
|
|
|
|
|
|
|
data = scsp.midoutbuf[0];
|
|
|
|
|
|
|
|
switch (--scsp.midoutcnt & 3)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
scsp.midoutbuf[0] = scsp.midoutbuf[1];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
scsp.midoutbuf[0] = scsp.midoutbuf[1];
|
|
|
|
scsp.midoutbuf[1] = scsp.midoutbuf[2];
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
scsp.midoutbuf[0] = scsp.midoutbuf[1];
|
|
|
|
scsp.midoutbuf[1] = scsp.midoutbuf[2];
|
|
|
|
scsp.midoutbuf[2] = scsp.midoutbuf[3];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Access
|
|
|
|
|
|
|
|
void FASTCALL
|
|
|
|
scsp_w_b (u32 a, u8 d)
|
|
|
|
{
|
|
|
|
a &= 0xFFF;
|
|
|
|
|
|
|
|
if (a < 0x400)
|
|
|
|
{
|
|
|
|
scsp_slot_set_b (a >> 5, a, d);
|
|
|
|
FLUSH_SCSP ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (a < 0x600)
|
|
|
|
{
|
|
|
|
if (a < 0x440)
|
|
|
|
{
|
|
|
|
scsp_set_b (a, d);
|
|
|
|
FLUSH_SCSP ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (a < 0x700)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (a < 0xee4)
|
|
|
|
{
|
|
|
|
a &= 0x3ff;
|
|
|
|
scsp_dcr[a ^ 3] = d;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SCSPLOG("WARNING: scsp w_b to %08lx w/ %02x\n", a, d);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void FASTCALL
|
|
|
|
scsp_w_w (u32 a, u16 d)
|
|
|
|
{
|
|
|
|
if (a & 1)
|
|
|
|
{
|
|
|
|
SCSPLOG ("ERROR: scsp w_w misaligned : %.8X\n", a);
|
|
|
|
}
|
|
|
|
|
|
|
|
a &= 0xFFE;
|
|
|
|
|
|
|
|
if (a < 0x400)
|
|
|
|
{
|
|
|
|
scsp_slot_set_w (a >> 5, a, d);
|
|
|
|
FLUSH_SCSP ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (a < 0x600)
|
|
|
|
{
|
|
|
|
if (a < 0x440)
|
|
|
|
{
|
|
|
|
scsp_set_w (a, d);
|
|
|
|
FLUSH_SCSP ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (a < 0x700)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (a < 0xee4)
|
|
|
|
{
|
|
|
|
a &= 0x3ff;
|
|
|
|
*(u16 *)&scsp_dcr[a ^ 2] = d;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SCSPLOG ("WARNING: scsp w_w to %08lx w/ %04x\n", a, d);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void FASTCALL
|
|
|
|
scsp_w_d (u32 a, u32 d)
|
|
|
|
{
|
|
|
|
if (a & 3)
|
|
|
|
{
|
|
|
|
SCSPLOG ("ERROR: scsp w_d misaligned : %.8X\n", a);
|
|
|
|
}
|
|
|
|
|
|
|
|
a &= 0xFFC;
|
|
|
|
|
|
|
|
if (a < 0x400)
|
|
|
|
{
|
|
|
|
scsp_slot_set_w (a >> 5, a + 0, d >> 16);
|
|
|
|
scsp_slot_set_w (a >> 5, a + 2, d & 0xFFFF);
|
|
|
|
FLUSH_SCSP ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (a < 0x600)
|
|
|
|
{
|
|
|
|
if (a < 0x440)
|
|
|
|
{
|
|
|
|
scsp_set_w (a + 0, d >> 16);
|
|
|
|
scsp_set_w (a + 2, d & 0xFFFF);
|
|
|
|
FLUSH_SCSP ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (a < 0x700)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (a < 0xee4)
|
|
|
|
{
|
|
|
|
a &= 0x3ff;
|
|
|
|
*(u32 *)&scsp_dcr[a] = d;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SCSPLOG ("WARNING: scsp w_d to %08lx w/ %08lx\n", a, d);
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
u8 FASTCALL
|
|
|
|
scsp_r_b (u32 a)
|
|
|
|
{
|
|
|
|
a &= 0xFFF;
|
|
|
|
|
|
|
|
if (a < 0x400)
|
|
|
|
{
|
|
|
|
return scsp_slot_get_b(a >> 5, a);
|
|
|
|
}
|
|
|
|
else if (a < 0x600)
|
|
|
|
{
|
|
|
|
if (a < 0x440) return scsp_get_b(a);
|
|
|
|
}
|
|
|
|
else if (a < 0x700)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (a < 0xee4)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
SCSPLOG("WARNING: scsp r_b to %08lx\n", a);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
u16 FASTCALL
|
|
|
|
scsp_r_w (u32 a)
|
|
|
|
{
|
|
|
|
if (a & 1)
|
|
|
|
{
|
|
|
|
SCSPLOG ("ERROR: scsp r_w misaligned : %.8X\n", a);
|
|
|
|
}
|
|
|
|
|
|
|
|
a &= 0xFFE;
|
|
|
|
|
|
|
|
if (a < 0x400)
|
|
|
|
{
|
|
|
|
return scsp_slot_get_w (a >> 5, a);
|
|
|
|
}
|
|
|
|
else if (a < 0x600)
|
|
|
|
{
|
|
|
|
if (a < 0x440) return scsp_get_w (a);
|
|
|
|
}
|
|
|
|
else if (a < 0x700)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (a < 0xee4)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
SCSPLOG ("WARNING: scsp r_w to %08lx\n", a);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
u32 FASTCALL
|
|
|
|
scsp_r_d (u32 a)
|
|
|
|
{
|
|
|
|
if (a & 3)
|
|
|
|
{
|
|
|
|
SCSPLOG ("ERROR: scsp r_d misaligned : %.8X\n", a);
|
|
|
|
}
|
|
|
|
|
|
|
|
a &= 0xFFC;
|
|
|
|
|
|
|
|
if (a < 0x400)
|
|
|
|
{
|
|
|
|
return (scsp_slot_get_w (a >> 5, a + 0) << 16) | scsp_slot_get_w (a >> 5, a + 2);
|
|
|
|
}
|
|
|
|
else if (a < 0x600)
|
|
|
|
{
|
|
|
|
if (a < 0x440) return (scsp_get_w (a + 0) << 16) | scsp_get_w (a + 2);
|
|
|
|
}
|
|
|
|
else if (a < 0x700)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (a < 0xee4)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
SCSPLOG("WARNING: scsp r_d to %08lx\n", a);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
// Interface
|
|
|
|
|
|
|
|
void
|
|
|
|
scsp_shutdown (void)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
scsp_reset (void)
|
|
|
|
{
|
|
|
|
slot_t *slot;
|
|
|
|
|
|
|
|
memset(scsp_reg, 0, 0x1000);
|
|
|
|
|
|
|
|
scsp.mem4b = 0;
|
|
|
|
scsp.mvol = 0;
|
|
|
|
scsp.rbl = 0;
|
|
|
|
scsp.rbp = 0;
|
|
|
|
scsp.mslc = 0;
|
|
|
|
scsp.ca = 0;
|
|
|
|
|
|
|
|
scsp.dmea = 0;
|
|
|
|
scsp.drga = 0;
|
|
|
|
scsp.dmfl = 0;
|
|
|
|
scsp.dmlen = 0;
|
|
|
|
|
|
|
|
scsp.midincnt = 0;
|
|
|
|
scsp.midoutcnt = 0;
|
|
|
|
scsp.midflag = SCSP_MIDI_IN_EMP | SCSP_MIDI_OUT_EMP;
|
|
|
|
scsp.midflag2 = 0;
|
|
|
|
|
|
|
|
scsp.timacnt = 0xFF00;
|
|
|
|
scsp.timbcnt = 0xFF00;
|
|
|
|
scsp.timccnt = 0xFF00;
|
|
|
|
scsp.timasd = 0;
|
|
|
|
scsp.timbsd = 0;
|
|
|
|
scsp.timcsd = 0;
|
|
|
|
|
|
|
|
scsp.mcieb = 0;
|
|
|
|
scsp.mcipd = 0;
|
|
|
|
scsp.scieb = 0;
|
|
|
|
scsp.scipd = 0;
|
|
|
|
scsp.scilv0 = 0;
|
|
|
|
scsp.scilv1 = 0;
|
|
|
|
scsp.scilv2 = 0;
|
|
|
|
|
|
|
|
for(slot = &(scsp.slot[0]); slot < &(scsp.slot[32]); slot++)
|
|
|
|
{
|
|
|
|
memset(slot, 0, sizeof(slot_t));
|
|
|
|
slot->ecnt = SCSP_ENV_DE; // slot off
|
|
|
|
slot->ecurp = SCSP_ENV_RELEASE;
|
|
|
|
slot->dislr = slot->disll = 31; // direct level sound off
|
|
|
|
slot->efslr = slot->efsll = 31; // effect level sound off
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
scsp_init (u8 *scsp_ram, void (*sint_hand)(u32), void (*mint_hand)(void))
|
|
|
|
{
|
|
|
|
u32 i, j;
|
|
|
|
double x;
|
|
|
|
|
|
|
|
scsp_shutdown ();
|
|
|
|
|
2013-05-11 18:52:19 +00:00
|
|
|
memset(&scsp, 0, sizeof(scsp));
|
|
|
|
|
2013-04-30 21:20:11 +00:00
|
|
|
scsp_isr = &scsp_reg[0x0000];
|
|
|
|
scsp_ccr = &scsp_reg[0x0400];
|
|
|
|
scsp_dcr = &scsp_reg[0x0700];
|
|
|
|
|
|
|
|
scsp.scsp_ram = scsp_ram;
|
|
|
|
scsp.sintf = sint_hand;
|
|
|
|
scsp.mintf = mint_hand;
|
|
|
|
|
2013-05-03 22:12:35 +00:00
|
|
|
scsp.slotstart = 0x4b435546;
|
|
|
|
|
2013-04-30 21:20:11 +00:00
|
|
|
for (i = 0; i < SCSP_ENV_LEN; i++)
|
|
|
|
{
|
|
|
|
// Attack Curve (x^7 ?)
|
|
|
|
x = pow (((double)(SCSP_ENV_MASK - i) / (double)SCSP_ENV_LEN), 7);
|
|
|
|
x *= (double)SCSP_ENV_LEN;
|
|
|
|
scsp_env_table[i] = SCSP_ENV_MASK - (s32)x;
|
|
|
|
|
|
|
|
// Decay curve (x = linear)
|
|
|
|
x = pow (((double)i / (double)SCSP_ENV_LEN), 1);
|
|
|
|
x *= (double)SCSP_ENV_LEN;
|
|
|
|
scsp_env_table[i + SCSP_ENV_LEN] = SCSP_ENV_MASK - (s32)x;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < 32; i++)
|
|
|
|
{
|
|
|
|
j += 1 << (i >> 2);
|
|
|
|
|
|
|
|
// lfo freq
|
|
|
|
x = (SCSP_FREQ / 256.0) / (double)j;
|
|
|
|
|
|
|
|
// converting lfo freq in lfo step
|
|
|
|
scsp_lfo_step[31 - i] = scsp_round(x * ((double)SCSP_LFO_LEN /
|
|
|
|
(double)SCSP_FREQ) *
|
|
|
|
(double)(1 << SCSP_LFO_LB));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate LFO (modulation) values
|
|
|
|
for (i = 0; i < SCSP_LFO_LEN; i++)
|
|
|
|
{
|
|
|
|
// Envelope modulation
|
|
|
|
scsp_lfo_sawt_e[i] = SCSP_LFO_MASK - i;
|
|
|
|
|
|
|
|
if (i < (SCSP_LFO_LEN / 2))
|
|
|
|
scsp_lfo_squa_e[i] = SCSP_LFO_MASK;
|
|
|
|
else
|
|
|
|
scsp_lfo_squa_e[i] = 0;
|
|
|
|
|
|
|
|
if (i < (SCSP_LFO_LEN / 2))
|
|
|
|
scsp_lfo_tri_e[i] = SCSP_LFO_MASK - (i * 2);
|
|
|
|
else
|
|
|
|
scsp_lfo_tri_e[i] = (i - (SCSP_LFO_LEN / 2)) * 2;
|
|
|
|
|
|
|
|
scsp_lfo_noi_e[i] = rand() & SCSP_LFO_MASK;
|
|
|
|
|
|
|
|
// Frequency modulation
|
|
|
|
scsp_lfo_sawt_f[(i + 512) & SCSP_LFO_MASK] = i - (SCSP_LFO_LEN / 2);
|
|
|
|
|
|
|
|
if (i < (SCSP_LFO_LEN / 2))
|
|
|
|
scsp_lfo_squa_f[i] = SCSP_LFO_MASK - (SCSP_LFO_LEN / 2) - 128;
|
|
|
|
else
|
|
|
|
scsp_lfo_squa_f[i] = 0 - (SCSP_LFO_LEN / 2) + 128;
|
|
|
|
|
|
|
|
if (i < (SCSP_LFO_LEN / 2))
|
|
|
|
scsp_lfo_tri_f[(i + 768) & SCSP_LFO_MASK] = (i * 2) -
|
|
|
|
(SCSP_LFO_LEN / 2);
|
|
|
|
else
|
|
|
|
scsp_lfo_tri_f[(i + 768) & SCSP_LFO_MASK] =
|
|
|
|
(SCSP_LFO_MASK - ((i - (SCSP_LFO_LEN / 2)) * 2)) -
|
|
|
|
(SCSP_LFO_LEN / 2) + 1;
|
|
|
|
|
|
|
|
scsp_lfo_noi_f[i] = scsp_lfo_noi_e[i] - (SCSP_LFO_LEN / 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
scsp_attack_rate[i] = 0;
|
|
|
|
scsp_decay_rate[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < 60; i++)
|
|
|
|
{
|
|
|
|
x = 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75
|
|
|
|
x *= (double)(1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15)
|
|
|
|
x *= (double)(SCSP_ENV_LEN << SCSP_ENV_LB); // adjust for table scsp_env_table
|
|
|
|
|
|
|
|
scsp_attack_rate[i + 4] = scsp_round(x / (double)SCSP_ATTACK_R);
|
|
|
|
scsp_decay_rate[i + 4] = scsp_round(x / (double)SCSP_DECAY_R);
|
|
|
|
|
|
|
|
if (scsp_attack_rate[i + 4] == 0) scsp_attack_rate[i + 4] = 1;
|
|
|
|
if (scsp_decay_rate[i + 4] == 0) scsp_decay_rate[i + 4] = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp_attack_rate[63] = SCSP_ENV_AE;
|
|
|
|
scsp_decay_rate[61] = scsp_decay_rate[60];
|
|
|
|
scsp_decay_rate[62] = scsp_decay_rate[60];
|
|
|
|
scsp_decay_rate[63] = scsp_decay_rate[60];
|
|
|
|
|
|
|
|
for(i = 64; i < 96; i++)
|
|
|
|
{
|
|
|
|
scsp_attack_rate[i] = scsp_attack_rate[63];
|
|
|
|
scsp_decay_rate[i] = scsp_decay_rate[63];
|
|
|
|
scsp_null_rate[i - 64] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < 96; i++)
|
|
|
|
{
|
|
|
|
SCSPLOG ("attack rate[%d] = %.8X -> %.8X\n", i, scsp_attack_rate[i],
|
|
|
|
scsp_attack_rate[i] >> SCSP_ENV_LB);
|
|
|
|
SCSPLOG ("decay rate[%d] = %.8X -> %.8X\n", i, scsp_decay_rate[i],
|
|
|
|
scsp_decay_rate[i] >> SCSP_ENV_LB);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < 256; i++)
|
|
|
|
scsp_tl_table[i] = scsp_round(pow(10, ((double)i * -0.3762) / 20) * 1024.0);
|
|
|
|
|
|
|
|
scsp_reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Yabause specific
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
u8 *SoundRam = NULL;
|
|
|
|
ScspInternal *ScspInternalVars;
|
|
|
|
static SoundInterface_struct *SNDCore = NULL;
|
|
|
|
extern SoundInterface_struct *SNDCoreList[];
|
|
|
|
|
|
|
|
struct sounddata
|
|
|
|
{
|
|
|
|
u32 *data32;
|
|
|
|
} scspchannel[2];
|
|
|
|
|
|
|
|
static u32 scspsoundlen; // Samples to output per frame
|
|
|
|
static u32 scsplines; // Lines per frame
|
|
|
|
static u32 scspsoundbufs; // Number of "scspsoundlen"-sample buffers
|
|
|
|
static u32 scspsoundbufsize; // scspsoundlen * scspsoundbufs
|
|
|
|
static int scspframeaccurate; // True to generate frame-accurate audio
|
|
|
|
static u32 scspsoundgenpos; // Offset of next byte to generate
|
|
|
|
static u32 scspsoundoutleft; // Samples not yet sent to host driver
|
|
|
|
|
|
|
|
static int
|
|
|
|
scsp_alloc_bufs (void)
|
|
|
|
{
|
|
|
|
if (scspchannel[0].data32)
|
|
|
|
free(scspchannel[0].data32);
|
|
|
|
scspchannel[0].data32 = NULL;
|
|
|
|
if (scspchannel[1].data32)
|
|
|
|
free(scspchannel[1].data32);
|
|
|
|
scspchannel[1].data32 = NULL;
|
|
|
|
|
|
|
|
scspchannel[0].data32 = (u32 *)calloc(scspsoundbufsize, sizeof(u32));
|
|
|
|
if (scspchannel[0].data32 == NULL)
|
|
|
|
return -1;
|
|
|
|
scspchannel[1].data32 = (u32 *)calloc(scspsoundbufsize, sizeof(u32));
|
|
|
|
if (scspchannel[1].data32 == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 IsM68KRunning;
|
|
|
|
static s32 FASTCALL (*m68kexecptr)(s32 cycles); // M68K->Exec or M68KExecBP
|
|
|
|
static s32 savedcycles; // Cycles left over from the last M68KExec() call
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static u32 FASTCALL
|
|
|
|
c68k_byte_read (const u32 adr)
|
|
|
|
{
|
|
|
|
if (adr < 0x100000)
|
|
|
|
return T2ReadByte(SoundRam, adr & 0x7FFFF);
|
|
|
|
else
|
|
|
|
return scsp_r_b(adr);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static void FASTCALL
|
|
|
|
c68k_byte_write (const u32 adr, u32 data)
|
|
|
|
{
|
|
|
|
if (adr < 0x100000)
|
|
|
|
T2WriteByte(SoundRam, adr & 0x7FFFF, data);
|
|
|
|
else
|
|
|
|
scsp_w_b(adr, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/* exported to m68kd.c */
|
|
|
|
u32 FASTCALL
|
|
|
|
c68k_word_read (const u32 adr)
|
|
|
|
{
|
|
|
|
if (adr < 0x100000)
|
|
|
|
return T2ReadWord(SoundRam, adr & 0x7FFFF);
|
|
|
|
else
|
|
|
|
return scsp_r_w(adr);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static void FASTCALL
|
|
|
|
c68k_word_write (const u32 adr, u32 data)
|
|
|
|
{
|
|
|
|
if (adr < 0x100000)
|
|
|
|
T2WriteWord (SoundRam, adr & 0x7FFFF, data);
|
|
|
|
else
|
|
|
|
scsp_w_w (adr, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static void
|
|
|
|
c68k_interrupt_handler (u32 level)
|
|
|
|
{
|
|
|
|
// send interrupt to 68k
|
|
|
|
M68K->SetIRQ ((s32)level);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static void
|
|
|
|
scu_interrupt_handler (void)
|
|
|
|
{
|
|
|
|
// send interrupt to scu
|
|
|
|
ScuSendSoundRequest ();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
u8 FASTCALL
|
|
|
|
SoundRamReadByte (u32 addr)
|
|
|
|
{
|
|
|
|
addr &= 0xFFFFF;
|
|
|
|
|
|
|
|
// If mem4b is set, mirror ram every 256k
|
|
|
|
if (scsp.mem4b == 0)
|
|
|
|
addr &= 0x3FFFF;
|
|
|
|
else if (addr > 0x7FFFF)
|
|
|
|
return 0xFF;
|
|
|
|
|
|
|
|
return T2ReadByte (SoundRam, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void FASTCALL
|
|
|
|
SoundRamWriteByte (u32 addr, u8 val)
|
|
|
|
{
|
|
|
|
addr &= 0xFFFFF;
|
|
|
|
|
|
|
|
// If mem4b is set, mirror ram every 256k
|
|
|
|
if (scsp.mem4b == 0)
|
|
|
|
addr &= 0x3FFFF;
|
|
|
|
else if (addr > 0x7FFFF)
|
|
|
|
return;
|
|
|
|
|
|
|
|
T2WriteByte (SoundRam, addr, val);
|
|
|
|
M68K->WriteNotify (addr, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
u16 FASTCALL
|
|
|
|
SoundRamReadWord (u32 addr)
|
|
|
|
{
|
|
|
|
addr &= 0xFFFFF;
|
|
|
|
|
|
|
|
if (scsp.mem4b == 0)
|
|
|
|
addr &= 0x3FFFF;
|
|
|
|
else if (addr > 0x7FFFF)
|
|
|
|
return 0xFFFF;
|
|
|
|
|
|
|
|
return T2ReadWord (SoundRam, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void FASTCALL
|
|
|
|
SoundRamWriteWord (u32 addr, u16 val)
|
|
|
|
{
|
|
|
|
addr &= 0xFFFFF;
|
|
|
|
|
|
|
|
// If mem4b is set, mirror ram every 256k
|
|
|
|
if (scsp.mem4b == 0)
|
|
|
|
addr &= 0x3FFFF;
|
|
|
|
else if (addr > 0x7FFFF)
|
|
|
|
return;
|
|
|
|
|
|
|
|
T2WriteWord (SoundRam, addr, val);
|
|
|
|
M68K->WriteNotify (addr, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
u32 FASTCALL
|
|
|
|
SoundRamReadLong (u32 addr)
|
|
|
|
{
|
|
|
|
addr &= 0xFFFFF;
|
|
|
|
|
|
|
|
// If mem4b is set, mirror ram every 256k
|
|
|
|
if (scsp.mem4b == 0)
|
|
|
|
addr &= 0x3FFFF;
|
|
|
|
else if (addr > 0x7FFFF)
|
|
|
|
return 0xFFFFFFFF;
|
|
|
|
|
|
|
|
return T2ReadLong (SoundRam, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void FASTCALL
|
|
|
|
SoundRamWriteLong (u32 addr, u32 val)
|
|
|
|
{
|
|
|
|
addr &= 0xFFFFF;
|
|
|
|
|
|
|
|
// If mem4b is set, mirror ram every 256k
|
|
|
|
if (scsp.mem4b == 0)
|
|
|
|
addr &= 0x3FFFF;
|
|
|
|
else if (addr > 0x7FFFF)
|
|
|
|
return;
|
|
|
|
|
|
|
|
T2WriteLong (SoundRam, addr, val);
|
|
|
|
M68K->WriteNotify (addr, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int
|
|
|
|
ScspInit (int coreid)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ((SoundRam = T2MemoryInit (0x80000)) == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((ScspInternalVars = (ScspInternal *)calloc(1, sizeof(ScspInternal))) == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (M68K->Init () != 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
M68K->SetReadB ((C68K_READ *)c68k_byte_read);
|
|
|
|
M68K->SetReadW ((C68K_READ *)c68k_word_read);
|
|
|
|
M68K->SetWriteB ((C68K_WRITE *)c68k_byte_write);
|
|
|
|
M68K->SetWriteW ((C68K_WRITE *)c68k_word_write);
|
|
|
|
|
|
|
|
M68K->SetFetch (0x000000, 0x040000, (pointer)SoundRam);
|
|
|
|
M68K->SetFetch (0x040000, 0x080000, (pointer)SoundRam);
|
|
|
|
M68K->SetFetch (0x080000, 0x0C0000, (pointer)SoundRam);
|
|
|
|
M68K->SetFetch (0x0C0000, 0x100000, (pointer)SoundRam);
|
|
|
|
|
|
|
|
IsM68KRunning = 0;
|
|
|
|
|
|
|
|
scsp_init (SoundRam, &c68k_interrupt_handler, &scu_interrupt_handler);
|
|
|
|
ScspInternalVars->scsptiming1 = 0;
|
|
|
|
ScspInternalVars->scsptiming2 = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_BREAKPOINTS; i++)
|
|
|
|
ScspInternalVars->codebreakpoint[i].addr = 0xFFFFFFFF;
|
|
|
|
ScspInternalVars->numcodebreakpoints = 0;
|
|
|
|
ScspInternalVars->BreakpointCallBack = NULL;
|
|
|
|
ScspInternalVars->inbreakpoint = 0;
|
|
|
|
|
|
|
|
m68kexecptr = M68K->Exec;
|
|
|
|
|
|
|
|
// Allocate enough memory for each channel buffer(may have to change)
|
|
|
|
scspsoundlen = 44100 / 60; // assume it's NTSC timing
|
|
|
|
scsplines = 213;
|
|
|
|
scspsoundbufs = 10; // should be enough to prevent skipping
|
|
|
|
scspsoundbufsize = scspsoundlen * scspsoundbufs;
|
|
|
|
if (scsp_alloc_bufs () < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// Reset output pointers
|
|
|
|
scspsoundgenpos = 0;
|
|
|
|
scspsoundoutleft = 0;
|
|
|
|
|
|
|
|
return ScspChangeSoundCore (coreid);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int
|
|
|
|
ScspChangeSoundCore (int coreid)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// Make sure the old core is freed
|
|
|
|
if (SNDCore)
|
|
|
|
SNDCore->DeInit();
|
|
|
|
|
|
|
|
// So which core do we want?
|
|
|
|
if (coreid == SNDCORE_DEFAULT)
|
|
|
|
coreid = 0; // Assume we want the first one
|
|
|
|
|
|
|
|
// Go through core list and find the id
|
|
|
|
for (i = 0; SNDCoreList[i] != NULL; i++)
|
|
|
|
{
|
|
|
|
if (SNDCoreList[i]->id == coreid)
|
|
|
|
{
|
|
|
|
// Set to current core
|
|
|
|
SNDCore = SNDCoreList[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SNDCore == NULL)
|
|
|
|
{
|
|
|
|
SNDCore = &SNDDummy;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SNDCore->Init () == -1)
|
|
|
|
{
|
|
|
|
// Since it failed, instead of it being fatal, we'll just use the dummy
|
|
|
|
// core instead
|
|
|
|
|
|
|
|
// This might be helpful though.
|
|
|
|
YabSetError (YAB_ERR_CANNOTINIT, (void *)SNDCore->Name);
|
|
|
|
|
|
|
|
SNDCore = &SNDDummy;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SNDCore)
|
|
|
|
{
|
|
|
|
if (scsp_mute_flags) SNDCore->MuteAudio();
|
|
|
|
else SNDCore->UnMuteAudio();
|
|
|
|
SNDCore->SetVolume(scsp_volume);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspSetFrameAccurate (int on)
|
|
|
|
{
|
|
|
|
scspframeaccurate = (on != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspDeInit (void)
|
|
|
|
{
|
|
|
|
if (scspchannel[0].data32)
|
|
|
|
free(scspchannel[0].data32);
|
|
|
|
scspchannel[0].data32 = NULL;
|
|
|
|
|
|
|
|
if (scspchannel[1].data32)
|
|
|
|
free(scspchannel[1].data32);
|
|
|
|
scspchannel[1].data32 = NULL;
|
|
|
|
|
|
|
|
if (SNDCore)
|
|
|
|
SNDCore->DeInit();
|
|
|
|
SNDCore = NULL;
|
|
|
|
|
|
|
|
scsp_shutdown();
|
|
|
|
|
|
|
|
if (SoundRam)
|
|
|
|
T2MemoryDeInit (SoundRam);
|
|
|
|
SoundRam = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KStart (void)
|
|
|
|
{
|
|
|
|
M68K->Reset ();
|
|
|
|
savedcycles = 0;
|
|
|
|
IsM68KRunning = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KStop (void)
|
|
|
|
{
|
|
|
|
IsM68KRunning = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspReset (void)
|
|
|
|
{
|
|
|
|
scsp_reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int
|
|
|
|
ScspChangeVideoFormat (int type)
|
|
|
|
{
|
|
|
|
scspsoundlen = 44100 / (type ? 50 : 60);
|
|
|
|
scsplines = type ? 313 : 263;
|
|
|
|
scspsoundbufsize = scspsoundlen * scspsoundbufs;
|
|
|
|
|
|
|
|
if (scsp_alloc_bufs () < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
SNDCore->ChangeVideoFormat (type ? 50 : 60);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
/* Process breakpoints in a separate function to avoid unnecessary register
|
|
|
|
* spillage on the fast path (and to avoid too much block nesting) */
|
|
|
|
#ifdef __GNUC__
|
|
|
|
__attribute__((noinline))
|
|
|
|
#endif
|
|
|
|
static s32 FASTCALL M68KExecBP (s32 cycles);
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KExec (s32 cycles)
|
|
|
|
{
|
|
|
|
s32 newcycles = savedcycles - cycles;
|
|
|
|
if (LIKELY(IsM68KRunning))
|
|
|
|
{
|
|
|
|
if (LIKELY(newcycles < 0))
|
|
|
|
{
|
|
|
|
s32 cyclestoexec = -newcycles;
|
|
|
|
newcycles += (*m68kexecptr)(cyclestoexec);
|
|
|
|
}
|
|
|
|
savedcycles = newcycles;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static s32 FASTCALL
|
|
|
|
M68KExecBP (s32 cycles)
|
|
|
|
{
|
|
|
|
s32 cyclestoexec=cycles;
|
|
|
|
s32 cyclesexecuted=0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
while (cyclesexecuted < cyclestoexec)
|
|
|
|
{
|
|
|
|
// Make sure it isn't one of our breakpoints
|
|
|
|
for (i = 0; i < ScspInternalVars->numcodebreakpoints; i++)
|
|
|
|
{
|
|
|
|
if ((M68K->GetPC () == ScspInternalVars->codebreakpoint[i].addr) &&
|
|
|
|
ScspInternalVars->inbreakpoint == 0)
|
|
|
|
{
|
|
|
|
ScspInternalVars->inbreakpoint = 1;
|
|
|
|
if (ScspInternalVars->BreakpointCallBack)
|
|
|
|
ScspInternalVars->BreakpointCallBack (ScspInternalVars->codebreakpoint[i].addr);
|
|
|
|
ScspInternalVars->inbreakpoint = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// execute instructions individually
|
|
|
|
cyclesexecuted += M68K->Exec(1);
|
|
|
|
|
|
|
|
}
|
|
|
|
return cyclesexecuted;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KStep (void)
|
|
|
|
{
|
|
|
|
M68K->Exec(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Wait for background execution to finish (used on PSP)
|
|
|
|
void
|
|
|
|
M68KSync (void)
|
|
|
|
{
|
|
|
|
M68K->Sync();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspConvert32uto16s (s32 *srcL, s32 *srcR, s16 *dst, u32 len)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
{
|
|
|
|
// Left Channel
|
|
|
|
if (*srcL > 0x7FFF)
|
|
|
|
*dst = 0x7FFF;
|
|
|
|
else if (*srcL < -0x8000)
|
|
|
|
*dst = -0x8000;
|
|
|
|
else
|
|
|
|
*dst = *srcL;
|
|
|
|
|
|
|
|
srcL++;
|
|
|
|
dst++;
|
|
|
|
|
|
|
|
// Right Channel
|
|
|
|
if (*srcR > 0x7FFF)
|
|
|
|
*dst = 0x7FFF;
|
|
|
|
else if (*srcR < -0x8000)
|
|
|
|
*dst = -0x8000;
|
|
|
|
else
|
|
|
|
*dst = *srcR;
|
|
|
|
|
|
|
|
srcR++;
|
|
|
|
dst++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspReceiveCDDA (const u8 *sector)
|
|
|
|
{
|
|
|
|
memcpy(cddabuf.sectors[cddanextin], sector, 2352);
|
|
|
|
cddanextin = (cddanextin+1) % (sizeof(cddabuf.sectors) /
|
|
|
|
sizeof(cddabuf.sectors[0]));
|
|
|
|
cddaoutleft += 2352;
|
|
|
|
|
|
|
|
if (cddaoutleft > sizeof(cddabuf.data))
|
|
|
|
{
|
|
|
|
SCSPLOG ("WARNING: CDDA buffer overrun\n");
|
|
|
|
cddaoutleft = sizeof(cddabuf.data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspExec ()
|
|
|
|
{
|
|
|
|
#ifdef WIN32
|
|
|
|
s16 stereodata16[(44100 / 60) * 16]; //11760
|
|
|
|
#endif
|
|
|
|
u32 audiosize;
|
|
|
|
|
|
|
|
ScspInternalVars->scsptiming2 +=
|
|
|
|
((scspsoundlen << 16) + scsplines / 2) / scsplines;
|
|
|
|
scsp_update_timer (ScspInternalVars->scsptiming2 >> 16); // Pass integer part
|
|
|
|
ScspInternalVars->scsptiming2 &= 0xFFFF; // Keep fractional part
|
|
|
|
ScspInternalVars->scsptiming1++;
|
|
|
|
|
|
|
|
if (ScspInternalVars->scsptiming1 >= scsplines)
|
|
|
|
{
|
|
|
|
s32 *bufL, *bufR;
|
|
|
|
|
|
|
|
ScspInternalVars->scsptiming1 -= scsplines;
|
|
|
|
ScspInternalVars->scsptiming2 = 0;
|
|
|
|
|
|
|
|
if (scspframeaccurate)
|
|
|
|
{
|
|
|
|
// Update sound buffers
|
|
|
|
if (scspsoundgenpos + scspsoundlen > scspsoundbufsize)
|
|
|
|
scspsoundgenpos = 0;
|
|
|
|
|
|
|
|
if (scspsoundoutleft + scspsoundlen > scspsoundbufsize)
|
|
|
|
{
|
|
|
|
u32 overrun = (scspsoundoutleft + scspsoundlen) -
|
|
|
|
scspsoundbufsize;
|
|
|
|
SCSPLOG ("WARNING: Sound buffer overrun, %lu samples\n",
|
|
|
|
(long)overrun);
|
|
|
|
scspsoundoutleft -= overrun;
|
|
|
|
}
|
|
|
|
|
|
|
|
bufL = (s32 *)&scspchannel[0].data32[scspsoundgenpos];
|
|
|
|
bufR = (s32 *)&scspchannel[1].data32[scspsoundgenpos];
|
|
|
|
memset (bufL, 0, sizeof(u32) * scspsoundlen);
|
|
|
|
memset (bufR, 0, sizeof(u32) * scspsoundlen);
|
|
|
|
scsp_update (bufL, bufR, scspsoundlen);
|
|
|
|
scspsoundgenpos += scspsoundlen;
|
|
|
|
scspsoundoutleft += scspsoundlen;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scspframeaccurate)
|
|
|
|
{
|
|
|
|
while (scspsoundoutleft > 0 &&
|
|
|
|
(audiosize = SNDCore->GetAudioSpace ()) > 0)
|
|
|
|
{
|
|
|
|
s32 outstart = (s32)scspsoundgenpos - (s32)scspsoundoutleft;
|
|
|
|
|
|
|
|
if (outstart < 0)
|
|
|
|
outstart += scspsoundbufsize;
|
|
|
|
if (audiosize > scspsoundoutleft)
|
|
|
|
audiosize = scspsoundoutleft;
|
|
|
|
if (audiosize > scspsoundbufsize - outstart)
|
|
|
|
audiosize = scspsoundbufsize - outstart;
|
|
|
|
|
|
|
|
SNDCore->UpdateAudio (&scspchannel[0].data32[outstart],
|
|
|
|
&scspchannel[1].data32[outstart], audiosize);
|
|
|
|
scspsoundoutleft -= audiosize;
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
ScspConvert32uto16s (&scspchannel[0].data32[outstart],
|
|
|
|
&scspchannel[1].data32[outstart],
|
|
|
|
(s16 *)stereodata16, audiosize);
|
|
|
|
DRV_AviSoundUpdate(stereodata16, audiosize);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((audiosize = SNDCore->GetAudioSpace ()))
|
|
|
|
{
|
|
|
|
if (audiosize > scspsoundlen)
|
|
|
|
audiosize = scspsoundlen;
|
|
|
|
memset(scspchannel[0].data32, 0, sizeof(u32) * audiosize);
|
|
|
|
memset(scspchannel[1].data32, 0, sizeof(u32) * audiosize);
|
|
|
|
|
|
|
|
scsp_update ((s32 *)scspchannel[0].data32,
|
|
|
|
(s32 *)scspchannel[1].data32, audiosize);
|
|
|
|
SNDCore->UpdateAudio (scspchannel[0].data32,
|
|
|
|
(u32 *)scspchannel[1].data32, audiosize);
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
ScspConvert32uto16s ((s32 *)scspchannel[0].data32,
|
|
|
|
(s32 *)scspchannel[1].data32,
|
|
|
|
(s16 *)stereodata16, audiosize);
|
|
|
|
DRV_AviSoundUpdate (stereodata16, audiosize);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
} // if (scspframeaccurate)
|
|
|
|
|
|
|
|
scsp_update_monitor ();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KWriteNotify (u32 address, u32 size)
|
|
|
|
{
|
|
|
|
M68K->WriteNotify (address, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KGetRegisters (m68kregs_struct *regs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (regs != NULL)
|
|
|
|
{
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
regs->D[i] = M68K->GetDReg (i);
|
|
|
|
regs->A[i] = M68K->GetAReg (i);
|
|
|
|
}
|
|
|
|
|
|
|
|
regs->SR = M68K->GetSR ();
|
|
|
|
regs->PC = M68K->GetPC ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KSetRegisters (m68kregs_struct *regs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (regs != NULL)
|
|
|
|
{
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
M68K->SetDReg (i, regs->D[i]);
|
|
|
|
M68K->SetAReg (i, regs->A[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
M68K->SetSR (regs->SR);
|
|
|
|
M68K->SetPC (regs->PC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspMuteAudio (int flags)
|
|
|
|
{
|
|
|
|
scsp_mute_flags |= flags;
|
|
|
|
if (SNDCore && scsp_mute_flags)
|
|
|
|
SNDCore->MuteAudio ();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspUnMuteAudio (int flags)
|
|
|
|
{
|
|
|
|
scsp_mute_flags &= ~flags;
|
|
|
|
if (SNDCore && (scsp_mute_flags == 0))
|
|
|
|
SNDCore->UnMuteAudio ();
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspSetVolume (int volume)
|
|
|
|
{
|
|
|
|
scsp_volume = volume;
|
|
|
|
if (SNDCore)
|
|
|
|
SNDCore->SetVolume (volume);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KSetBreakpointCallBack (void (*func)(u32))
|
|
|
|
{
|
|
|
|
ScspInternalVars->BreakpointCallBack = func;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int
|
|
|
|
M68KAddCodeBreakpoint (u32 addr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (ScspInternalVars->numcodebreakpoints < MAX_BREAKPOINTS)
|
|
|
|
{
|
|
|
|
// Make sure it isn't already on the list
|
|
|
|
for (i = 0; i < ScspInternalVars->numcodebreakpoints; i++)
|
|
|
|
{
|
|
|
|
if (addr == ScspInternalVars->codebreakpoint[i].addr)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ScspInternalVars->codebreakpoint[i].addr = addr;
|
|
|
|
ScspInternalVars->numcodebreakpoints++;
|
|
|
|
m68kexecptr = M68KExecBP;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KSortCodeBreakpoints (void)
|
|
|
|
{
|
|
|
|
int i, i2;
|
|
|
|
u32 tmp;
|
|
|
|
|
|
|
|
for (i = 0; i < (MAX_BREAKPOINTS - 1); i++)
|
|
|
|
{
|
|
|
|
for (i2 = i+1; i2 < MAX_BREAKPOINTS; i2++)
|
|
|
|
{
|
|
|
|
if (ScspInternalVars->codebreakpoint[i].addr == 0xFFFFFFFF &&
|
|
|
|
ScspInternalVars->codebreakpoint[i2].addr != 0xFFFFFFFF)
|
|
|
|
{
|
|
|
|
tmp = ScspInternalVars->codebreakpoint[i].addr;
|
|
|
|
ScspInternalVars->codebreakpoint[i].addr =
|
|
|
|
ScspInternalVars->codebreakpoint[i2].addr;
|
|
|
|
ScspInternalVars->codebreakpoint[i2].addr = tmp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int
|
|
|
|
M68KDelCodeBreakpoint (u32 addr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (ScspInternalVars->numcodebreakpoints > 0)
|
|
|
|
{
|
|
|
|
for (i = 0; i < ScspInternalVars->numcodebreakpoints; i++)
|
|
|
|
{
|
|
|
|
if (ScspInternalVars->codebreakpoint[i].addr == addr)
|
|
|
|
{
|
|
|
|
ScspInternalVars->codebreakpoint[i].addr = 0xFFFFFFFF;
|
|
|
|
M68KSortCodeBreakpoints ();
|
|
|
|
ScspInternalVars->numcodebreakpoints--;
|
|
|
|
if (ScspInternalVars->numcodebreakpoints == 0)
|
|
|
|
m68kexecptr = M68K->Exec;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
m68kcodebreakpoint_struct *
|
|
|
|
M68KGetBreakpointList ()
|
|
|
|
{
|
|
|
|
return ScspInternalVars->codebreakpoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
M68KClearCodeBreakpoints ()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_BREAKPOINTS; i++)
|
|
|
|
ScspInternalVars->codebreakpoint[i].addr = 0xFFFFFFFF;
|
|
|
|
|
|
|
|
ScspInternalVars->numcodebreakpoints = 0;
|
|
|
|
}
|
|
|
|
|
2013-05-03 22:12:35 +00:00
|
|
|
void StateFlatten(slot_t *s)
|
|
|
|
{
|
2013-05-11 18:52:19 +00:00
|
|
|
if (s->buf8)
|
|
|
|
s->buf8 = (s8*)(int)(s->buf8 - (s8*)scsp.scsp_ram);
|
|
|
|
if (s->buf16)
|
|
|
|
s->buf16 = (s16*)(int)(s->buf16 - (s16*)scsp.scsp_ram);
|
2013-05-03 22:12:35 +00:00
|
|
|
if (s->einc)
|
|
|
|
s->einc = (s32*)(int)(s->einc - (s32*)s);
|
|
|
|
if (s->arp == &scsp_null_rate[0])
|
|
|
|
s->arp = (s32*)(void*)0xdeadbeef;
|
2013-05-11 18:52:19 +00:00
|
|
|
else if (s->arp)
|
2013-05-03 22:12:35 +00:00
|
|
|
s->arp = (s32*)(int)(s->arp - scsp_attack_rate);
|
|
|
|
if (s->drp == &scsp_null_rate[0])
|
|
|
|
s->drp = (s32*)(void*)0xdeadbeef;
|
2013-05-11 18:52:19 +00:00
|
|
|
else if (s->drp)
|
2013-05-03 22:12:35 +00:00
|
|
|
s->drp = (s32*)(int)(s->drp - scsp_decay_rate);
|
|
|
|
if (s->srp == &scsp_null_rate[0])
|
|
|
|
s->srp = (s32*)(void*)0xdeadbeef;
|
2013-05-11 18:52:19 +00:00
|
|
|
else if (s->srp)
|
2013-05-03 22:12:35 +00:00
|
|
|
s->srp = (s32*)(int)(s->srp - scsp_decay_rate);
|
|
|
|
if (s->rrp == &scsp_null_rate[0])
|
|
|
|
s->rrp = (s32*)(void*)0xdeadbeef;
|
2013-05-11 18:52:19 +00:00
|
|
|
else if (s->rrp)
|
2013-05-03 22:12:35 +00:00
|
|
|
s->rrp = (s32*)(int)(s->rrp - scsp_decay_rate);
|
|
|
|
|
|
|
|
if (s->lfofmw == scsp_lfo_sawt_f)
|
|
|
|
s->lfofmw = (s32*)1;
|
|
|
|
else if (s->lfofmw == scsp_lfo_squa_f)
|
|
|
|
s->lfofmw = (s32*)2;
|
|
|
|
else if (s->lfofmw == scsp_lfo_tri_f)
|
|
|
|
s->lfofmw = (s32*)3;
|
|
|
|
else if (s->lfofmw == scsp_lfo_noi_f)
|
|
|
|
s->lfofmw = (s32*)4;
|
|
|
|
|
|
|
|
if (s->lfoemw == scsp_lfo_sawt_e)
|
|
|
|
s->lfoemw = (s32*)1;
|
|
|
|
else if (s->lfoemw == scsp_lfo_squa_e)
|
|
|
|
s->lfoemw = (s32*)2;
|
|
|
|
else if (s->lfoemw == scsp_lfo_tri_e)
|
|
|
|
s->lfoemw = (s32*)3;
|
|
|
|
else if (s->lfoemw == scsp_lfo_noi_e)
|
|
|
|
s->lfoemw = (s32*)4;
|
|
|
|
|
|
|
|
if (s->enxt == scsp_env_null_next)
|
|
|
|
s->enxt = (void (*)(slot_t*))(void*)1;
|
|
|
|
else if (s->enxt == scsp_release_next)
|
|
|
|
s->enxt = (void (*)(slot_t*))(void*)2;
|
|
|
|
else if (s->enxt == scsp_sustain_next)
|
|
|
|
s->enxt = (void (*)(slot_t*))(void*)3;
|
|
|
|
else if (s->enxt == scsp_decay_next)
|
|
|
|
s->enxt = (void (*)(slot_t*))(void*)4;
|
|
|
|
else if (s->enxt == scsp_attack_next)
|
|
|
|
s->enxt = (void (*)(slot_t*))(void*)5;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StateUnFlatten(slot_t *s)
|
|
|
|
{
|
2013-05-11 18:52:19 +00:00
|
|
|
if (s->buf8)
|
|
|
|
s->buf8 = (s8*)scsp.scsp_ram + (int)(s->buf8);
|
|
|
|
if (s->buf16)
|
|
|
|
s->buf16 = (s16*)scsp.scsp_ram + (int)(s->buf16);
|
2013-05-03 22:12:35 +00:00
|
|
|
if (s->einc)
|
|
|
|
s->einc = (s32*)s + (int)(s->einc);
|
|
|
|
if (s->arp == (void*)0xdeadbeef)
|
|
|
|
s->arp = &scsp_null_rate[0];
|
2013-05-11 18:52:19 +00:00
|
|
|
else if (s->arp)
|
2013-05-03 22:12:35 +00:00
|
|
|
s->arp = scsp_attack_rate + (int)(s->arp);
|
|
|
|
if (s->drp == (void*)0xdeadbeef)
|
|
|
|
s->drp = &scsp_null_rate[0];
|
2013-05-11 18:52:19 +00:00
|
|
|
else if (s->drp)
|
2013-05-03 22:12:35 +00:00
|
|
|
s->drp = scsp_decay_rate + (int)(s->drp);
|
|
|
|
if (s->srp == (void*)0xdeadbeef)
|
|
|
|
s->srp = &scsp_null_rate[0];
|
2013-05-11 18:52:19 +00:00
|
|
|
else if (s->srp)
|
2013-05-03 22:12:35 +00:00
|
|
|
s->srp = scsp_decay_rate + (int)(s->srp);
|
|
|
|
if (s->rrp == (void*)0xdeadbeef)
|
|
|
|
s->rrp = &scsp_null_rate[0];
|
2013-05-11 18:52:19 +00:00
|
|
|
else if (s->rrp)
|
2013-05-03 22:12:35 +00:00
|
|
|
s->rrp = scsp_decay_rate + (int)(s->rrp);
|
|
|
|
|
|
|
|
switch ((int)(s->lfofmw))
|
|
|
|
{
|
|
|
|
case 1: s->lfofmw = scsp_lfo_sawt_f; break;
|
|
|
|
case 2: s->lfofmw = scsp_lfo_squa_f; break;
|
|
|
|
case 3: s->lfofmw = scsp_lfo_tri_f; break;
|
|
|
|
case 4: s->lfofmw = scsp_lfo_noi_f; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ((int)(s->lfoemw))
|
|
|
|
{
|
|
|
|
case 1: s->lfoemw = scsp_lfo_sawt_e; break;
|
|
|
|
case 2: s->lfoemw = scsp_lfo_squa_e; break;
|
|
|
|
case 3: s->lfoemw = scsp_lfo_tri_e; break;
|
|
|
|
case 4: s->lfoemw = scsp_lfo_noi_e; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ((int)(s->enxt))
|
|
|
|
{
|
|
|
|
case 1: s->enxt = scsp_env_null_next; break;
|
|
|
|
case 2: s->enxt = scsp_release_next; break;
|
|
|
|
case 3: s->enxt = scsp_sustain_next; break;
|
|
|
|
case 4: s->enxt = scsp_decay_next; break;
|
|
|
|
case 5: s->enxt = scsp_attack_next; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-30 21:20:11 +00:00
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int
|
|
|
|
SoundSaveState (FILE *fp)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u32 temp;
|
|
|
|
int offset;
|
|
|
|
u8 nextphase;
|
|
|
|
IOCheck_struct check;
|
|
|
|
|
|
|
|
offset = StateWriteHeader (fp, "SCSP", 2);
|
|
|
|
|
|
|
|
// Save 68k registers first
|
|
|
|
ywrite (&check, (void *)&IsM68KRunning, 1, 1, fp);
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
temp = M68K->GetDReg (i);
|
|
|
|
ywrite (&check, (void *)&temp, 4, 1, fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
temp = M68K->GetAReg (i);
|
|
|
|
ywrite (&check, (void *)&temp, 4, 1, fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
temp = M68K->GetSR ();
|
|
|
|
ywrite (&check, (void *)&temp, 4, 1, fp);
|
|
|
|
temp = M68K->GetPC ();
|
|
|
|
ywrite (&check, (void *)&temp, 4, 1, fp);
|
|
|
|
|
|
|
|
// Now for the SCSP registers
|
|
|
|
ywrite (&check, (void *)scsp_reg, 0x1000, 1, fp);
|
|
|
|
|
|
|
|
// Sound RAM is important
|
|
|
|
ywrite (&check, (void *)SoundRam, 0x80000, 1, fp);
|
|
|
|
|
2013-05-03 22:12:35 +00:00
|
|
|
for (i = 0; i < 32; i++)
|
|
|
|
StateFlatten(&scsp.slot[i]);
|
|
|
|
|
2013-05-03 19:51:03 +00:00
|
|
|
ywrite (&check, (void *)&scsp, offsetof(scsp_t, scsp_ram), 1, fp);
|
|
|
|
|
2013-05-03 22:12:35 +00:00
|
|
|
for (i = 0; i < 32; i++)
|
|
|
|
StateUnFlatten(&scsp.slot[i]);
|
|
|
|
|
2013-05-03 19:51:03 +00:00
|
|
|
/*
|
2013-04-30 21:20:11 +00:00
|
|
|
// Write slot internal variables
|
|
|
|
for (i = 0; i < 32; i++)
|
|
|
|
{
|
|
|
|
s32 einc;
|
|
|
|
|
|
|
|
ywrite (&check, (void *)&scsp.slot[i].key, 1, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.slot[i].fcnt, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.slot[i].ecnt, 4, 1, fp);
|
|
|
|
|
|
|
|
if (scsp.slot[i].einc == &scsp.slot[i].einca)
|
|
|
|
einc = 0;
|
|
|
|
else if (scsp.slot[i].einc == &scsp.slot[i].eincd)
|
|
|
|
einc = 1;
|
|
|
|
else if (scsp.slot[i].einc == &scsp.slot[i].eincs)
|
|
|
|
einc = 2;
|
|
|
|
else if (scsp.slot[i].einc == &scsp.slot[i].eincr)
|
|
|
|
einc = 3;
|
|
|
|
else
|
|
|
|
einc = 4;
|
|
|
|
|
|
|
|
ywrite (&check, (void *)&einc, 4, 1, fp);
|
|
|
|
|
|
|
|
ywrite (&check, (void *)&scsp.slot[i].ecmp, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.slot[i].ecurp, 4, 1, fp);
|
|
|
|
|
|
|
|
if (scsp.slot[i].enxt == scsp_env_null_next)
|
|
|
|
nextphase = 0;
|
|
|
|
else if (scsp.slot[i].enxt == scsp_release_next)
|
|
|
|
nextphase = 1;
|
|
|
|
else if (scsp.slot[i].enxt == scsp_sustain_next)
|
|
|
|
nextphase = 2;
|
|
|
|
else if (scsp.slot[i].enxt == scsp_decay_next)
|
|
|
|
nextphase = 3;
|
|
|
|
else if (scsp.slot[i].enxt == scsp_attack_next)
|
|
|
|
nextphase = 4;
|
|
|
|
ywrite (&check, (void *)&nextphase, 1, 1, fp);
|
|
|
|
|
|
|
|
ywrite (&check, (void *)&scsp.slot[i].lfocnt, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.slot[i].lfoinc, 4, 1, fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write main internal variables
|
|
|
|
ywrite (&check, (void *)&scsp.mem4b, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.mvol, 4, 1, fp);
|
|
|
|
|
|
|
|
ywrite (&check, (void *)&scsp.rbl, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.rbp, 4, 1, fp);
|
|
|
|
|
|
|
|
ywrite (&check, (void *)&scsp.mslc, 4, 1, fp);
|
|
|
|
|
|
|
|
ywrite (&check, (void *)&scsp.dmea, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.drga, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.dmfl, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.dmlen, 4, 1, fp);
|
|
|
|
|
|
|
|
ywrite (&check, (void *)scsp.midinbuf, 1, 4, fp);
|
|
|
|
ywrite (&check, (void *)scsp.midoutbuf, 1, 4, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.midincnt, 1, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.midoutcnt, 1, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.midflag, 1, 1, fp);
|
|
|
|
|
|
|
|
ywrite (&check, (void *)&scsp.timacnt, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.timasd, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.timbcnt, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.timbsd, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.timccnt, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.timcsd, 4, 1, fp);
|
|
|
|
|
|
|
|
ywrite (&check, (void *)&scsp.scieb, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.scipd, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.scilv0, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.scilv1, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.scilv2, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.mcieb, 4, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp.mcipd, 4, 1, fp);
|
|
|
|
|
|
|
|
ywrite (&check, (void *)scsp.stack, 4, 32 * 2, fp);
|
2013-05-03 19:51:03 +00:00
|
|
|
*/
|
2013-04-30 21:20:11 +00:00
|
|
|
return StateFinishHeader (fp, offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int
|
|
|
|
SoundLoadState (FILE *fp, int version, int size)
|
|
|
|
{
|
|
|
|
int i, i2;
|
|
|
|
u32 temp;
|
|
|
|
u8 nextphase;
|
|
|
|
IOCheck_struct check;
|
|
|
|
|
|
|
|
// Read 68k registers first
|
|
|
|
yread (&check, (void *)&IsM68KRunning, 1, 1, fp);
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
yread (&check, (void *)&temp, 4, 1, fp);
|
|
|
|
M68K->SetDReg (i, temp);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
{
|
|
|
|
yread (&check, (void *)&temp, 4, 1, fp);
|
|
|
|
M68K->SetAReg (i, temp);
|
|
|
|
}
|
|
|
|
|
|
|
|
yread (&check, (void *)&temp, 4, 1, fp);
|
|
|
|
M68K->SetSR (temp);
|
|
|
|
yread (&check, (void *)&temp, 4, 1, fp);
|
|
|
|
M68K->SetPC (temp);
|
|
|
|
|
|
|
|
// Now for the SCSP registers
|
|
|
|
yread (&check, (void *)scsp_reg, 0x1000, 1, fp);
|
|
|
|
|
|
|
|
// Lastly, sound ram
|
|
|
|
yread (&check, (void *)SoundRam, 0x80000, 1, fp);
|
|
|
|
|
2013-05-03 19:51:03 +00:00
|
|
|
yread (&check, (void *)&scsp, offsetof(scsp_t, scsp_ram), 1, fp);
|
|
|
|
|
2013-05-03 22:12:35 +00:00
|
|
|
for (i = 0; i < 32; i++)
|
|
|
|
StateUnFlatten(&scsp.slot[i]);
|
|
|
|
|
2013-05-03 19:51:03 +00:00
|
|
|
/*
|
2013-04-30 21:20:11 +00:00
|
|
|
if (version > 1)
|
|
|
|
{
|
|
|
|
// Internal variables need to be regenerated
|
|
|
|
for(i = 0; i < 32; i++)
|
|
|
|
{
|
|
|
|
for (i2 = 0; i2 < 0x20; i2+=2)
|
|
|
|
scsp_slot_set_w (i, 0x1E - i2, scsp_slot_get_w (i, 0x1E - i2));
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp_set_w (0x402, scsp_get_w (0x402));
|
|
|
|
|
|
|
|
// Read slot internal variables
|
|
|
|
for (i = 0; i < 32; i++)
|
|
|
|
{
|
|
|
|
s32 einc;
|
|
|
|
|
|
|
|
yread (&check, (void *)&scsp.slot[i].key, 1, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.slot[i].fcnt, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.slot[i].ecnt, 4, 1, fp);
|
|
|
|
|
|
|
|
yread (&check, (void *)&einc, 4, 1, fp);
|
|
|
|
switch (einc)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
scsp.slot[i].einc = &scsp.slot[i].einca;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
scsp.slot[i].einc = &scsp.slot[i].eincd;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
scsp.slot[i].einc = &scsp.slot[i].eincs;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
scsp.slot[i].einc = &scsp.slot[i].eincr;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
scsp.slot[i].einc = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
yread (&check, (void *)&scsp.slot[i].ecmp, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.slot[i].ecurp, 4, 1, fp);
|
|
|
|
|
|
|
|
yread (&check, (void *)&nextphase, 1, 1, fp);
|
|
|
|
switch (nextphase)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
scsp.slot[i].enxt = scsp_env_null_next;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
scsp.slot[i].enxt = scsp_release_next;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
scsp.slot[i].enxt = scsp_sustain_next;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
scsp.slot[i].enxt = scsp_decay_next;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
scsp.slot[i].enxt = scsp_attack_next;
|
|
|
|
break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
yread (&check, (void *)&scsp.slot[i].lfocnt, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.slot[i].lfoinc, 4, 1, fp);
|
|
|
|
|
|
|
|
// Rebuild the buf8/buf16 variables
|
|
|
|
if (scsp.slot[i].pcm8b)
|
|
|
|
{
|
|
|
|
scsp.slot[i].buf8 = (s8*)&(scsp.scsp_ram[scsp.slot[i].sa]);
|
|
|
|
if ((scsp.slot[i].sa + (scsp.slot[i].lea >> SCSP_FREQ_LB)) >
|
|
|
|
SCSP_RAM_MASK)
|
|
|
|
scsp.slot[i].lea = (SCSP_RAM_MASK - scsp.slot[i].sa) <<
|
|
|
|
SCSP_FREQ_LB;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
scsp.slot[i].buf16 = (s16*)&(scsp.scsp_ram[scsp.slot[i].sa & ~1]);
|
|
|
|
if ((scsp.slot[i].sa + (scsp.slot[i].lea >> (SCSP_FREQ_LB - 1))) >
|
|
|
|
SCSP_RAM_MASK)
|
|
|
|
scsp.slot[i].lea = (SCSP_RAM_MASK - scsp.slot[i].sa) <<
|
|
|
|
(SCSP_FREQ_LB - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read main internal variables
|
|
|
|
yread (&check, (void *)&scsp.mem4b, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.mvol, 4, 1, fp);
|
|
|
|
|
|
|
|
yread (&check, (void *)&scsp.rbl, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.rbp, 4, 1, fp);
|
|
|
|
|
|
|
|
yread (&check, (void *)&scsp.mslc, 4, 1, fp);
|
|
|
|
|
|
|
|
yread (&check, (void *)&scsp.dmea, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.drga, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.dmfl, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.dmlen, 4, 1, fp);
|
|
|
|
|
|
|
|
yread (&check, (void *)scsp.midinbuf, 1, 4, fp);
|
|
|
|
yread (&check, (void *)scsp.midoutbuf, 1, 4, fp);
|
|
|
|
yread (&check, (void *)&scsp.midincnt, 1, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.midoutcnt, 1, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.midflag, 1, 1, fp);
|
|
|
|
|
|
|
|
yread (&check, (void *)&scsp.timacnt, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.timasd, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.timbcnt, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.timbsd, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.timccnt, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.timcsd, 4, 1, fp);
|
|
|
|
|
|
|
|
yread (&check, (void *)&scsp.scieb, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.scipd, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.scilv0, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.scilv1, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.scilv2, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.mcieb, 4, 1, fp);
|
|
|
|
yread (&check, (void *)&scsp.mcipd, 4, 1, fp);
|
|
|
|
|
|
|
|
yread (&check, (void *)scsp.stack, 4, 32 * 2, fp);
|
|
|
|
}
|
2013-05-03 19:51:03 +00:00
|
|
|
*/
|
|
|
|
|
2013-04-30 21:20:11 +00:00
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static char *
|
|
|
|
AddSoundLFO (char *outstring, const char *string, u16 level, u16 waveform)
|
|
|
|
{
|
|
|
|
if (level > 0)
|
|
|
|
{
|
|
|
|
switch (waveform)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
AddString(outstring, "%s Sawtooth\r\n", string);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
AddString(outstring, "%s Square\r\n", string);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
AddString(outstring, "%s Triangle\r\n", string);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
AddString(outstring, "%s Noise\r\n", string);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return outstring;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static char *
|
|
|
|
AddSoundPan (char *outstring, u16 pan)
|
|
|
|
{
|
|
|
|
if (pan == 0x0F)
|
|
|
|
{
|
|
|
|
AddString(outstring, "Left = -MAX dB, Right = -0 dB\r\n");
|
|
|
|
}
|
|
|
|
else if (pan == 0x1F)
|
|
|
|
{
|
|
|
|
AddString(outstring, "Left = -0 dB, Right = -MAX dB\r\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
AddString(outstring, "Left = -%d dB, Right = -%d dB\r\n", (pan & 0xF) * 3, (pan >> 4) * 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
return outstring;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static char *
|
|
|
|
AddSoundLevel (char *outstring, u16 level)
|
|
|
|
{
|
|
|
|
if (level == 0)
|
|
|
|
{
|
|
|
|
AddString(outstring, "-MAX dB\r\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
AddString(outstring, "-%d dB\r\n", (7-level) * 6);
|
|
|
|
}
|
|
|
|
|
|
|
|
return outstring;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspSlotDebugStats (u8 slotnum, char *outstring)
|
|
|
|
{
|
|
|
|
u32 slotoffset = slotnum * 0x20;
|
|
|
|
|
|
|
|
AddString (outstring, "Sound Source = ");
|
|
|
|
switch (scsp.slot[slotnum].ssctl)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
AddString (outstring, "External DRAM data\r\n");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
AddString (outstring, "Internal(Noise)\r\n");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
AddString (outstring, "Internal(0's)\r\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
AddString (outstring, "Invalid setting\r\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
AddString (outstring, "Source bit = ");
|
|
|
|
switch(scsp.slot[slotnum].sbctl)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
AddString (outstring, "No bit reversal\r\n");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
AddString (outstring, "Reverse other bits\r\n");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
AddString (outstring, "Reverse sign bit\r\n");
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
AddString (outstring, "Reverse sign and other bits\r\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop Control
|
|
|
|
AddString (outstring, "Loop Mode = ");
|
|
|
|
switch (scsp.slot[slotnum].lpctl)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
AddString (outstring, "Off\r\n");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
AddString (outstring, "Normal\r\n");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
AddString (outstring, "Reverse\r\n");
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
AddString (outstring, "Alternating\r\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// PCM8B
|
|
|
|
// NOTE: Need curly braces here, as AddString is a macro.
|
|
|
|
if (scsp.slot[slotnum].pcm8b)
|
|
|
|
{
|
|
|
|
AddString (outstring, "8-bit samples\r\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
AddString (outstring, "16-bit samples\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
AddString (outstring, "Start Address = %05lX\r\n", (unsigned long)scsp.slot[slotnum].sa);
|
|
|
|
AddString (outstring, "Loop Start Address = %04lX\r\n", (unsigned long)scsp.slot[slotnum].lsa >> SCSP_FREQ_LB);
|
|
|
|
AddString (outstring, "Loop End Address = %04lX\r\n", (unsigned long)scsp.slot[slotnum].lea >> SCSP_FREQ_LB);
|
|
|
|
AddString (outstring, "Decay 1 Rate = %ld\r\n", (unsigned long)scsp.slot[slotnum].dr);
|
|
|
|
AddString (outstring, "Decay 2 Rate = %ld\r\n", (unsigned long)scsp.slot[slotnum].sr);
|
|
|
|
if (scsp.slot[slotnum].eghold)
|
|
|
|
AddString (outstring, "EG Hold Enabled\r\n");
|
|
|
|
AddString (outstring, "Attack Rate = %ld\r\n", (unsigned long)scsp.slot[slotnum].ar);
|
|
|
|
|
|
|
|
if (scsp.slot[slotnum].lslnk)
|
|
|
|
AddString (outstring, "Loop Start Link Enabled\r\n");
|
|
|
|
|
|
|
|
if (scsp.slot[slotnum].krs != 0)
|
|
|
|
AddString (outstring, "Key rate scaling = %ld\r\n", (unsigned long)scsp.slot[slotnum].krs);
|
|
|
|
|
|
|
|
AddString (outstring, "Decay Level = %d\r\n", (scsp_r_w(slotoffset + 0xA) >> 5) & 0x1F);
|
|
|
|
AddString (outstring, "Release Rate = %ld\r\n", (unsigned long)scsp.slot[slotnum].rr);
|
|
|
|
|
|
|
|
if (scsp.slot[slotnum].swe)
|
|
|
|
AddString (outstring, "Stack Write Inhibited\r\n");
|
|
|
|
|
|
|
|
if (scsp.slot[slotnum].sdir)
|
|
|
|
AddString (outstring, "Sound Direct Enabled\r\n");
|
|
|
|
|
|
|
|
AddString (outstring, "Total Level = %ld\r\n", (unsigned long)scsp.slot[slotnum].tl);
|
|
|
|
|
|
|
|
AddString (outstring, "Modulation Level = %d\r\n", scsp.slot[slotnum].mdl);
|
|
|
|
AddString (outstring, "Modulation Input X = %d\r\n", scsp.slot[slotnum].mdx);
|
|
|
|
AddString (outstring, "Modulation Input Y = %d\r\n", scsp.slot[slotnum].mdy);
|
|
|
|
|
|
|
|
AddString (outstring, "Octave = %d\r\n", (scsp_r_w(slotoffset + 0x10) >> 11) & 0xF);
|
|
|
|
AddString (outstring, "Frequency Number Switch = %d\r\n", scsp_r_w(slotoffset + 0x10) & 0x3FF);
|
|
|
|
|
|
|
|
AddString (outstring, "LFO Reset = %s\r\n", ((scsp_r_w(slotoffset + 0x12) >> 15) & 0x1) ? "TRUE" : "FALSE");
|
|
|
|
AddString (outstring, "LFO Frequency = %d\r\n", (scsp_r_w(slotoffset + 0x12) >> 10) & 0x1F);
|
|
|
|
outstring = AddSoundLFO (outstring, "LFO Frequency modulation waveform = ",
|
|
|
|
(scsp_r_w(slotoffset + 0x12) >> 5) & 0x7,
|
|
|
|
(scsp_r_w(slotoffset + 0x12) >> 8) & 0x3);
|
|
|
|
AddString (outstring, "LFO Frequency modulation level = %d\r\n", (scsp_r_w(slotoffset + 0x12) >> 5) & 0x7);
|
|
|
|
outstring = AddSoundLFO (outstring, "LFO Amplitude modulation waveform = ",
|
|
|
|
scsp_r_w(slotoffset + 0x12) & 0x7,
|
|
|
|
(scsp_r_w(slotoffset + 0x12) >> 3) & 0x3);
|
|
|
|
AddString (outstring, "LFO Amplitude modulation level = %d\r\n", scsp_r_w(slotoffset + 0x12) & 0x7);
|
|
|
|
|
|
|
|
AddString (outstring, "Input mix level = ");
|
|
|
|
outstring = AddSoundLevel (outstring, scsp_r_w(slotoffset + 0x14) & 0x7);
|
|
|
|
AddString (outstring, "Input Select = %d\r\n", (scsp_r_w(slotoffset + 0x14) >> 3) & 0x1F);
|
|
|
|
|
|
|
|
AddString (outstring, "Direct data send level = ");
|
|
|
|
outstring = AddSoundLevel (outstring, (scsp_r_w(slotoffset + 0x16) >> 13) & 0x7);
|
|
|
|
AddString (outstring, "Direct data panpot = ");
|
|
|
|
outstring = AddSoundPan (outstring, (scsp_r_w(slotoffset + 0x16) >> 8) & 0x1F);
|
|
|
|
|
|
|
|
AddString (outstring, "Effect data send level = ");
|
|
|
|
outstring = AddSoundLevel (outstring, (scsp_r_w(slotoffset + 0x16) >> 5) & 0x7);
|
|
|
|
AddString (outstring, "Effect data panpot = ");
|
|
|
|
outstring = AddSoundPan (outstring, scsp_r_w(slotoffset + 0x16) & 0x1F);
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void
|
|
|
|
ScspCommonControlRegisterDebugStats (char *outstring)
|
|
|
|
{
|
|
|
|
AddString (outstring, "Memory: %s\r\n", scsp.mem4b ? "4 Mbit" : "2 Mbit");
|
|
|
|
AddString (outstring, "Master volume: %ld\r\n", (unsigned long)scsp.mvol);
|
|
|
|
AddString (outstring, "Ring buffer length: %ld\r\n", (unsigned long)scsp.rbl);
|
|
|
|
AddString (outstring, "Ring buffer address: %08lX\r\n", (unsigned long)scsp.rbp);
|
|
|
|
AddString (outstring, "\r\n");
|
|
|
|
|
|
|
|
AddString (outstring, "Slot Status Registers\r\n");
|
|
|
|
AddString (outstring, "-----------------\r\n");
|
|
|
|
AddString (outstring, "Monitor slot: %ld\r\n", (unsigned long)scsp.mslc);
|
|
|
|
AddString (outstring, "Call address: %ld\r\n", (unsigned long)scsp.ca);
|
|
|
|
AddString (outstring, "\r\n");
|
|
|
|
|
|
|
|
AddString (outstring, "DMA Registers\r\n");
|
|
|
|
AddString (outstring, "-----------------\r\n");
|
|
|
|
AddString (outstring, "DMA memory address start: %08lX\r\n", (unsigned long)scsp.dmea);
|
|
|
|
AddString (outstring, "DMA register address start: %08lX\r\n", (unsigned long)scsp.drga);
|
|
|
|
AddString (outstring, "DMA Flags: %lX\r\n", (unsigned long)scsp.dmlen);
|
|
|
|
AddString (outstring, "\r\n");
|
|
|
|
|
|
|
|
AddString (outstring, "Timer Registers\r\n");
|
|
|
|
AddString (outstring, "-----------------\r\n");
|
|
|
|
AddString (outstring, "Timer A counter: %02lX\r\n", (unsigned long)scsp.timacnt >> 8);
|
|
|
|
AddString (outstring, "Timer A increment: Every %d sample(s)\r\n", (int)pow(2, (double)scsp.timasd));
|
|
|
|
AddString (outstring, "Timer B counter: %02lX\r\n", (unsigned long)scsp.timbcnt >> 8);
|
|
|
|
AddString (outstring, "Timer B increment: Every %d sample(s)\r\n", (int)pow(2, (double)scsp.timbsd));
|
|
|
|
AddString (outstring, "Timer C counter: %02lX\r\n", (unsigned long)scsp.timccnt >> 8);
|
|
|
|
AddString (outstring, "Timer C increment: Every %d sample(s)\r\n", (int)pow(2, (double)scsp.timcsd));
|
|
|
|
AddString (outstring, "\r\n");
|
|
|
|
|
|
|
|
AddString (outstring, "Interrupt Registers\r\n");
|
|
|
|
AddString (outstring, "-----------------\r\n");
|
|
|
|
AddString (outstring, "Sound cpu interrupt pending: %04lX\r\n", (unsigned long)scsp.scipd);
|
|
|
|
AddString (outstring, "Sound cpu interrupt enable: %04lX\r\n", (unsigned long)scsp.scieb);
|
|
|
|
AddString (outstring, "Sound cpu interrupt level 0: %04lX\r\n", (unsigned long)scsp.scilv0);
|
|
|
|
AddString (outstring, "Sound cpu interrupt level 1: %04lX\r\n", (unsigned long)scsp.scilv1);
|
|
|
|
AddString (outstring, "Sound cpu interrupt level 2: %04lX\r\n", (unsigned long)scsp.scilv2);
|
|
|
|
AddString (outstring, "Main cpu interrupt pending: %04lX\r\n", (unsigned long)scsp.mcipd);
|
|
|
|
AddString (outstring, "Main cpu interrupt enable: %04lX\r\n", (unsigned long)scsp.mcieb);
|
|
|
|
AddString (outstring, "\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int
|
|
|
|
ScspSlotDebugSaveRegisters (u8 slotnum, const char *filename)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
int i;
|
|
|
|
IOCheck_struct check;
|
|
|
|
|
|
|
|
if ((fp = fopen (filename, "wb")) == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = (slotnum * 0x20); i < ((slotnum+1) * 0x20); i += 2)
|
|
|
|
{
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
|
|
ywrite (&check, (void *)&scsp_isr[i ^ 2], 1, 2, fp);
|
|
|
|
#else
|
|
|
|
ywrite (&check, (void *)&scsp_isr[(i + 1) ^ 2], 1, 1, fp);
|
|
|
|
ywrite (&check, (void *)&scsp_isr[i ^ 2], 1, 1, fp);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose (fp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
static u32
|
|
|
|
ScspSlotDebugAudio (slot_t *slot, u32 *workbuf, s16 *buf, u32 len)
|
|
|
|
{
|
|
|
|
u32 *bufL, *bufR;
|
|
|
|
|
|
|
|
bufL = workbuf;
|
|
|
|
bufR = workbuf+len;
|
|
|
|
scsp_bufL = (s32 *)bufL;
|
|
|
|
scsp_bufR = (s32 *)bufR;
|
|
|
|
|
|
|
|
if (slot->ecnt >= SCSP_ENV_DE)
|
|
|
|
{
|
|
|
|
// envelope null...
|
|
|
|
memset (buf, 0, sizeof(s16) * 2 * len);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slot->ssctl)
|
|
|
|
{
|
|
|
|
memset (buf, 0, sizeof(s16) * 2 * len);
|
|
|
|
return 0; // not yet supported!
|
|
|
|
}
|
|
|
|
|
|
|
|
scsp_buf_len = len;
|
|
|
|
scsp_buf_pos = 0;
|
|
|
|
|
|
|
|
// take effect sound volume if no direct sound volume...
|
|
|
|
if ((slot->disll == 31) && (slot->dislr == 31))
|
|
|
|
{
|
|
|
|
slot->disll = slot->efsll;
|
|
|
|
slot->dislr = slot->efslr;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset (bufL, 0, sizeof(u32) * len);
|
|
|
|
memset (bufR, 0, sizeof(u32) * len);
|
|
|
|
scsp_slot_update_p[(slot->lfofms == 31)?0:1]
|
|
|
|
[(slot->lfoems == 31)?0:1]
|
|
|
|
[(slot->pcm8b == 0)?1:0]
|
|
|
|
[(slot->disll == 31)?0:1]
|
|
|
|
[(slot->dislr == 31)?0:1](slot);
|
|
|
|
ScspConvert32uto16s ((s32 *)bufL, (s32 *)bufR, (s16 *)buf, len);
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char id[4];
|
|
|
|
u32 size;
|
|
|
|
} chunk_struct;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
chunk_struct riff;
|
|
|
|
char rifftype[4];
|
|
|
|
} waveheader_struct;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
chunk_struct chunk;
|
|
|
|
u16 compress;
|
|
|
|
u16 numchan;
|
|
|
|
u32 rate;
|
|
|
|
u32 bytespersec;
|
|
|
|
u16 blockalign;
|
|
|
|
u16 bitspersample;
|
|
|
|
} fmt_struct;
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int
|
|
|
|
ScspSlotDebugAudioSaveWav (u8 slotnum, const char *filename)
|
|
|
|
{
|
|
|
|
u32 workbuf[512*2*2];
|
|
|
|
s16 buf[512*2];
|
|
|
|
slot_t slot;
|
|
|
|
FILE *fp;
|
|
|
|
u32 counter = 0;
|
|
|
|
waveheader_struct waveheader;
|
|
|
|
fmt_struct fmt;
|
|
|
|
chunk_struct data;
|
|
|
|
long length;
|
|
|
|
IOCheck_struct check;
|
|
|
|
|
|
|
|
if (scsp.slot[slotnum].lea == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((fp = fopen (filename, "wb")) == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// Do wave header
|
|
|
|
memcpy (waveheader.riff.id, "RIFF", 4);
|
|
|
|
waveheader.riff.size = 0; // we'll fix this after the file is closed
|
|
|
|
memcpy (waveheader.rifftype, "WAVE", 4);
|
|
|
|
ywrite (&check, (void *)&waveheader, 1, sizeof(waveheader_struct), fp);
|
|
|
|
|
|
|
|
// fmt chunk
|
|
|
|
memcpy (fmt.chunk.id, "fmt ", 4);
|
|
|
|
fmt.chunk.size = 16; // we'll fix this at the end
|
|
|
|
fmt.compress = 1; // PCM
|
|
|
|
fmt.numchan = 2; // Stereo
|
|
|
|
fmt.rate = 44100;
|
|
|
|
fmt.bitspersample = 16;
|
|
|
|
fmt.blockalign = fmt.bitspersample / 8 * fmt.numchan;
|
|
|
|
fmt.bytespersec = fmt.rate * fmt.blockalign;
|
|
|
|
ywrite (&check, (void *)&fmt, 1, sizeof(fmt_struct), fp);
|
|
|
|
|
|
|
|
// data chunk
|
|
|
|
memcpy (data.id, "data", 4);
|
|
|
|
data.size = 0; // we'll fix this at the end
|
|
|
|
ywrite (&check, (void *)&data, 1, sizeof(chunk_struct), fp);
|
|
|
|
|
|
|
|
memcpy (&slot, &scsp.slot[slotnum], sizeof(slot_t));
|
|
|
|
|
|
|
|
// Clear out the phase counter, etc.
|
|
|
|
slot.fcnt = 0;
|
|
|
|
slot.ecnt = SCSP_ENV_AS;
|
|
|
|
slot.einc = &slot.einca;
|
|
|
|
slot.ecmp = SCSP_ENV_AE;
|
|
|
|
slot.ecurp = SCSP_ENV_ATTACK;
|
|
|
|
slot.enxt = scsp_attack_next;
|
|
|
|
|
|
|
|
// Mix the audio, and then write it to the file
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
if (ScspSlotDebugAudio (&slot, workbuf, buf, 512) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
counter += 512;
|
|
|
|
ywrite (&check, (void *)buf, 2, 512 * 2, fp);
|
|
|
|
if (slot.lpctl != 0 && counter >= (44100 * 2 * 5))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
length = ftell (fp);
|
|
|
|
|
|
|
|
// Let's fix the riff chunk size and the data chunk size
|
|
|
|
fseek (fp, sizeof(waveheader_struct)-0x8, SEEK_SET);
|
|
|
|
length -= 0x4;
|
|
|
|
ywrite (&check, (void *)&length, 1, 4, fp);
|
|
|
|
|
|
|
|
fseek (fp, sizeof(waveheader_struct) + sizeof(fmt_struct) + 0x4, SEEK_SET);
|
|
|
|
length -= sizeof(waveheader_struct) + sizeof(fmt_struct);
|
|
|
|
ywrite (&check, (void *)&length, 1, 4, fp);
|
|
|
|
fclose (fp);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|