mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 15:27:46 -04:00
audio: added SSG-EG to YM2612, plus some timing changes for SN76496+YM2612
This commit is contained in:
parent
2a942f0d41
commit
8ac9ab7fcb
13 changed files with 571 additions and 455 deletions
2
Makefile
2
Makefile
|
@ -36,7 +36,7 @@ endif
|
|||
|
||||
ifeq ("$(PLATFORM)",$(filter "$(PLATFORM)","gp2x" "opendingux" "rpi1"))
|
||||
# very small caches, avoid optimization options making the binary much bigger
|
||||
CFLAGS += -finline-limit=43 -fno-unroll-loops -fno-ipa-cp -ffast-math
|
||||
CFLAGS += -finline-limit=42 -fno-unroll-loops -fno-ipa-cp -ffast-math
|
||||
# this gets you about 20% better execution speed on 32bit arm/mips
|
||||
CFLAGS += -fno-common -fno-stack-protector -fno-guess-branch-probability -fno-caller-saves -fno-tree-loop-if-convert -fno-regmove
|
||||
endif
|
||||
|
|
|
@ -1393,7 +1393,7 @@ static void emith_sync_t(int sr)
|
|||
else if (tcond >= 0) {
|
||||
int tmp = rcache_get_tmp();
|
||||
EMIT(A64_CSET(tcond, tmp));
|
||||
EMIT(A64_BFI_IMM(sr, tmp, 0, 1)); // assumes SR.T = bit 0
|
||||
EMIT(A64_BFI_IMM(sr, tmp, __builtin_ffs(T)-1, 1));
|
||||
rcache_free_tmp(tmp);
|
||||
}
|
||||
tcond = -1;
|
||||
|
|
|
@ -546,7 +546,7 @@ static void PicoWrite8_z80(u32 a, u32 d)
|
|||
}
|
||||
if ((a & 0x6000) == 0x4000) { // FM Sound
|
||||
if (PicoIn.opt & POPT_EN_FM)
|
||||
Pico.m.status |= ym2612_write_local(a & 3, d & 0xff, 0) & 1;
|
||||
ym2612_write_local(a & 3, d & 0xff, 0);
|
||||
return;
|
||||
}
|
||||
// TODO: probably other VDP access too? Maybe more mirrors?
|
||||
|
@ -1059,6 +1059,8 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
|
|||
break;
|
||||
}
|
||||
|
||||
int scanline = get_scanline(is_from_z80);
|
||||
PsndDoFM(scanline);
|
||||
#ifdef __GP2X__
|
||||
if (PicoIn.opt & POPT_EXT_FM)
|
||||
return YM2612Write_940(a, d, get_scanline(is_from_z80));
|
||||
|
@ -1224,7 +1226,7 @@ static unsigned char z80_md_bank_read(unsigned short a)
|
|||
static void z80_md_ym2612_write(unsigned int a, unsigned char data)
|
||||
{
|
||||
if (PicoIn.opt & POPT_EN_FM)
|
||||
Pico.m.status |= ym2612_write_local(a, data, 1) & 1;
|
||||
ym2612_write_local(a, data, 1);
|
||||
}
|
||||
|
||||
static void z80_md_vdp_br_write(unsigned int a, unsigned char data)
|
||||
|
|
|
@ -70,7 +70,7 @@ extern void *p32x_bios_g, *p32x_bios_m, *p32x_bios_s;
|
|||
#define POPT_EN_DRC (1<<17)
|
||||
#define POPT_DIS_SPRITE_LIM (1<<18)
|
||||
#define POPT_DIS_IDLE_DET (1<<19)
|
||||
#define POPT_EN_32X (1<<20)
|
||||
#define POPT_EN_32X (1<<20) // x0 0000
|
||||
#define POPT_EN_PWM (1<<21)
|
||||
#define POPT_PWM_IRQ_OPT (1<<22)
|
||||
|
||||
|
|
|
@ -88,7 +88,6 @@ static void do_timing_hacks_vb(void)
|
|||
static int PicoFrameHints(void)
|
||||
{
|
||||
struct PicoVideo *pv = &Pico.video;
|
||||
int line_sample = Pico.m.pal ? 68 : 93;
|
||||
int vdp_slots = (Pico.video.reg[12] & 1) ? 18 : 16;
|
||||
int lines, y, lines_vis, skip;
|
||||
int vcnt_wrap, vcnt_adj;
|
||||
|
@ -150,23 +149,6 @@ static int PicoFrameHints(void)
|
|||
}
|
||||
}
|
||||
|
||||
// get samples from sound chips
|
||||
if ((y == 224 || y == line_sample) && PicoIn.sndOut)
|
||||
{
|
||||
cycles = SekCyclesDone();
|
||||
|
||||
if (Pico.m.z80Run && !Pico.m.z80_reset && (PicoIn.opt&POPT_EN_Z80))
|
||||
PicoSyncZ80(cycles);
|
||||
#ifdef PICO_CD
|
||||
if (PicoIn.AHW & PAHW_MCD)
|
||||
pcd_sync_s68k(cycles, 0);
|
||||
#endif
|
||||
#ifdef PICO_32X
|
||||
p32x_sync_sh2s(cycles);
|
||||
#endif
|
||||
PsndGetSamples(y);
|
||||
}
|
||||
|
||||
// Run scanline:
|
||||
Pico.t.m68c_line_start = Pico.t.m68c_aim;
|
||||
do_timing_hacks_as(pv, vdp_slots);
|
||||
|
@ -238,10 +220,6 @@ static int PicoFrameHints(void)
|
|||
p32x_start_blank();
|
||||
#endif
|
||||
|
||||
// get samples from sound chips
|
||||
if (y == 224 && PicoIn.sndOut)
|
||||
PsndGetSamples(y);
|
||||
|
||||
// Run scanline:
|
||||
CPUS_RUN(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG);
|
||||
|
||||
|
@ -298,7 +276,7 @@ static int PicoFrameHints(void)
|
|||
pv->status |= ((pv->reg[1] >> 3) ^ SR_VB) & SR_VB; // forced blanking
|
||||
|
||||
// last scanline
|
||||
Pico.m.scanline = y;
|
||||
Pico.m.scanline = y++;
|
||||
pv->v_counter = 0xff;
|
||||
pv->lwrite_cnt = 0;
|
||||
|
||||
|
@ -337,6 +315,11 @@ static int PicoFrameHints(void)
|
|||
#ifdef PICO_32X
|
||||
p32x_sync_sh2s(cycles);
|
||||
#endif
|
||||
|
||||
// get samples from sound chips
|
||||
if (PicoIn.sndOut)
|
||||
PsndGetSamples(y);
|
||||
|
||||
timers_cycle();
|
||||
|
||||
pv->hint_cnt = hint;
|
||||
|
|
|
@ -336,7 +336,7 @@ struct PicoMisc
|
|||
unsigned char eeprom_cycle; // EEPROM cycle number
|
||||
unsigned char eeprom_slave; // EEPROM slave word for X24C02 and better SRAMs
|
||||
unsigned char eeprom_status;
|
||||
unsigned char status; // rapid_ym2612, multi_ym_updates
|
||||
unsigned char pad1; // was ym2612 status
|
||||
unsigned short dma_xfers; // 18
|
||||
unsigned char eeprom_wb[2]; // EEPROM latch/write buffer
|
||||
unsigned int frame_count; // 1c for movies and idle det
|
||||
|
@ -433,6 +433,8 @@ struct PicoSound
|
|||
int len_e_cnt;
|
||||
short dac_line;
|
||||
short psg_line;
|
||||
unsigned int fm_mult; // samples per line in Q16
|
||||
unsigned int fm_pos; // last FM position in Q16
|
||||
};
|
||||
|
||||
// run tools/mkoffsets pico/pico_int_offs.h if you change these
|
||||
|
@ -872,9 +874,10 @@ PICO_INTERNAL void PsndReset(void);
|
|||
PICO_INTERNAL void PsndStartFrame(void);
|
||||
PICO_INTERNAL void PsndDoDAC(int line_to);
|
||||
PICO_INTERNAL void PsndDoPSG(int line_to);
|
||||
PICO_INTERNAL void PsndDoFM(int line_to);
|
||||
PICO_INTERNAL void PsndClear(void);
|
||||
PICO_INTERNAL void PsndGetSamples(int y);
|
||||
PICO_INTERNAL void PsndGetSamplesMS(void);
|
||||
PICO_INTERNAL void PsndGetSamplesMS(int y);
|
||||
|
||||
// sms.c
|
||||
#ifndef NO_SMS
|
||||
|
|
|
@ -320,16 +320,12 @@ void PicoFrameMS(void)
|
|||
}
|
||||
}
|
||||
|
||||
// 224 because of how it's done for MD...
|
||||
if (y == 224 && PicoIn.sndOut)
|
||||
PsndGetSamplesMS();
|
||||
|
||||
cycles_aim += cycles_line;
|
||||
cycles_done += z80_run((cycles_aim - cycles_done) >> 8) << 8;
|
||||
}
|
||||
|
||||
if (PicoIn.sndOut && Pico.snd.psg_line < lines)
|
||||
PsndDoPSG(lines - 1);
|
||||
if (PicoIn.sndOut)
|
||||
PsndGetSamplesMS(lines);
|
||||
}
|
||||
|
||||
void PicoFrameDrawOnlyMS(void)
|
||||
|
|
|
@ -12,16 +12,15 @@
|
|||
#define MINOUT (-32768)
|
||||
|
||||
/* limitter */
|
||||
#define Limit16(val) { \
|
||||
val -= (val >> 2); \
|
||||
if ((short)val != val) val = (val < 0 ? MINOUT : MAXOUT); \
|
||||
}
|
||||
#define Limit16(val) \
|
||||
if ((short)val != val) val = (val < 0 ? MINOUT : MAXOUT)
|
||||
|
||||
int mix_32_to_16l_level;
|
||||
|
||||
static struct iir2 { // 2-pole IIR
|
||||
int x[2]; // sample buffer
|
||||
int y[2]; // filter intermediates
|
||||
int i;
|
||||
} lfi2, rfi2;
|
||||
|
||||
// NB ">>" rounds to -infinity, "/" to 0. To compensate the effect possibly use
|
||||
|
|
|
@ -400,6 +400,8 @@ m32_16l_st_l_no_unal2:
|
|||
ldmfd sp!, {r4-r11,lr}
|
||||
bx lr
|
||||
|
||||
#endif /* __GP2X__ */
|
||||
|
||||
.global mix_reset @ void
|
||||
mix_reset:
|
||||
ldr r0, =filter
|
||||
|
@ -409,11 +411,7 @@ mix_reset:
|
|||
bx lr
|
||||
|
||||
.data
|
||||
DCfilt r4, r10
|
||||
DCfilt r5, r11
|
||||
filter:
|
||||
.ds 8
|
||||
|
||||
#endif /* __GP2X__ */
|
||||
|
||||
@ vim:filetype=armasm
|
||||
|
|
|
@ -32,52 +32,17 @@ extern int *sn76496_regs;
|
|||
static void dac_recalculate(void)
|
||||
{
|
||||
int lines = Pico.m.pal ? 313 : 262;
|
||||
int mid = Pico.m.pal ? 68 : 93;
|
||||
int i, dac_cnt, pos, len;
|
||||
int i, pos;
|
||||
|
||||
if (Pico.snd.len <= lines)
|
||||
{
|
||||
// shrinking algo
|
||||
dac_cnt = -Pico.snd.len;
|
||||
len=1; pos=0;
|
||||
dac_info[225] = 1;
|
||||
pos = 0; // Q16
|
||||
|
||||
for(i=226; i != 225; i++)
|
||||
{
|
||||
if (i >= lines) i = 0;
|
||||
if(dac_cnt < 0) {
|
||||
pos++;
|
||||
dac_cnt += lines;
|
||||
}
|
||||
dac_cnt -= Pico.snd.len;
|
||||
dac_info[i] = pos;
|
||||
}
|
||||
}
|
||||
else
|
||||
for(i = 0; i <= lines; i++)
|
||||
{
|
||||
// stretching
|
||||
dac_cnt = Pico.snd.len;
|
||||
pos=0;
|
||||
for(i = 225; i != 224; i++)
|
||||
{
|
||||
if (i >= lines) i = 0;
|
||||
len=0;
|
||||
while(dac_cnt >= 0) {
|
||||
dac_cnt -= lines;
|
||||
len++;
|
||||
}
|
||||
if (i == mid) // midpoint
|
||||
while(pos+len < Pico.snd.len/2) {
|
||||
dac_cnt -= lines;
|
||||
len++;
|
||||
}
|
||||
dac_cnt += Pico.snd.len;
|
||||
pos += len;
|
||||
dac_info[i] = pos;
|
||||
}
|
||||
dac_info[i] = ((pos+(1<<15)) >> 16); // round to nearest
|
||||
pos += Pico.snd.fm_mult;
|
||||
}
|
||||
for (i = lines; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)
|
||||
dac_info[i] = dac_info[0];
|
||||
for (i = lines+1; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)
|
||||
dac_info[i] = dac_info[i-1];
|
||||
}
|
||||
|
||||
|
||||
|
@ -95,6 +60,7 @@ void PsndRerate(int preserve_state)
|
|||
{
|
||||
void *state = NULL;
|
||||
int target_fps = Pico.m.pal ? 50 : 60;
|
||||
int target_lines = Pico.m.pal ? 313 : 262;
|
||||
|
||||
if (preserve_state) {
|
||||
state = malloc(0x204);
|
||||
|
@ -121,6 +87,9 @@ void PsndRerate(int preserve_state)
|
|||
Pico.snd.len_e_add = ((PicoIn.sndRate - Pico.snd.len * target_fps) << 16) / target_fps;
|
||||
Pico.snd.len_e_cnt = 0;
|
||||
|
||||
// samples per line
|
||||
Pico.snd.fm_mult = 65536.0 * PicoIn.sndRate / (target_fps*target_lines);
|
||||
|
||||
// recalculate dac info
|
||||
dac_recalculate();
|
||||
|
||||
|
@ -149,8 +118,7 @@ PICO_INTERNAL void PsndStartFrame(void)
|
|||
}
|
||||
|
||||
Pico.snd.dac_line = Pico.snd.psg_line = 0;
|
||||
Pico.m.status &= ~1;
|
||||
dac_info[224] = Pico.snd.len_use;
|
||||
Pico.snd.fm_pos = 0;
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PsndDoDAC(int line_to)
|
||||
|
@ -159,9 +127,6 @@ PICO_INTERNAL void PsndDoDAC(int line_to)
|
|||
int dout = ym2612.dacout;
|
||||
int line_from = Pico.snd.dac_line;
|
||||
|
||||
if (line_to >= 313)
|
||||
line_to = 312;
|
||||
|
||||
pos = dac_info[line_from];
|
||||
pos1 = dac_info[line_to + 1];
|
||||
len = pos1 - pos;
|
||||
|
@ -188,14 +153,9 @@ PICO_INTERNAL void PsndDoPSG(int line_to)
|
|||
int pos, pos1, len;
|
||||
int stereo = 0;
|
||||
|
||||
if (line_to >= 313)
|
||||
line_to = 312;
|
||||
|
||||
pos = dac_info[line_from];
|
||||
pos1 = dac_info[line_to + 1];
|
||||
len = pos1 - pos;
|
||||
//elprintf(EL_STATUS, "%3d %3d %3d %3d %3d",
|
||||
// pos, pos1, len, line_from, line_to);
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
|
@ -211,6 +171,34 @@ PICO_INTERNAL void PsndDoPSG(int line_to)
|
|||
SN76496Update(PicoIn.sndOut + pos, len, stereo);
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PsndDoFM(int line_to)
|
||||
{
|
||||
int pos, len;
|
||||
int stereo = 0;
|
||||
|
||||
// Q16, number of samples to fill in buffer
|
||||
len = ((line_to-1) * Pico.snd.fm_mult) - Pico.snd.fm_pos;
|
||||
|
||||
// don't do this too often (no more than 256 per sec)
|
||||
if (len >> 16 <= PicoIn.sndRate >> 9)
|
||||
return;
|
||||
|
||||
// update position and calculate buffer offset and length
|
||||
pos = Pico.snd.fm_pos >> 16;
|
||||
Pico.snd.fm_pos += len;
|
||||
len = (Pico.snd.fm_pos >> 16) - pos;
|
||||
|
||||
// fill buffer
|
||||
if (PicoIn.opt & POPT_EN_STEREO) {
|
||||
stereo = 1;
|
||||
pos <<= 1;
|
||||
}
|
||||
if (PicoIn.opt & POPT_EN_FM)
|
||||
YM2612UpdateOne(PsndBuffer + pos, len, stereo, 1);
|
||||
else
|
||||
memset32(PsndBuffer + pos, 0, len<<stereo);
|
||||
}
|
||||
|
||||
// cdda
|
||||
static void cdda_raw_update(int *buffer, int length)
|
||||
{
|
||||
|
@ -275,11 +263,12 @@ PICO_INTERNAL void PsndClear(void)
|
|||
|
||||
static int PsndRender(int offset, int length)
|
||||
{
|
||||
int buf32_updated = 0;
|
||||
int *buf32 = PsndBuffer+offset;
|
||||
int *buf32;
|
||||
int stereo = (PicoIn.opt & 8) >> 3;
|
||||
int fmlen = (Pico.snd.fm_pos >> 16) - offset;
|
||||
|
||||
offset <<= stereo;
|
||||
buf32 = PsndBuffer+offset;
|
||||
|
||||
pprof_start(sound);
|
||||
|
||||
|
@ -288,14 +277,15 @@ static int PsndRender(int offset, int length)
|
|||
return length;
|
||||
}
|
||||
|
||||
// Add in the stereo FM buffer
|
||||
if (PicoIn.opt & POPT_EN_FM) {
|
||||
buf32_updated = YM2612UpdateOne(buf32, length, stereo, 1);
|
||||
} else
|
||||
memset32(buf32, 0, length<<stereo);
|
||||
|
||||
//printf("active_chs: %02x\n", buf32_updated);
|
||||
(void)buf32_updated;
|
||||
// Add in parts of the FM buffer not yet done
|
||||
if (length-fmlen > 0) {
|
||||
int *fmbuf = buf32 + (fmlen << stereo);
|
||||
if (PicoIn.opt & POPT_EN_FM)
|
||||
YM2612UpdateOne(fmbuf, length-fmlen, stereo, 1);
|
||||
else
|
||||
memset32(fmbuf, 0, (length-fmlen)<<stereo);
|
||||
Pico.snd.fm_pos += (length-fmlen)<<16;
|
||||
}
|
||||
|
||||
// CD: PCM sound
|
||||
if (PicoIn.AHW & PAHW_MCD) {
|
||||
|
@ -327,7 +317,6 @@ static int PsndRender(int offset, int length)
|
|||
return length;
|
||||
}
|
||||
|
||||
// to be called on 224 or line_sample scanlines only
|
||||
PICO_INTERNAL void PsndGetSamples(int y)
|
||||
{
|
||||
static int curr_pos = 0;
|
||||
|
@ -336,33 +325,20 @@ PICO_INTERNAL void PsndGetSamples(int y)
|
|||
PsndDoDAC(y - 1);
|
||||
PsndDoPSG(y - 1);
|
||||
|
||||
if (y == 224)
|
||||
{
|
||||
if (Pico.m.status & 2)
|
||||
curr_pos += PsndRender(curr_pos, Pico.snd.len-Pico.snd.len/2);
|
||||
else curr_pos = PsndRender(0, Pico.snd.len_use);
|
||||
if (Pico.m.status & 1)
|
||||
Pico.m.status |= 2;
|
||||
else Pico.m.status &= ~2;
|
||||
if (PicoIn.writeSound)
|
||||
PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));
|
||||
// clear sound buffer
|
||||
PsndClear();
|
||||
Pico.snd.dac_line = 224;
|
||||
dac_info[224] = 0;
|
||||
}
|
||||
else if (Pico.m.status & 3) {
|
||||
Pico.m.status |= 2;
|
||||
Pico.m.status &= ~1;
|
||||
curr_pos = PsndRender(0, Pico.snd.len/2);
|
||||
}
|
||||
curr_pos = PsndRender(0, Pico.snd.len_use);
|
||||
|
||||
if (PicoIn.writeSound)
|
||||
PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));
|
||||
// clear sound buffer
|
||||
PsndClear();
|
||||
Pico.snd.dac_line = y;
|
||||
}
|
||||
|
||||
PICO_INTERNAL void PsndGetSamplesMS(void)
|
||||
PICO_INTERNAL void PsndGetSamplesMS(int y)
|
||||
{
|
||||
int length = Pico.snd.len_use;
|
||||
|
||||
PsndDoPSG(223);
|
||||
PsndDoPSG(y - 1);
|
||||
|
||||
// upmix to "stereo" if needed
|
||||
if (PicoIn.opt & POPT_EN_STEREO) {
|
||||
|
@ -374,8 +350,6 @@ PICO_INTERNAL void PsndGetSamplesMS(void)
|
|||
if (PicoIn.writeSound != NULL)
|
||||
PicoIn.writeSound(length * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));
|
||||
PsndClear();
|
||||
|
||||
dac_info[224] = 0;
|
||||
}
|
||||
|
||||
// vim:shiftwidth=2:ts=2:expandtab
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
** document it ("proprietary") and tells to write 0 to SSG-EG control register.
|
||||
**
|
||||
** updated with fixes from mame 0.216 (file version 1.5.1) (kub)
|
||||
** SSG-EG readded from GenPlus (kub)
|
||||
** linear sample interpolation for chip to output rate adaption (kub)
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -174,16 +176,6 @@ void memset32(int *dest, int c, int count);
|
|||
|
||||
#define EG_TIMER_OVERFLOW (3*(1<<EG_SH)) /* envelope generator timer overflows every 3 samples (on real chip) */
|
||||
|
||||
#define MAXOUT (+32767)
|
||||
#define MINOUT (-32768)
|
||||
|
||||
/* limitter */
|
||||
#define Limit(val, max,min) { \
|
||||
if ( val > max ) val = max; \
|
||||
else if ( val < min ) val = min; \
|
||||
}
|
||||
|
||||
|
||||
/* TL_TAB_LEN is calculated as:
|
||||
* 13 - sinus amplitude bits (Y axis)
|
||||
* 2 - sinus sign bit (Y axis)
|
||||
|
@ -289,8 +281,8 @@ O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
|
|||
O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
|
||||
|
||||
/* rates 00-11 */
|
||||
O(18),O(18),O( 0),O( 0),
|
||||
O( 0),O( 0),O( 2),O( 2),
|
||||
O(18),O(18),O( 2),O( 3),
|
||||
O( 0),O( 1),O( 2),O( 3),
|
||||
O( 0),O( 1),O( 2),O( 3),
|
||||
O( 0),O( 1),O( 2),O( 3),
|
||||
O( 0),O( 1),O( 2),O( 3),
|
||||
|
@ -554,6 +546,13 @@ INLINE void set_timers( int v )
|
|||
ym2612.OPN.ST.status &= ~1;
|
||||
}
|
||||
|
||||
INLINE void recalc_volout(FM_SLOT *SLOT)
|
||||
{
|
||||
INT16 vol_out = SLOT->volume;
|
||||
if ((SLOT->ssg&0x0c) == 0x0c)
|
||||
vol_out = (0x200 - SLOT->volume) & MAX_ATT_INDEX;
|
||||
SLOT->vol_out = vol_out + SLOT->tl;
|
||||
}
|
||||
|
||||
INLINE void FM_KEYON(int c , int s )
|
||||
{
|
||||
|
@ -562,13 +561,15 @@ INLINE void FM_KEYON(int c , int s )
|
|||
{
|
||||
SLOT->key = 1;
|
||||
SLOT->phase = 0; /* restart Phase Generator */
|
||||
SLOT->ssg ^= SLOT->ssgn;
|
||||
SLOT->ssgn = 0;
|
||||
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
|
||||
if (SLOT->ar + SLOT->ksr < 32+62) {
|
||||
SLOT->state = (SLOT->volume > MIN_ATT_INDEX) ? EG_ATT :
|
||||
((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC);
|
||||
if (SLOT->volume > MIN_ATT_INDEX) SLOT->state = EG_ATT;
|
||||
} else {
|
||||
SLOT->volume = MIN_ATT_INDEX;
|
||||
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
|
||||
}
|
||||
recalc_volout(SLOT);
|
||||
ym2612.slot_mask |= (1<<s) << (c*4);
|
||||
}
|
||||
}
|
||||
|
@ -579,8 +580,18 @@ INLINE void FM_KEYOFF(int c , int s )
|
|||
if( SLOT->key )
|
||||
{
|
||||
SLOT->key = 0;
|
||||
if (SLOT->state>EG_REL)
|
||||
if (SLOT->state>EG_REL) {
|
||||
SLOT->state = EG_REL;/* phase -> Release */
|
||||
if (SLOT->ssg&0x08) {
|
||||
if (SLOT->ssg&0x04)
|
||||
SLOT->volume = (0x200 - SLOT->volume);
|
||||
if (SLOT->volume >= 0x200) {
|
||||
SLOT->volume = MAX_ATT_INDEX;
|
||||
SLOT->state = EG_OFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
SLOT->vol_out = SLOT->volume + SLOT->tl;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -597,12 +608,15 @@ INLINE void set_det_mul(FM_CH *CH, FM_SLOT *SLOT, int v)
|
|||
INLINE void set_tl(FM_SLOT *SLOT, int v)
|
||||
{
|
||||
SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
|
||||
if (SLOT->state > EG_REL)
|
||||
recalc_volout(SLOT);
|
||||
}
|
||||
|
||||
/* set attack rate & key scale */
|
||||
INLINE void set_ar_ksr(FM_CH *CH, FM_SLOT *SLOT, int v)
|
||||
{
|
||||
UINT8 old_KSR = SLOT->KSR;
|
||||
int eg_sh_ar, eg_sel_ar;
|
||||
|
||||
SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
|
||||
|
||||
|
@ -611,24 +625,20 @@ INLINE void set_ar_ksr(FM_CH *CH, FM_SLOT *SLOT, int v)
|
|||
{
|
||||
CH->SLOT[SLOT1].Incr=-1;
|
||||
}
|
||||
|
||||
/* refresh Attack rate */
|
||||
if ((SLOT->ar + SLOT->ksr) < 32+62)
|
||||
{
|
||||
eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
|
||||
eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
|
||||
}
|
||||
else
|
||||
{
|
||||
int eg_sh_ar, eg_sel_ar;
|
||||
|
||||
/* refresh Attack rate */
|
||||
if ((SLOT->ar + SLOT->ksr) < 32+62)
|
||||
{
|
||||
eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
|
||||
eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
|
||||
}
|
||||
else
|
||||
{
|
||||
eg_sh_ar = 0;
|
||||
eg_sel_ar = 18;
|
||||
}
|
||||
|
||||
SLOT->eg_pack_ar = eg_inc_pack[eg_sel_ar] | (eg_sh_ar<<24);
|
||||
eg_sh_ar = 0;
|
||||
eg_sel_ar = 18;
|
||||
}
|
||||
|
||||
SLOT->eg_pack_ar = eg_inc_pack[eg_sel_ar] | (eg_sh_ar<<24);
|
||||
}
|
||||
|
||||
/* set decay rate */
|
||||
|
@ -750,7 +760,7 @@ INLINE int advance_lfo(int lfo_ampm, UINT32 lfo_cnt_old, UINT32 lfo_cnt)
|
|||
return lfo_ampm;
|
||||
}
|
||||
|
||||
INLINE void update_eg_phase(UINT16 *vol_out, FM_SLOT *SLOT, UINT32 eg_cnt)
|
||||
INLINE void update_eg_phase(FM_SLOT *SLOT, UINT32 eg_cnt)
|
||||
{
|
||||
INT32 volume = SLOT->volume;
|
||||
UINT32 pack = SLOT->eg_pack[SLOT->state - 1];
|
||||
|
@ -763,44 +773,113 @@ INLINE void update_eg_phase(UINT16 *vol_out, FM_SLOT *SLOT, UINT32 eg_cnt)
|
|||
eg_inc_val = pack >> ((eg_cnt >> shift) & 7) * 3;
|
||||
eg_inc_val = (1 << (eg_inc_val & 7)) >> 1;
|
||||
|
||||
switch (SLOT->state)
|
||||
{
|
||||
case EG_ATT: /* attack phase */
|
||||
volume += ( ~volume * eg_inc_val ) >> 4;
|
||||
if ( volume <= MIN_ATT_INDEX )
|
||||
if (SLOT->ssg&0x08) {
|
||||
switch (SLOT->state)
|
||||
{
|
||||
volume = MIN_ATT_INDEX;
|
||||
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS: EG_DEC;
|
||||
case EG_ATT: /* attack phase */
|
||||
volume += ( ~volume * eg_inc_val ) >> 4;
|
||||
if ( volume <= MIN_ATT_INDEX )
|
||||
{
|
||||
volume = MIN_ATT_INDEX;
|
||||
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS: EG_DEC;
|
||||
}
|
||||
break;
|
||||
|
||||
case EG_DEC: /* decay phase */
|
||||
if (volume < 0x200)
|
||||
volume += 4*eg_inc_val;
|
||||
if ( volume >= (INT32) SLOT->sl )
|
||||
SLOT->state = EG_SUS;
|
||||
break;
|
||||
|
||||
case EG_SUS: /* sustain phase */
|
||||
if (volume < 0x200)
|
||||
volume += 4*eg_inc_val;
|
||||
break;
|
||||
|
||||
case EG_REL: /* release phase */
|
||||
if (volume < 0x200)
|
||||
volume += 4*eg_inc_val;
|
||||
if ( volume >= 0x200 )
|
||||
{
|
||||
volume = MAX_ATT_INDEX;
|
||||
SLOT->state = EG_OFF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case EG_DEC: /* decay phase */
|
||||
volume += eg_inc_val;
|
||||
if ( volume >= (INT32) SLOT->sl )
|
||||
SLOT->state = EG_SUS;
|
||||
break;
|
||||
|
||||
case EG_SUS: /* sustain phase */
|
||||
volume += eg_inc_val;
|
||||
if ( volume >= MAX_ATT_INDEX )
|
||||
SLOT->vol_out = volume + SLOT->tl;
|
||||
if ((SLOT->ssg&0x04) && (SLOT->state > EG_REL))
|
||||
SLOT->vol_out = ((0x200 - volume) & MAX_ATT_INDEX) + SLOT->tl;
|
||||
} else {
|
||||
switch (SLOT->state)
|
||||
{
|
||||
volume = MAX_ATT_INDEX;
|
||||
/* do not change SLOT->state (verified on real chip) */
|
||||
}
|
||||
break;
|
||||
case EG_ATT: /* attack phase */
|
||||
volume += ( ~volume * eg_inc_val ) >> 4;
|
||||
if ( volume <= MIN_ATT_INDEX )
|
||||
{
|
||||
volume = MIN_ATT_INDEX;
|
||||
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS: EG_DEC;
|
||||
}
|
||||
break;
|
||||
|
||||
case EG_REL: /* release phase */
|
||||
volume += eg_inc_val;
|
||||
if ( volume >= MAX_ATT_INDEX )
|
||||
{
|
||||
volume = MAX_ATT_INDEX;
|
||||
SLOT->state = EG_OFF;
|
||||
case EG_DEC: /* decay phase */
|
||||
volume += eg_inc_val;
|
||||
if ( volume >= (INT32) SLOT->sl )
|
||||
SLOT->state = EG_SUS;
|
||||
break;
|
||||
|
||||
case EG_SUS: /* sustain phase */
|
||||
volume += eg_inc_val;
|
||||
if ( volume >= MAX_ATT_INDEX )
|
||||
{
|
||||
volume = MAX_ATT_INDEX;
|
||||
/* do not change SLOT->state (verified on real chip) */
|
||||
}
|
||||
break;
|
||||
|
||||
case EG_REL: /* release phase */
|
||||
volume += eg_inc_val;
|
||||
if ( volume >= MAX_ATT_INDEX )
|
||||
{
|
||||
volume = MAX_ATT_INDEX;
|
||||
SLOT->state = EG_OFF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
SLOT->vol_out = volume + SLOT->tl;
|
||||
}
|
||||
|
||||
SLOT->volume = volume;
|
||||
*vol_out = SLOT->tl + volume; /* tl is 7bit<<3, volume 0-1023 (0-2039 total) */
|
||||
}
|
||||
|
||||
INLINE void update_ssg_eg_phase(FM_SLOT *SLOT)
|
||||
{
|
||||
if (SLOT->ssg&0x01) {
|
||||
if (SLOT->ssg&0x02) {
|
||||
SLOT->ssg ^= SLOT->ssgn ^ 4;
|
||||
SLOT->ssgn = 4;
|
||||
}
|
||||
|
||||
if (SLOT->state != EG_ATT && !(SLOT->ssg&0x04))
|
||||
SLOT->volume = MAX_ATT_INDEX;
|
||||
} else {
|
||||
if (SLOT->ssg&0x02) {
|
||||
SLOT->ssg ^= 4;
|
||||
SLOT->ssgn ^= 4;
|
||||
} else
|
||||
SLOT->phase = 0;
|
||||
|
||||
if (SLOT->state != EG_ATT) {
|
||||
SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
|
||||
if (SLOT->ar + SLOT->ksr < 32+62) {
|
||||
if (SLOT->volume > MIN_ATT_INDEX) SLOT->state = EG_ATT;
|
||||
} else {
|
||||
SLOT->volume = MIN_ATT_INDEX;
|
||||
}
|
||||
}
|
||||
}
|
||||
recalc_volout(SLOT);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -846,6 +925,16 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
|
|||
{
|
||||
int smp = 0; /* produced sample */
|
||||
unsigned int eg_out, eg_out2, eg_out4;
|
||||
FM_SLOT *SLOT;
|
||||
|
||||
SLOT = &ct->CH->SLOT[SLOT1];
|
||||
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200) update_ssg_eg_phase(SLOT);
|
||||
SLOT = &ct->CH->SLOT[SLOT2];
|
||||
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200) update_ssg_eg_phase(SLOT);
|
||||
SLOT = &ct->CH->SLOT[SLOT3];
|
||||
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200) update_ssg_eg_phase(SLOT);
|
||||
SLOT = &ct->CH->SLOT[SLOT4];
|
||||
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200) update_ssg_eg_phase(SLOT);
|
||||
|
||||
if (ct->pack & 8) { /* LFO enabled ? (test Earthworm Jim in between demo 1 and 2) */
|
||||
ct->pack = (ct->pack&0xffff) | (advance_lfo(ct->pack >> 16, ct->lfo_cnt, ct->lfo_cnt + ct->lfo_inc) << 16);
|
||||
|
@ -857,12 +946,58 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
|
|||
{
|
||||
ct->eg_timer -= EG_TIMER_OVERFLOW;
|
||||
ct->eg_cnt++;
|
||||
if (ct->eg_cnt >= 4096) ct->eg_cnt = 1;
|
||||
|
||||
if (ct->CH->SLOT[SLOT1].state != EG_OFF) update_eg_phase(&ct->vol_out1, &ct->CH->SLOT[SLOT1], ct->eg_cnt);
|
||||
if (ct->CH->SLOT[SLOT2].state != EG_OFF) update_eg_phase(&ct->vol_out2, &ct->CH->SLOT[SLOT2], ct->eg_cnt);
|
||||
if (ct->CH->SLOT[SLOT3].state != EG_OFF) update_eg_phase(&ct->vol_out3, &ct->CH->SLOT[SLOT3], ct->eg_cnt);
|
||||
if (ct->CH->SLOT[SLOT4].state != EG_OFF) update_eg_phase(&ct->vol_out4, &ct->CH->SLOT[SLOT4], ct->eg_cnt);
|
||||
SLOT = &ct->CH->SLOT[SLOT1];
|
||||
SLOT->vol_ipol = SLOT->vol_out;
|
||||
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt);
|
||||
SLOT = &ct->CH->SLOT[SLOT2];
|
||||
SLOT->vol_ipol = SLOT->vol_out;
|
||||
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt);
|
||||
SLOT = &ct->CH->SLOT[SLOT3];
|
||||
SLOT->vol_ipol = SLOT->vol_out;
|
||||
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt);
|
||||
SLOT = &ct->CH->SLOT[SLOT4];
|
||||
SLOT->vol_ipol = SLOT->vol_out;
|
||||
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt);
|
||||
}
|
||||
#if 0
|
||||
UINT32 ifrac0 = ct->eg_timer / (EG_TIMER_OVERFLOW>>EG_SH);
|
||||
UINT32 ifrac1 = (1<<EG_SH) - ifrac0;
|
||||
SLOT = &ct->CH->SLOT[SLOT1];
|
||||
ct->vol_out1 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;
|
||||
SLOT = &ct->CH->SLOT[SLOT2];
|
||||
ct->vol_out2 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;
|
||||
SLOT = &ct->CH->SLOT[SLOT3];
|
||||
ct->vol_out3 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;
|
||||
SLOT = &ct->CH->SLOT[SLOT4];
|
||||
ct->vol_out4 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;
|
||||
#else
|
||||
switch (ct->eg_timer >> EG_SH)
|
||||
{
|
||||
case 0:
|
||||
ct->vol_out1 = ct->CH->SLOT[SLOT1].vol_ipol;
|
||||
ct->vol_out2 = ct->CH->SLOT[SLOT2].vol_ipol;
|
||||
ct->vol_out3 = ct->CH->SLOT[SLOT3].vol_ipol;
|
||||
ct->vol_out4 = ct->CH->SLOT[SLOT4].vol_ipol;
|
||||
break;
|
||||
case (EG_TIMER_OVERFLOW>>EG_SH)-1:
|
||||
ct->vol_out1 = ct->CH->SLOT[SLOT1].vol_out;
|
||||
ct->vol_out2 = ct->CH->SLOT[SLOT2].vol_out;
|
||||
ct->vol_out3 = ct->CH->SLOT[SLOT3].vol_out;
|
||||
ct->vol_out4 = ct->CH->SLOT[SLOT4].vol_out;
|
||||
break;
|
||||
default:
|
||||
ct->vol_out1 = (ct->CH->SLOT[SLOT1].vol_ipol +
|
||||
ct->CH->SLOT[SLOT1].vol_out) >> 1;
|
||||
ct->vol_out2 = (ct->CH->SLOT[SLOT2].vol_ipol +
|
||||
ct->CH->SLOT[SLOT2].vol_out) >> 1;
|
||||
ct->vol_out3 = (ct->CH->SLOT[SLOT3].vol_ipol +
|
||||
ct->CH->SLOT[SLOT3].vol_out) >> 1;
|
||||
ct->vol_out4 = (ct->CH->SLOT[SLOT4].vol_ipol +
|
||||
ct->CH->SLOT[SLOT4].vol_out) >> 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ct->pack & 4) continue; /* output disabled */
|
||||
|
||||
|
@ -892,7 +1027,7 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
|
|||
if (ct->pack & (1<<(SLOT4+8))) eg_out4 += add;
|
||||
}
|
||||
|
||||
switch( ct->CH->ALGO )
|
||||
switch( ct->algo&0x7 )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
|
@ -1086,6 +1221,33 @@ static void chan_render_finish(void)
|
|||
ym2612.OPN.lfo_cnt = crct.lfo_cnt;
|
||||
}
|
||||
|
||||
static UINT32 update_lfo_phase(FM_SLOT *SLOT, UINT32 block_fnum)
|
||||
{
|
||||
UINT32 fnum_lfo;
|
||||
INT32 lfo_fn_table_index_offset;
|
||||
UINT8 blk;
|
||||
UINT32 fn;
|
||||
int fc,fdt;
|
||||
|
||||
fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
|
||||
lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + crct.CH->pms + ((crct.pack>>16)&0xff) ];
|
||||
if (lfo_fn_table_index_offset) /* LFO phase modulation active */
|
||||
{
|
||||
block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
|
||||
blk = (block_fnum&0x7000) >> 12;
|
||||
fn = block_fnum & 0xfff;
|
||||
|
||||
/* phase increment counter */
|
||||
fc = (fn_table[fn]>>(7-blk));
|
||||
|
||||
fdt = fc + SLOT->DT[crct.CH->kcode];
|
||||
if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;
|
||||
|
||||
return (fdt * SLOT->mul) >> 1;
|
||||
} else
|
||||
return SLOT->Incr;
|
||||
}
|
||||
|
||||
static int chan_render(int *buffer, int length, int c, UINT32 flags) // flags: stereo, ?, disabled, ?, pan_r, pan_l
|
||||
{
|
||||
crct.CH = &ym2612.CH[c];
|
||||
|
@ -1114,58 +1276,22 @@ static int chan_render(int *buffer, int length, int c, UINT32 flags) // flags: s
|
|||
crct.phase3 = crct.CH->SLOT[SLOT3].phase;
|
||||
crct.phase4 = crct.CH->SLOT[SLOT4].phase;
|
||||
|
||||
/* current output from EG circuit (without AM from LFO) */
|
||||
crct.vol_out1 = crct.CH->SLOT[SLOT1].tl + ((UINT32)crct.CH->SLOT[SLOT1].volume);
|
||||
crct.vol_out2 = crct.CH->SLOT[SLOT2].tl + ((UINT32)crct.CH->SLOT[SLOT2].volume);
|
||||
crct.vol_out3 = crct.CH->SLOT[SLOT3].tl + ((UINT32)crct.CH->SLOT[SLOT3].volume);
|
||||
crct.vol_out4 = crct.CH->SLOT[SLOT4].tl + ((UINT32)crct.CH->SLOT[SLOT4].volume);
|
||||
|
||||
crct.op1_out = crct.CH->op1_out;
|
||||
crct.algo = crct.CH->ALGO & 7;
|
||||
|
||||
if(crct.CH->pms)
|
||||
if(crct.CH->pms && (ym2612.OPN.ST.mode & 0xC0) && c == 2) {
|
||||
/* 3 slot mode */
|
||||
crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], ym2612.OPN.SL3.block_fnum[1]);
|
||||
crct.incr2 = update_lfo_phase(&crct.CH->SLOT[SLOT2], ym2612.OPN.SL3.block_fnum[2]);
|
||||
crct.incr3 = update_lfo_phase(&crct.CH->SLOT[SLOT3], ym2612.OPN.SL3.block_fnum[0]);
|
||||
crct.incr4 = update_lfo_phase(&crct.CH->SLOT[SLOT4], crct.CH->block_fnum);
|
||||
}
|
||||
else if(crct.CH->pms)
|
||||
{
|
||||
/* add support for 3 slot mode */
|
||||
UINT32 block_fnum = crct.CH->block_fnum;
|
||||
|
||||
UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
|
||||
INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + crct.CH->pms + ((crct.pack>>16)&0xff) ];
|
||||
|
||||
if (lfo_fn_table_index_offset) /* LFO phase modulation active */
|
||||
{
|
||||
UINT8 blk;
|
||||
UINT32 fn;
|
||||
int kc,fc,fdt;
|
||||
|
||||
block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
|
||||
blk = (block_fnum&0x7000) >> 12;
|
||||
fn = block_fnum & 0xfff;
|
||||
|
||||
/* keyscale code */
|
||||
kc = (blk<<2) | opn_fktable[(fn >> 7) & 0xf];
|
||||
/* phase increment counter */
|
||||
fc = (fn_table[fn]>>(7-blk));
|
||||
|
||||
fdt = fc + crct.CH->SLOT[SLOT1].DT[kc];
|
||||
if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;
|
||||
crct.incr1 = (fdt*crct.CH->SLOT[SLOT1].mul) >> 1;
|
||||
fdt = fc + crct.CH->SLOT[SLOT2].DT[kc];
|
||||
if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;
|
||||
crct.incr2 = (fdt*crct.CH->SLOT[SLOT2].mul) >> 1;
|
||||
fdt = fc + crct.CH->SLOT[SLOT3].DT[kc];
|
||||
if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;
|
||||
crct.incr3 = (fdt*crct.CH->SLOT[SLOT3].mul) >> 1;
|
||||
fdt = fc + crct.CH->SLOT[SLOT4].DT[kc];
|
||||
if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;
|
||||
crct.incr4 = (fdt*crct.CH->SLOT[SLOT4].mul) >> 1;
|
||||
}
|
||||
else /* LFO phase modulation = zero */
|
||||
{
|
||||
crct.incr1 = crct.CH->SLOT[SLOT1].Incr;
|
||||
crct.incr2 = crct.CH->SLOT[SLOT2].Incr;
|
||||
crct.incr3 = crct.CH->SLOT[SLOT3].Incr;
|
||||
crct.incr4 = crct.CH->SLOT[SLOT4].Incr;
|
||||
}
|
||||
crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], crct.CH->block_fnum);
|
||||
crct.incr2 = update_lfo_phase(&crct.CH->SLOT[SLOT2], crct.CH->block_fnum);
|
||||
crct.incr3 = update_lfo_phase(&crct.CH->SLOT[SLOT3], crct.CH->block_fnum);
|
||||
crct.incr4 = update_lfo_phase(&crct.CH->SLOT[SLOT4], crct.CH->block_fnum);
|
||||
}
|
||||
else /* no LFO phase modulation */
|
||||
{
|
||||
|
@ -1297,8 +1423,13 @@ static void reset_channels(FM_CH *CH)
|
|||
CH[c].fc = 0;
|
||||
for(s = 0 ; s < 4 ; s++ )
|
||||
{
|
||||
CH[c].SLOT[s].Incr = -1;
|
||||
CH[c].SLOT[s].key = 0;
|
||||
CH[c].SLOT[s].phase = 0;
|
||||
CH[c].SLOT[s].ssg = CH[c].SLOT[s].ssgn = 0;
|
||||
CH[c].SLOT[s].state= EG_OFF;
|
||||
CH[c].SLOT[s].volume = MAX_ATT_INDEX;
|
||||
CH[c].SLOT[s].vol_out = MAX_ATT_INDEX;
|
||||
}
|
||||
CH[c].mem_value = CH[c].op1_out = 0;
|
||||
}
|
||||
|
@ -1503,8 +1634,10 @@ static int OPNWriteReg(int r, int v)
|
|||
break;
|
||||
|
||||
case 0x90: /* SSG-EG */
|
||||
// removed.
|
||||
ret = 0;
|
||||
SLOT->ssg = v&0x0f;
|
||||
SLOT->ssg ^= SLOT->ssgn;
|
||||
if (SLOT->state > EG_REL)
|
||||
recalc_volout(SLOT);
|
||||
break;
|
||||
|
||||
case 0xa0:
|
||||
|
|
|
@ -53,6 +53,11 @@ typedef struct
|
|||
};
|
||||
UINT32 eg_pack[4];
|
||||
};
|
||||
|
||||
UINT8 ssg; /* 0x30 SSG-EG waveform */
|
||||
UINT8 ssgn;
|
||||
UINT16 vol_out; /* 0x32 current output from EG (without LFO) */
|
||||
UINT16 vol_ipol; /* 0x34 interpolator memory */
|
||||
} FM_SLOT;
|
||||
|
||||
|
||||
|
@ -176,21 +181,22 @@ int YM2612PicoStateLoad2(int *tat, int *tbt);
|
|||
#else
|
||||
/* GP2X specific */
|
||||
#include "../../platform/gp2x/940ctl.h"
|
||||
#define YM2612Init(baseclock,rate) { \
|
||||
#define YM2612Init(baseclock,rate) do { \
|
||||
if (PicoIn.opt&POPT_EXT_FM) YM2612Init_940(baseclock, rate); \
|
||||
else YM2612Init_(baseclock, rate); \
|
||||
}
|
||||
#define YM2612ResetChip() { \
|
||||
} while (0)
|
||||
#define YM2612ResetChip() do { \
|
||||
if (PicoIn.opt&POPT_EXT_FM) YM2612ResetChip_940(); \
|
||||
else YM2612ResetChip_(); \
|
||||
}
|
||||
#define YM2612UpdateOne(buffer,length,stereo,is_buf_empty) \
|
||||
} while (0)
|
||||
#define YM2612UpdateOne(buffer,length,stereo,is_buf_empty) do { \
|
||||
(PicoIn.opt&POPT_EXT_FM) ? YM2612UpdateOne_940(buffer, length, stereo, is_buf_empty) : \
|
||||
YM2612UpdateOne_(buffer, length, stereo, is_buf_empty);
|
||||
#define YM2612PicoStateLoad() { \
|
||||
YM2612UpdateOne_(buffer, length, stereo, is_buf_empty); \
|
||||
} while (0)
|
||||
#define YM2612PicoStateLoad() do { \
|
||||
if (PicoIn.opt&POPT_EXT_FM) YM2612PicoStateLoad_940(); \
|
||||
else YM2612PicoStateLoad_(); \
|
||||
}
|
||||
} while (0)
|
||||
#endif /* __GP2X__ */
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* PicoDrive
|
||||
* (C) notaz, 2006
|
||||
* (C) kub, 2020 added SSG-EG and simple output rate interpolation
|
||||
*
|
||||
* This work is licensed under the terms of MAME license.
|
||||
* See COPYING file in the top-level directory.
|
||||
|
@ -18,7 +19,7 @@
|
|||
.equiv SLOT2, 2
|
||||
.equiv SLOT3, 1
|
||||
.equiv SLOT4, 3
|
||||
.equiv SLOT_STRUCT_SIZE, 0x30
|
||||
.equiv SLOT_STRUCT_SIZE, 0x38
|
||||
|
||||
.equiv TL_TAB_LEN, 0x1A00
|
||||
|
||||
|
@ -28,11 +29,11 @@
|
|||
.equiv EG_REL, 1
|
||||
.equiv EG_OFF, 0
|
||||
|
||||
.equiv EG_SH, 16 @ 16.16 fixed point (envelope generator timing)
|
||||
.equiv EG_SH, 16 @ 16.16 fixed point (envelope generator timing)
|
||||
.equiv EG_TIMER_OVERFLOW, (3*(1<<EG_SH)) @ envelope generator timer overflows every 3 samples (on real chip)
|
||||
.equiv LFO_SH, 24 /* 8.24 fixed point (LFO calculations) */
|
||||
|
||||
.equiv ENV_QUIET, (2*13*256/8)
|
||||
.equiv ENV_QUIET, (2*13*256/8)
|
||||
|
||||
.text
|
||||
.align 2
|
||||
|
@ -41,8 +42,10 @@
|
|||
@ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
|
||||
@ writes output to routp, but only if vol_out changes
|
||||
.macro update_eg_phase_slot slot
|
||||
ldrh r0, [r5,#0x32] @ vol_out
|
||||
ldrb r2, [r5,#0x17] @ state
|
||||
add r3, r5, #0x1c
|
||||
strh r0, [r5,#0x34] @ vol_ipol
|
||||
tst r2, r2
|
||||
beq 0f @ EG_OFF
|
||||
|
||||
|
@ -60,11 +63,17 @@
|
|||
add r3, r3, r3, lsl #1
|
||||
mov r3, r2, lsr r3
|
||||
and r3, r3, #7 @ eg_inc_val shift, may be 0
|
||||
ldrb r0, [r5,#0x30] @ ssg
|
||||
ldrb r2, [r5,#0x17] @ state
|
||||
ldrh r0, [r5,#0x1a] @ volume, unsigned (0-1023)
|
||||
|
||||
tst r0, #0x08 @ ssg enabled?
|
||||
bne 9f
|
||||
|
||||
@ non-SSG-EG mode
|
||||
cmp r2, #4 @ EG_ATT
|
||||
ldrh r0, [r5,#0x1a] @ volume, unsigned (0-1023)
|
||||
beq 4f
|
||||
|
||||
cmp r2, #2
|
||||
mov r2, #1
|
||||
mov r2, r2, lsl r3
|
||||
|
@ -82,10 +91,9 @@
|
|||
|
||||
4: @ EG_ATT
|
||||
subs r3, r3, #1 @ eg_inc_val_shift - 1
|
||||
mov r2, #0
|
||||
mvnpl r2, r0
|
||||
mov r2, r2, lsl r3
|
||||
add r0, r0, r2, asr #4
|
||||
movpl r2, r2, lsl r3
|
||||
addpl r0, r0, r2, asr #4
|
||||
cmp r0, #0 @ if (volume <= MIN_ATT_INDEX)
|
||||
bgt 10f
|
||||
ldr r2, [r5,#0x1c]
|
||||
|
@ -112,24 +120,81 @@
|
|||
strgeb r3, [r5,#0x17] @ state
|
||||
|
||||
10: @ finish
|
||||
ldrh r3, [r5,#0x18] @ tl
|
||||
strh r0, [r5,#0x1a] @ volume
|
||||
b 11f
|
||||
|
||||
9: @ SSG-EG mode
|
||||
cmp r2, #4 @ EG_ATT
|
||||
ldrh r0, [r5,#0x1a] @ volume, unsigned (0-1023)
|
||||
beq 4f
|
||||
|
||||
cmp r0, #0x200 @ if ( volume < 0x200 )
|
||||
movlt r0, #1
|
||||
movlt r3, r0, lsl r3
|
||||
ldrlth r0, [r5,#0x1a] @ volume, unsigned (0-1023)
|
||||
movlt r3, r3, lsr #1 @ eg_inc_val
|
||||
addlt r0, r0, r3, lsr #2
|
||||
|
||||
cmp r2, #2
|
||||
blt 1f @ EG_REL
|
||||
beq 10f @ EG_SUS - nothing more to do
|
||||
|
||||
3: @ EG_DEC
|
||||
ldr r2, [r5,#0x1c] @ sl (can be 16bit?)
|
||||
mov r3, #EG_SUS
|
||||
cmp r0, r2 @ if ( volume >= (INT32) SLOT->sl )
|
||||
strgeb r3, [r5,#0x17] @ state
|
||||
b 10f
|
||||
|
||||
4: @ EG_ATT
|
||||
subs r3, r3, #1 @ eg_inc_val_shift - 1
|
||||
mvnpl r2, r0
|
||||
movpl r2, r2, lsl r3
|
||||
addpl r0, r0, r2, asr #4
|
||||
cmp r0, #0 @ if (volume <= MIN_ATT_INDEX)
|
||||
bgt 10f
|
||||
ldr r2, [r5,#0x1c]
|
||||
mov r0, #0
|
||||
cmp r2, #0
|
||||
movne r3, #EG_DEC
|
||||
moveq r3, #EG_SUS
|
||||
strb r3, [r5,#0x17] @ state
|
||||
b 10f
|
||||
|
||||
1: @ EG_REL
|
||||
mov r2, #0x200
|
||||
cmp r0, r2 @ if ( volume >= 0x200 )
|
||||
movge r0, #1024
|
||||
subge r0, #1
|
||||
movge r3, #EG_OFF
|
||||
strgeb r3, [r5,#0x17] @ state
|
||||
|
||||
10: @ finish
|
||||
strh r0, [r5,#0x1a] @ volume
|
||||
ldrb r2, [r5,#0x30] @ ssg
|
||||
ldrb r3, [r5,#0x17] @ state
|
||||
cmp r2, #0x0c @ if ( ssg&0x04 && state > EG_REL )
|
||||
cmpge r3, #EG_REL+1
|
||||
rsbge r0, r0, #0x200 @ volume = (0x200-volume) & MAX_ATT
|
||||
lslge r0, r0, #10
|
||||
lsrge r0, r0, #10
|
||||
|
||||
11:
|
||||
ldrh r3, [r5,#0x18] @ tl
|
||||
add r0, r0, r3 @ volume += tl
|
||||
strh r0, [r5,#0x32] @ vol_out
|
||||
.if \slot == SLOT1
|
||||
mov r6, r6, lsr #16
|
||||
add r0, r0, r3
|
||||
orr r6, r0, r6, lsl #16
|
||||
.elseif \slot == SLOT2
|
||||
mov r6, r6, lsl #16
|
||||
add r0, r0, r3
|
||||
mov r0, r0, lsl #16
|
||||
orr r6, r0, r6, lsr #16
|
||||
.elseif \slot == SLOT3
|
||||
mov r7, r7, lsr #16
|
||||
add r0, r0, r3
|
||||
orr r7, r0, r7, lsl #16
|
||||
.elseif \slot == SLOT4
|
||||
mov r7, r7, lsl #16
|
||||
add r0, r0, r3
|
||||
mov r0, r0, lsl #16
|
||||
orr r7, r0, r7, lsr #16
|
||||
.endif
|
||||
|
@ -137,6 +202,63 @@
|
|||
0: @ EG_OFF
|
||||
.endm
|
||||
|
||||
@ r5=slot, trashes: r0,r2,r3
|
||||
.macro update_ssg_eg
|
||||
ldrh r0, [r5,#0x30] @ ssg+ssgn
|
||||
ldrb r2, [r5,#0x17] @ state
|
||||
ldrh r3, [r5,#0x1a] @ volume
|
||||
tst r0, #0x08 @ ssg enabled?
|
||||
beq 9f
|
||||
cmp r2, #EG_REL @ state > EG_REL?
|
||||
ble 9f
|
||||
cmp r3, #0x200 @ volume >= 0x200?
|
||||
blt 9f
|
||||
|
||||
tst r0, #0x01
|
||||
beq 1f
|
||||
|
||||
tst r0, #0x02
|
||||
eorne r0, r0, lsr #8 @ ssg ^= ssgn ^ 4
|
||||
eorne r0, r0, #0x4
|
||||
orrne r0, r0, #0x400 @ ssgn = 4
|
||||
strneh r0, [r5,#0x30]
|
||||
|
||||
eor r0, r0, #0x4 @ if ( !(ssg&0x04 )
|
||||
tst r0, #0x4
|
||||
cmpne r2, #EG_ATT @ if ( state != EG_ATT )
|
||||
movne r0, #0x400
|
||||
subne r0, r0, #1
|
||||
strneh r0, [r5,#0x1a] @ volume = MAX_ATT
|
||||
b 9f
|
||||
|
||||
1: tst r0, #0x02
|
||||
eorne r0, r0, #0x4 @ ssg ^= 4
|
||||
eorne r0, r0, #0x400 @ ssgn ^= 4
|
||||
strneh r0, [r5,#0x30]
|
||||
moveq r3, #0
|
||||
streq r3, [r5,#0x0c] @ phase = 0
|
||||
|
||||
cmp r2, #EG_ATT @ if ( state != EG_ATT )
|
||||
beq 9f
|
||||
|
||||
ldr r3, [r5,#0x1c] @ sl
|
||||
mov r2, #EG_SUS @ state = sl==MIN_ATT ? EG_SUS:EG_DEC
|
||||
cmp r3, #0
|
||||
|
||||
ldr r0, [r5,#0x04] @ ar
|
||||
ldr r3, [r5,#0x14] @ ksr
|
||||
movne r2, #EG_DEC
|
||||
add r0, r0, r3
|
||||
cmp r0, #32+62 @ if ( ar+ksr >= 32+62 )
|
||||
ldrlt r0, [r5,#0x1a]
|
||||
movge r0, #0
|
||||
strgeh r0, [r5,#0x1a] @ volume = MIN_ATT
|
||||
|
||||
cmp r0, #0
|
||||
movgt r2, #EG_ATT
|
||||
strb r2, [r5,#0x17] @ state
|
||||
9:
|
||||
.endm
|
||||
|
||||
@ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt, r3=scratch
|
||||
.macro advance_lfo_m
|
||||
|
@ -532,187 +654,6 @@
|
|||
.endm
|
||||
|
||||
|
||||
/*
|
||||
.global update_eg_phase @ FM_SLOT *SLOT, UINT32 eg_cnt
|
||||
|
||||
update_eg_phase:
|
||||
stmfd sp!, {r5,r6}
|
||||
mov r5, r0 @ slot
|
||||
ldrh r3, [r5,#0x18] @ tl
|
||||
ldrh r6, [r5,#0x1a] @ volume
|
||||
add r6, r6, r3
|
||||
update_eg_phase_slot SLOT1
|
||||
mov r0, r6
|
||||
ldmfd sp!, {r5,r6}
|
||||
bx lr
|
||||
.pool
|
||||
|
||||
|
||||
.global advance_lfo @ int lfo_ampm, UINT32 lfo_cnt_old, UINT32 lfo_cnt
|
||||
|
||||
advance_lfo:
|
||||
mov r12, r0, lsl #16
|
||||
advance_lfo_m
|
||||
mov r0, r12, lsr #16
|
||||
bx lr
|
||||
.pool
|
||||
|
||||
|
||||
.global upd_algo0 @ chan_rend_context *c
|
||||
upd_algo0:
|
||||
stmfd sp!, {r4-r10,lr}
|
||||
mov lr, r0
|
||||
|
||||
PIC_LDR(r3, ip, ym_sin_tab)
|
||||
PIC_LDR(r5, ip, ym_tl_tab)
|
||||
ldmia lr, {r6-r7}
|
||||
ldr r10, [lr, #0x54]
|
||||
ldr r12, [lr, #0x4c]
|
||||
|
||||
upd_algo0_m
|
||||
|
||||
ldmfd sp!, {r4-r10,pc}
|
||||
.pool
|
||||
|
||||
|
||||
.global upd_algo1 @ chan_rend_context *c
|
||||
upd_algo1:
|
||||
stmfd sp!, {r4-r10,lr}
|
||||
mov lr, r0
|
||||
|
||||
PIC_LDR(r3, ip, ym_sin_tab)
|
||||
PIC_LDR(r5, ip, ym_tl_tab)
|
||||
ldmia lr, {r6-r7}
|
||||
ldr r10, [lr, #0x54]
|
||||
ldr r12, [lr, #0x4c]
|
||||
|
||||
upd_algo1_m
|
||||
|
||||
ldmfd sp!, {r4-r10,pc}
|
||||
.pool
|
||||
|
||||
|
||||
.global upd_algo2 @ chan_rend_context *c
|
||||
upd_algo2:
|
||||
stmfd sp!, {r4-r10,lr}
|
||||
mov lr, r0
|
||||
|
||||
PIC_LDR(r3, ip, ym_sin_tab)
|
||||
PIC_LDR(r5, ip, ym_tl_tab)
|
||||
ldmia lr, {r6-r7}
|
||||
ldr r10, [lr, #0x54]
|
||||
ldr r12, [lr, #0x4c]
|
||||
|
||||
upd_algo2_m
|
||||
|
||||
ldmfd sp!, {r4-r10,pc}
|
||||
.pool
|
||||
|
||||
|
||||
.global upd_algo3 @ chan_rend_context *c
|
||||
upd_algo3:
|
||||
stmfd sp!, {r4-r10,lr}
|
||||
mov lr, r0
|
||||
|
||||
PIC_LDR(r3, ip, ym_sin_tab)
|
||||
PIC_LDR(r5, ip, ym_tl_tab)
|
||||
ldmia lr, {r6-r7}
|
||||
ldr r10, [lr, #0x54]
|
||||
ldr r12, [lr, #0x4c]
|
||||
|
||||
upd_algo3_m
|
||||
|
||||
ldmfd sp!, {r4-r10,pc}
|
||||
.pool
|
||||
|
||||
|
||||
.global upd_algo4 @ chan_rend_context *c
|
||||
upd_algo4:
|
||||
stmfd sp!, {r4-r10,lr}
|
||||
mov lr, r0
|
||||
|
||||
PIC_LDR(r3, ip, ym_sin_tab)
|
||||
PIC_LDR(r5, ip, ym_tl_tab)
|
||||
ldmia lr, {r6-r7}
|
||||
ldr r10, [lr, #0x54]
|
||||
ldr r12, [lr, #0x4c]
|
||||
|
||||
upd_algo4_m
|
||||
|
||||
ldmfd sp!, {r4-r10,pc}
|
||||
.pool
|
||||
|
||||
|
||||
.global upd_algo5 @ chan_rend_context *c
|
||||
upd_algo5:
|
||||
stmfd sp!, {r4-r10,lr}
|
||||
mov lr, r0
|
||||
|
||||
PIC_LDR(r3, ip, ym_sin_tab)
|
||||
PIC_LDR(r5, ip, ym_tl_tab)
|
||||
ldmia lr, {r6-r7}
|
||||
ldr r10, [lr, #0x54]
|
||||
ldr r12, [lr, #0x4c]
|
||||
|
||||
upd_algo5_m
|
||||
|
||||
ldmfd sp!, {r4-r10,pc}
|
||||
.pool
|
||||
|
||||
|
||||
.global upd_algo6 @ chan_rend_context *c
|
||||
upd_algo6:
|
||||
stmfd sp!, {r4-r10,lr}
|
||||
mov lr, r0
|
||||
|
||||
PIC_LDR(r3, ip, ym_sin_tab)
|
||||
PIC_LDR(r5, ip, ym_tl_tab)
|
||||
ldmia lr, {r6-r7}
|
||||
ldr r10, [lr, #0x54]
|
||||
ldr r12, [lr, #0x4c]
|
||||
|
||||
upd_algo6_m
|
||||
|
||||
ldmfd sp!, {r4-r10,pc}
|
||||
.pool
|
||||
|
||||
|
||||
.global upd_algo7 @ chan_rend_context *c
|
||||
upd_algo7:
|
||||
stmfd sp!, {r4-r10,lr}
|
||||
mov lr, r0
|
||||
|
||||
PIC_LDR(r3, ip, ym_sin_tab)
|
||||
PIC_LDR(r5, ip, ym_tl_tab)
|
||||
ldmia lr, {r6-r7}
|
||||
ldr r10, [lr, #0x54]
|
||||
ldr r12, [lr, #0x4c]
|
||||
|
||||
upd_algo7_m
|
||||
|
||||
ldmfd sp!, {r4-r10,pc}
|
||||
.pool
|
||||
|
||||
|
||||
.global upd_slot1 @ chan_rend_context *c
|
||||
upd_slot1:
|
||||
stmfd sp!, {r4-r10,lr}
|
||||
mov lr, r0
|
||||
|
||||
PIC_LDR(r3, ip, ym_sin_tab)
|
||||
PIC_LDR(r5, ip, ym_tl_tab)
|
||||
ldmia lr, {r6-r7}
|
||||
ldr r10, [lr, #0x54]
|
||||
ldr r12, [lr, #0x4c]
|
||||
|
||||
upd_slot1_m
|
||||
str r10, [lr, #0x38]
|
||||
|
||||
ldmfd sp!, {r4-r10,pc}
|
||||
.pool
|
||||
*/
|
||||
|
||||
|
||||
@ lr=context, r12=pack (stereo, lastchan, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16])
|
||||
@ r0-r2=scratch, r3=sin_tab/scratch, r4=(length<<8)|unused[4],was_update,algo[3], r5=tl_tab/slot,
|
||||
@ r6-r7=vol_out[4], r8=eg_timer, r9=eg_timer_add[31:16], r10=op1_out, r11=buffer
|
||||
|
@ -730,14 +671,21 @@ chan_render_loop:
|
|||
add r0, lr, #0x44
|
||||
ldmia r0, {r8,r9} @ eg_timer, eg_timer_add
|
||||
ldr r10, [lr, #0x54] @ op1_out
|
||||
ldmia lr, {r6,r7} @ load volumes
|
||||
@ ldmia lr, {r6,r7} @ load volumes
|
||||
ldr r5, [lr, #0x40] @ CH
|
||||
ldrh r6, [r5, #0x32] @ vol_out values for all slots
|
||||
ldrh r2, [r5, #0x32+SLOT_STRUCT_SIZE*2]
|
||||
ldrh r7, [r5, #0x32+SLOT_STRUCT_SIZE]
|
||||
ldrh r3, [r5, #0x32+SLOT_STRUCT_SIZE*3]
|
||||
orr r6, r6, r2, lsl #16
|
||||
orr r7, r7, r3, lsl #16
|
||||
|
||||
tst r12, #8 @ lfo?
|
||||
beq crl_loop
|
||||
|
||||
crl_loop_lfo:
|
||||
add r0, lr, #0x30
|
||||
ldmia r0, {r1,r2}
|
||||
ldmia r0, {r1,r2} @ lfo_cnt, lfo_inc
|
||||
|
||||
subs r4, r4, #0x100
|
||||
bmi crl_loop_end
|
||||
|
@ -754,15 +702,29 @@ crl_loop:
|
|||
subs r4, r4, #0x100
|
||||
bmi crl_loop_end
|
||||
|
||||
@ -- SSG --
|
||||
add r0, lr, #0x3c
|
||||
ldmia r0, {r1,r5} @ eg_cnt, CH
|
||||
|
||||
@ r5=slot, trashes: r0,r2,r3
|
||||
update_ssg_eg
|
||||
add r5, r5, #SLOT_STRUCT_SIZE*2 @ SLOT2 (2)
|
||||
update_ssg_eg
|
||||
sub r5, r5, #SLOT_STRUCT_SIZE @ SLOT3 (1)
|
||||
update_ssg_eg
|
||||
add r5, r5, #SLOT_STRUCT_SIZE*2 @ SLOT4 (3)
|
||||
update_ssg_eg
|
||||
sub r5, r5, #SLOT_STRUCT_SIZE*3
|
||||
|
||||
@ -- EG --
|
||||
add r8, r8, r9
|
||||
cmp r8, #EG_TIMER_OVERFLOW
|
||||
bcc eg_done
|
||||
add r0, lr, #0x3c
|
||||
ldmia r0, {r1,r5} @ eg_cnt, CH
|
||||
eg_loop:
|
||||
sub r8, r8, #EG_TIMER_OVERFLOW
|
||||
add r1, r1, #1
|
||||
cmp r1, #4096
|
||||
movge r1, #1
|
||||
@ SLOT1 (0)
|
||||
@ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
|
||||
update_eg_phase_slot SLOT1
|
||||
|
@ -774,8 +736,8 @@ eg_loop:
|
|||
update_eg_phase_slot SLOT4
|
||||
|
||||
cmp r8, #EG_TIMER_OVERFLOW
|
||||
subcs r5, r5, #SLOT_STRUCT_SIZE*3
|
||||
bcs eg_loop
|
||||
sub r5, r5, #SLOT_STRUCT_SIZE*3
|
||||
bhs eg_loop
|
||||
str r1, [lr, #0x3c]
|
||||
|
||||
eg_done:
|
||||
|
@ -787,6 +749,66 @@ eg_done:
|
|||
cmp r0, #0x4
|
||||
beq crl_loop
|
||||
|
||||
@ output interpolation
|
||||
#if 0
|
||||
@ basic interpolator, interpolate in middle region, else use closer value
|
||||
mov r3, r8, lsr #EG_SH @ eg_timer, [0..3<<EG_SH) after loop
|
||||
cmp r3, #(EG_TIMER_OVERFLOW>>EG_SH)/2
|
||||
bgt 0f @ mix is vol_out
|
||||
|
||||
ldrh r0, [r5,#0x34] @ SLOT1 vol_ipol
|
||||
lsleq r2, r6, #16
|
||||
addeq r0, r0, r2, lsr #16
|
||||
lsreq r0, r0, #1
|
||||
mov r6, r6, lsr #16
|
||||
orr r6, r0, r6, lsl #16
|
||||
|
||||
ldrh r0, [r5,#0x34+SLOT_STRUCT_SIZE*2] @ SLOT2 vol_ipol
|
||||
addeq r0, r0, r6, lsr #16
|
||||
lsreq r0, r0, #1
|
||||
mov r6, r6, lsl #16
|
||||
orr r6, r6, r0
|
||||
ror r6, r6, #16
|
||||
|
||||
ldrh r0, [r5,#0x34+SLOT_STRUCT_SIZE] @ SLOT3 vol_ipol
|
||||
lsleq r2, r7, #16
|
||||
addeq r0, r0, r2, lsr #16
|
||||
lsreq r0, r0, #1
|
||||
mov r7, r7, lsr #16
|
||||
orr r7, r0, r7, lsl #16
|
||||
|
||||
ldrh r0, [r5,#0x34+SLOT_STRUCT_SIZE*3] @ SLOT4 vol_ipol
|
||||
addeq r0, r0, r7, lsr #16
|
||||
lsreq r0, r0, #1
|
||||
mov r7, r7, lsl #16
|
||||
orr r7, r7, r0
|
||||
ror r7, r7, #16
|
||||
#elif 0
|
||||
@ super-basic... just take value closest to sample point
|
||||
mov r3, r8, lsr #EG_SH-1 @ eg_timer, [0..3<<EG_SH) after loop
|
||||
cmp r3, #(EG_TIMER_OVERFLOW>>EG_SH)
|
||||
bgt 0f @ mix is vol_out
|
||||
|
||||
ldrh r0, [r5,#0x34] @ SLOT1 vol_ipol
|
||||
mov r6, r6, lsr #16
|
||||
orr r6, r0, r6, lsl #16
|
||||
|
||||
ldrh r0, [r5,#0x34+SLOT_STRUCT_SIZE*2] @ SLOT2 vol_ipol
|
||||
mov r6, r6, lsl #16
|
||||
orr r6, r6, r0
|
||||
ror r6, r6, #16
|
||||
|
||||
ldrh r0, [r5,#0x34+SLOT_STRUCT_SIZE] @ SLOT3 vol_ipol
|
||||
mov r7, r7, lsr #16
|
||||
orr r7, r0, r7, lsl #16
|
||||
|
||||
ldrh r0, [r5,#0x34+SLOT_STRUCT_SIZE*3] @ SLOT4 vol_ipol
|
||||
mov r7, r7, lsl #16
|
||||
orr r7, r7, r0
|
||||
ror r7, r7, #16
|
||||
#endif
|
||||
0:
|
||||
|
||||
@ -- SLOT1 --
|
||||
PIC_LDR(r3, r2, ym_tl_tab)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue