audio: added SSG-EG to YM2612, plus some timing changes for SN76496+YM2612

This commit is contained in:
kub 2020-01-08 00:49:13 +01:00
parent 2a942f0d41
commit 8ac9ab7fcb
13 changed files with 571 additions and 455 deletions

View file

@ -36,7 +36,7 @@ endif
ifeq ("$(PLATFORM)",$(filter "$(PLATFORM)","gp2x" "opendingux" "rpi1")) ifeq ("$(PLATFORM)",$(filter "$(PLATFORM)","gp2x" "opendingux" "rpi1"))
# very small caches, avoid optimization options making the binary much bigger # 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 # 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 CFLAGS += -fno-common -fno-stack-protector -fno-guess-branch-probability -fno-caller-saves -fno-tree-loop-if-convert -fno-regmove
endif endif

View file

@ -1393,7 +1393,7 @@ static void emith_sync_t(int sr)
else if (tcond >= 0) { else if (tcond >= 0) {
int tmp = rcache_get_tmp(); int tmp = rcache_get_tmp();
EMIT(A64_CSET(tcond, 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); rcache_free_tmp(tmp);
} }
tcond = -1; tcond = -1;

View file

@ -546,7 +546,7 @@ static void PicoWrite8_z80(u32 a, u32 d)
} }
if ((a & 0x6000) == 0x4000) { // FM Sound if ((a & 0x6000) == 0x4000) { // FM Sound
if (PicoIn.opt & POPT_EN_FM) 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; return;
} }
// TODO: probably other VDP access too? Maybe more mirrors? // 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; break;
} }
int scanline = get_scanline(is_from_z80);
PsndDoFM(scanline);
#ifdef __GP2X__ #ifdef __GP2X__
if (PicoIn.opt & POPT_EXT_FM) if (PicoIn.opt & POPT_EXT_FM)
return YM2612Write_940(a, d, get_scanline(is_from_z80)); 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) static void z80_md_ym2612_write(unsigned int a, unsigned char data)
{ {
if (PicoIn.opt & POPT_EN_FM) 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) static void z80_md_vdp_br_write(unsigned int a, unsigned char data)

View file

@ -70,7 +70,7 @@ extern void *p32x_bios_g, *p32x_bios_m, *p32x_bios_s;
#define POPT_EN_DRC (1<<17) #define POPT_EN_DRC (1<<17)
#define POPT_DIS_SPRITE_LIM (1<<18) #define POPT_DIS_SPRITE_LIM (1<<18)
#define POPT_DIS_IDLE_DET (1<<19) #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_EN_PWM (1<<21)
#define POPT_PWM_IRQ_OPT (1<<22) #define POPT_PWM_IRQ_OPT (1<<22)

View file

@ -88,7 +88,6 @@ static void do_timing_hacks_vb(void)
static int PicoFrameHints(void) static int PicoFrameHints(void)
{ {
struct PicoVideo *pv = &Pico.video; struct PicoVideo *pv = &Pico.video;
int line_sample = Pico.m.pal ? 68 : 93;
int vdp_slots = (Pico.video.reg[12] & 1) ? 18 : 16; int vdp_slots = (Pico.video.reg[12] & 1) ? 18 : 16;
int lines, y, lines_vis, skip; int lines, y, lines_vis, skip;
int vcnt_wrap, vcnt_adj; 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: // Run scanline:
Pico.t.m68c_line_start = Pico.t.m68c_aim; Pico.t.m68c_line_start = Pico.t.m68c_aim;
do_timing_hacks_as(pv, vdp_slots); do_timing_hacks_as(pv, vdp_slots);
@ -238,10 +220,6 @@ static int PicoFrameHints(void)
p32x_start_blank(); p32x_start_blank();
#endif #endif
// get samples from sound chips
if (y == 224 && PicoIn.sndOut)
PsndGetSamples(y);
// Run scanline: // Run scanline:
CPUS_RUN(CYCLES_M68K_LINE - CYCLES_M68K_VINT_LAG); 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 pv->status |= ((pv->reg[1] >> 3) ^ SR_VB) & SR_VB; // forced blanking
// last scanline // last scanline
Pico.m.scanline = y; Pico.m.scanline = y++;
pv->v_counter = 0xff; pv->v_counter = 0xff;
pv->lwrite_cnt = 0; pv->lwrite_cnt = 0;
@ -337,6 +315,11 @@ static int PicoFrameHints(void)
#ifdef PICO_32X #ifdef PICO_32X
p32x_sync_sh2s(cycles); p32x_sync_sh2s(cycles);
#endif #endif
// get samples from sound chips
if (PicoIn.sndOut)
PsndGetSamples(y);
timers_cycle(); timers_cycle();
pv->hint_cnt = hint; pv->hint_cnt = hint;

View file

@ -336,7 +336,7 @@ struct PicoMisc
unsigned char eeprom_cycle; // EEPROM cycle number unsigned char eeprom_cycle; // EEPROM cycle number
unsigned char eeprom_slave; // EEPROM slave word for X24C02 and better SRAMs unsigned char eeprom_slave; // EEPROM slave word for X24C02 and better SRAMs
unsigned char eeprom_status; unsigned char eeprom_status;
unsigned char status; // rapid_ym2612, multi_ym_updates unsigned char pad1; // was ym2612 status
unsigned short dma_xfers; // 18 unsigned short dma_xfers; // 18
unsigned char eeprom_wb[2]; // EEPROM latch/write buffer unsigned char eeprom_wb[2]; // EEPROM latch/write buffer
unsigned int frame_count; // 1c for movies and idle det unsigned int frame_count; // 1c for movies and idle det
@ -433,6 +433,8 @@ struct PicoSound
int len_e_cnt; int len_e_cnt;
short dac_line; short dac_line;
short psg_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 // 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 PsndStartFrame(void);
PICO_INTERNAL void PsndDoDAC(int line_to); PICO_INTERNAL void PsndDoDAC(int line_to);
PICO_INTERNAL void PsndDoPSG(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 PsndClear(void);
PICO_INTERNAL void PsndGetSamples(int y); PICO_INTERNAL void PsndGetSamples(int y);
PICO_INTERNAL void PsndGetSamplesMS(void); PICO_INTERNAL void PsndGetSamplesMS(int y);
// sms.c // sms.c
#ifndef NO_SMS #ifndef NO_SMS

View file

@ -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_aim += cycles_line;
cycles_done += z80_run((cycles_aim - cycles_done) >> 8) << 8; cycles_done += z80_run((cycles_aim - cycles_done) >> 8) << 8;
} }
if (PicoIn.sndOut && Pico.snd.psg_line < lines) if (PicoIn.sndOut)
PsndDoPSG(lines - 1); PsndGetSamplesMS(lines);
} }
void PicoFrameDrawOnlyMS(void) void PicoFrameDrawOnlyMS(void)

View file

@ -12,16 +12,15 @@
#define MINOUT (-32768) #define MINOUT (-32768)
/* limitter */ /* limitter */
#define Limit16(val) { \ #define Limit16(val) \
val -= (val >> 2); \ if ((short)val != val) val = (val < 0 ? MINOUT : MAXOUT)
if ((short)val != val) val = (val < 0 ? MINOUT : MAXOUT); \
}
int mix_32_to_16l_level; int mix_32_to_16l_level;
static struct iir2 { // 2-pole IIR static struct iir2 { // 2-pole IIR
int x[2]; // sample buffer int x[2]; // sample buffer
int y[2]; // filter intermediates int y[2]; // filter intermediates
int i;
} lfi2, rfi2; } lfi2, rfi2;
// NB ">>" rounds to -infinity, "/" to 0. To compensate the effect possibly use // NB ">>" rounds to -infinity, "/" to 0. To compensate the effect possibly use

View file

@ -400,6 +400,8 @@ m32_16l_st_l_no_unal2:
ldmfd sp!, {r4-r11,lr} ldmfd sp!, {r4-r11,lr}
bx lr bx lr
#endif /* __GP2X__ */
.global mix_reset @ void .global mix_reset @ void
mix_reset: mix_reset:
ldr r0, =filter ldr r0, =filter
@ -409,11 +411,7 @@ mix_reset:
bx lr bx lr
.data .data
DCfilt r4, r10
DCfilt r5, r11
filter: filter:
.ds 8 .ds 8
#endif /* __GP2X__ */
@ vim:filetype=armasm @ vim:filetype=armasm

View file

@ -32,52 +32,17 @@ extern int *sn76496_regs;
static void dac_recalculate(void) static void dac_recalculate(void)
{ {
int lines = Pico.m.pal ? 313 : 262; int lines = Pico.m.pal ? 313 : 262;
int mid = Pico.m.pal ? 68 : 93; int i, pos;
int i, dac_cnt, pos, len;
if (Pico.snd.len <= lines) pos = 0; // Q16
{
// shrinking algo
dac_cnt = -Pico.snd.len;
len=1; pos=0;
dac_info[225] = 1;
for(i=226; i != 225; i++) for(i = 0; i <= lines; i++)
{ {
if (i >= lines) i = 0; dac_info[i] = ((pos+(1<<15)) >> 16); // round to nearest
if(dac_cnt < 0) { pos += Pico.snd.fm_mult;
pos++;
dac_cnt += lines;
} }
dac_cnt -= Pico.snd.len; for (i = lines+1; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)
dac_info[i] = pos; dac_info[i] = dac_info[i-1];
}
}
else
{
// 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;
}
}
for (i = lines; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)
dac_info[i] = dac_info[0];
} }
@ -95,6 +60,7 @@ void PsndRerate(int preserve_state)
{ {
void *state = NULL; void *state = NULL;
int target_fps = Pico.m.pal ? 50 : 60; int target_fps = Pico.m.pal ? 50 : 60;
int target_lines = Pico.m.pal ? 313 : 262;
if (preserve_state) { if (preserve_state) {
state = malloc(0x204); 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_add = ((PicoIn.sndRate - Pico.snd.len * target_fps) << 16) / target_fps;
Pico.snd.len_e_cnt = 0; Pico.snd.len_e_cnt = 0;
// samples per line
Pico.snd.fm_mult = 65536.0 * PicoIn.sndRate / (target_fps*target_lines);
// recalculate dac info // recalculate dac info
dac_recalculate(); dac_recalculate();
@ -149,8 +118,7 @@ PICO_INTERNAL void PsndStartFrame(void)
} }
Pico.snd.dac_line = Pico.snd.psg_line = 0; Pico.snd.dac_line = Pico.snd.psg_line = 0;
Pico.m.status &= ~1; Pico.snd.fm_pos = 0;
dac_info[224] = Pico.snd.len_use;
} }
PICO_INTERNAL void PsndDoDAC(int line_to) PICO_INTERNAL void PsndDoDAC(int line_to)
@ -159,9 +127,6 @@ PICO_INTERNAL void PsndDoDAC(int line_to)
int dout = ym2612.dacout; int dout = ym2612.dacout;
int line_from = Pico.snd.dac_line; int line_from = Pico.snd.dac_line;
if (line_to >= 313)
line_to = 312;
pos = dac_info[line_from]; pos = dac_info[line_from];
pos1 = dac_info[line_to + 1]; pos1 = dac_info[line_to + 1];
len = pos1 - pos; len = pos1 - pos;
@ -188,14 +153,9 @@ PICO_INTERNAL void PsndDoPSG(int line_to)
int pos, pos1, len; int pos, pos1, len;
int stereo = 0; int stereo = 0;
if (line_to >= 313)
line_to = 312;
pos = dac_info[line_from]; pos = dac_info[line_from];
pos1 = dac_info[line_to + 1]; pos1 = dac_info[line_to + 1];
len = pos1 - pos; len = pos1 - pos;
//elprintf(EL_STATUS, "%3d %3d %3d %3d %3d",
// pos, pos1, len, line_from, line_to);
if (len <= 0) if (len <= 0)
return; return;
@ -211,6 +171,34 @@ PICO_INTERNAL void PsndDoPSG(int line_to)
SN76496Update(PicoIn.sndOut + pos, len, stereo); 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 // cdda
static void cdda_raw_update(int *buffer, int length) 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) static int PsndRender(int offset, int length)
{ {
int buf32_updated = 0; int *buf32;
int *buf32 = PsndBuffer+offset;
int stereo = (PicoIn.opt & 8) >> 3; int stereo = (PicoIn.opt & 8) >> 3;
int fmlen = (Pico.snd.fm_pos >> 16) - offset;
offset <<= stereo; offset <<= stereo;
buf32 = PsndBuffer+offset;
pprof_start(sound); pprof_start(sound);
@ -288,14 +277,15 @@ static int PsndRender(int offset, int length)
return length; return length;
} }
// Add in the stereo FM buffer // Add in parts of the FM buffer not yet done
if (PicoIn.opt & POPT_EN_FM) { if (length-fmlen > 0) {
buf32_updated = YM2612UpdateOne(buf32, length, stereo, 1); int *fmbuf = buf32 + (fmlen << stereo);
} else if (PicoIn.opt & POPT_EN_FM)
memset32(buf32, 0, length<<stereo); YM2612UpdateOne(fmbuf, length-fmlen, stereo, 1);
else
//printf("active_chs: %02x\n", buf32_updated); memset32(fmbuf, 0, (length-fmlen)<<stereo);
(void)buf32_updated; Pico.snd.fm_pos += (length-fmlen)<<16;
}
// CD: PCM sound // CD: PCM sound
if (PicoIn.AHW & PAHW_MCD) { if (PicoIn.AHW & PAHW_MCD) {
@ -327,7 +317,6 @@ static int PsndRender(int offset, int length)
return length; return length;
} }
// to be called on 224 or line_sample scanlines only
PICO_INTERNAL void PsndGetSamples(int y) PICO_INTERNAL void PsndGetSamples(int y)
{ {
static int curr_pos = 0; static int curr_pos = 0;
@ -336,33 +325,20 @@ PICO_INTERNAL void PsndGetSamples(int y)
PsndDoDAC(y - 1); PsndDoDAC(y - 1);
PsndDoPSG(y - 1); PsndDoPSG(y - 1);
if (y == 224) curr_pos = PsndRender(0, Pico.snd.len_use);
{
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) if (PicoIn.writeSound)
PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2)); PicoIn.writeSound(curr_pos * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));
// clear sound buffer // clear sound buffer
PsndClear(); PsndClear();
Pico.snd.dac_line = 224; Pico.snd.dac_line = y;
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);
}
} }
PICO_INTERNAL void PsndGetSamplesMS(void) PICO_INTERNAL void PsndGetSamplesMS(int y)
{ {
int length = Pico.snd.len_use; int length = Pico.snd.len_use;
PsndDoPSG(223); PsndDoPSG(y - 1);
// upmix to "stereo" if needed // upmix to "stereo" if needed
if (PicoIn.opt & POPT_EN_STEREO) { if (PicoIn.opt & POPT_EN_STEREO) {
@ -374,8 +350,6 @@ PICO_INTERNAL void PsndGetSamplesMS(void)
if (PicoIn.writeSound != NULL) if (PicoIn.writeSound != NULL)
PicoIn.writeSound(length * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2)); PicoIn.writeSound(length * ((PicoIn.opt & POPT_EN_STEREO) ? 4 : 2));
PsndClear(); PsndClear();
dac_info[224] = 0;
} }
// vim:shiftwidth=2:ts=2:expandtab // vim:shiftwidth=2:ts=2:expandtab

View file

@ -7,6 +7,8 @@
** document it ("proprietary") and tells to write 0 to SSG-EG control register. ** 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) ** 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 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: /* TL_TAB_LEN is calculated as:
* 13 - sinus amplitude bits (Y axis) * 13 - sinus amplitude bits (Y axis)
* 2 - sinus sign bit (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), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
/* rates 00-11 */ /* rates 00-11 */
O(18),O(18),O( 0),O( 0), O(18),O(18),O( 2),O( 3),
O( 0),O( 0),O( 2),O( 2), 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), 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; 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 ) INLINE void FM_KEYON(int c , int s )
{ {
@ -562,13 +561,15 @@ INLINE void FM_KEYON(int c , int s )
{ {
SLOT->key = 1; SLOT->key = 1;
SLOT->phase = 0; /* restart Phase Generator */ 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) { if (SLOT->ar + SLOT->ksr < 32+62) {
SLOT->state = (SLOT->volume > MIN_ATT_INDEX) ? EG_ATT : if (SLOT->volume > MIN_ATT_INDEX) SLOT->state = EG_ATT;
((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC);
} else { } else {
SLOT->volume = MIN_ATT_INDEX; 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); ym2612.slot_mask |= (1<<s) << (c*4);
} }
} }
@ -579,8 +580,18 @@ INLINE void FM_KEYOFF(int c , int s )
if( SLOT->key ) if( SLOT->key )
{ {
SLOT->key = 0; SLOT->key = 0;
if (SLOT->state>EG_REL) if (SLOT->state>EG_REL) {
SLOT->state = EG_REL;/* phase -> Release */ 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) INLINE void set_tl(FM_SLOT *SLOT, int v)
{ {
SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
if (SLOT->state > EG_REL)
recalc_volout(SLOT);
} }
/* set attack rate & key scale */ /* set attack rate & key scale */
INLINE void set_ar_ksr(FM_CH *CH, FM_SLOT *SLOT, int v) INLINE void set_ar_ksr(FM_CH *CH, FM_SLOT *SLOT, int v)
{ {
UINT8 old_KSR = SLOT->KSR; UINT8 old_KSR = SLOT->KSR;
int eg_sh_ar, eg_sel_ar;
SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
@ -611,9 +625,6 @@ INLINE void set_ar_ksr(FM_CH *CH, FM_SLOT *SLOT, int v)
{ {
CH->SLOT[SLOT1].Incr=-1; CH->SLOT[SLOT1].Incr=-1;
} }
else
{
int eg_sh_ar, eg_sel_ar;
/* refresh Attack rate */ /* refresh Attack rate */
if ((SLOT->ar + SLOT->ksr) < 32+62) if ((SLOT->ar + SLOT->ksr) < 32+62)
@ -628,7 +639,6 @@ INLINE void set_ar_ksr(FM_CH *CH, FM_SLOT *SLOT, int v)
} }
SLOT->eg_pack_ar = eg_inc_pack[eg_sel_ar] | (eg_sh_ar<<24); SLOT->eg_pack_ar = eg_inc_pack[eg_sel_ar] | (eg_sh_ar<<24);
}
} }
/* set decay rate */ /* set decay rate */
@ -750,7 +760,7 @@ INLINE int advance_lfo(int lfo_ampm, UINT32 lfo_cnt_old, UINT32 lfo_cnt)
return lfo_ampm; 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; INT32 volume = SLOT->volume;
UINT32 pack = SLOT->eg_pack[SLOT->state - 1]; UINT32 pack = SLOT->eg_pack[SLOT->state - 1];
@ -763,6 +773,45 @@ 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 = pack >> ((eg_cnt >> shift) & 7) * 3;
eg_inc_val = (1 << (eg_inc_val & 7)) >> 1; eg_inc_val = (1 << (eg_inc_val & 7)) >> 1;
if (SLOT->ssg&0x08) {
switch (SLOT->state)
{
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;
}
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) switch (SLOT->state)
{ {
case EG_ATT: /* attack phase */ case EG_ATT: /* attack phase */
@ -799,8 +848,38 @@ INLINE void update_eg_phase(UINT16 *vol_out, FM_SLOT *SLOT, UINT32 eg_cnt)
break; break;
} }
SLOT->vol_out = volume + SLOT->tl;
}
SLOT->volume = volume; 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 #endif
@ -846,6 +925,16 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
{ {
int smp = 0; /* produced sample */ int smp = 0; /* produced sample */
unsigned int eg_out, eg_out2, eg_out4; 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) */ 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); 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_timer -= EG_TIMER_OVERFLOW;
ct->eg_cnt++; 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); SLOT = &ct->CH->SLOT[SLOT1];
if (ct->CH->SLOT[SLOT2].state != EG_OFF) update_eg_phase(&ct->vol_out2, &ct->CH->SLOT[SLOT2], ct->eg_cnt); SLOT->vol_ipol = SLOT->vol_out;
if (ct->CH->SLOT[SLOT3].state != EG_OFF) update_eg_phase(&ct->vol_out3, &ct->CH->SLOT[SLOT3], ct->eg_cnt); if (SLOT->state != EG_OFF) update_eg_phase(SLOT, 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[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 */ 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; if (ct->pack & (1<<(SLOT4+8))) eg_out4 += add;
} }
switch( ct->CH->ALGO ) switch( ct->algo&0x7 )
{ {
case 0: case 0:
{ {
@ -1086,6 +1221,33 @@ static void chan_render_finish(void)
ym2612.OPN.lfo_cnt = crct.lfo_cnt; 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 static int chan_render(int *buffer, int length, int c, UINT32 flags) // flags: stereo, ?, disabled, ?, pan_r, pan_l
{ {
crct.CH = &ym2612.CH[c]; 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.phase3 = crct.CH->SLOT[SLOT3].phase;
crct.phase4 = crct.CH->SLOT[SLOT4].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.op1_out = crct.CH->op1_out;
crct.algo = crct.CH->ALGO & 7; crct.algo = crct.CH->ALGO & 7;
if(crct.CH->pms) if(crct.CH->pms && (ym2612.OPN.ST.mode & 0xC0) && c == 2) {
{ /* 3 slot mode */
/* add support for 3 slot mode */ crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], ym2612.OPN.SL3.block_fnum[1]);
UINT32 block_fnum = crct.CH->block_fnum; 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]);
UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; crct.incr4 = update_lfo_phase(&crct.CH->SLOT[SLOT4], crct.CH->block_fnum);
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 */ else if(crct.CH->pms)
{ {
crct.incr1 = crct.CH->SLOT[SLOT1].Incr; crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], crct.CH->block_fnum);
crct.incr2 = crct.CH->SLOT[SLOT2].Incr; crct.incr2 = update_lfo_phase(&crct.CH->SLOT[SLOT2], crct.CH->block_fnum);
crct.incr3 = crct.CH->SLOT[SLOT3].Incr; crct.incr3 = update_lfo_phase(&crct.CH->SLOT[SLOT3], crct.CH->block_fnum);
crct.incr4 = crct.CH->SLOT[SLOT4].Incr; crct.incr4 = update_lfo_phase(&crct.CH->SLOT[SLOT4], crct.CH->block_fnum);
}
} }
else /* no LFO phase modulation */ else /* no LFO phase modulation */
{ {
@ -1297,8 +1423,13 @@ static void reset_channels(FM_CH *CH)
CH[c].fc = 0; CH[c].fc = 0;
for(s = 0 ; s < 4 ; s++ ) 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].state= EG_OFF;
CH[c].SLOT[s].volume = MAX_ATT_INDEX; 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; CH[c].mem_value = CH[c].op1_out = 0;
} }
@ -1503,8 +1634,10 @@ static int OPNWriteReg(int r, int v)
break; break;
case 0x90: /* SSG-EG */ case 0x90: /* SSG-EG */
// removed. SLOT->ssg = v&0x0f;
ret = 0; SLOT->ssg ^= SLOT->ssgn;
if (SLOT->state > EG_REL)
recalc_volout(SLOT);
break; break;
case 0xa0: case 0xa0:

View file

@ -53,6 +53,11 @@ typedef struct
}; };
UINT32 eg_pack[4]; 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; } FM_SLOT;
@ -176,21 +181,22 @@ int YM2612PicoStateLoad2(int *tat, int *tbt);
#else #else
/* GP2X specific */ /* GP2X specific */
#include "../../platform/gp2x/940ctl.h" #include "../../platform/gp2x/940ctl.h"
#define YM2612Init(baseclock,rate) { \ #define YM2612Init(baseclock,rate) do { \
if (PicoIn.opt&POPT_EXT_FM) YM2612Init_940(baseclock, rate); \ if (PicoIn.opt&POPT_EXT_FM) YM2612Init_940(baseclock, rate); \
else YM2612Init_(baseclock, rate); \ else YM2612Init_(baseclock, rate); \
} } while (0)
#define YM2612ResetChip() { \ #define YM2612ResetChip() do { \
if (PicoIn.opt&POPT_EXT_FM) YM2612ResetChip_940(); \ if (PicoIn.opt&POPT_EXT_FM) YM2612ResetChip_940(); \
else YM2612ResetChip_(); \ else YM2612ResetChip_(); \
} } while (0)
#define YM2612UpdateOne(buffer,length,stereo,is_buf_empty) \ #define YM2612UpdateOne(buffer,length,stereo,is_buf_empty) do { \
(PicoIn.opt&POPT_EXT_FM) ? YM2612UpdateOne_940(buffer, length, stereo, is_buf_empty) : \ (PicoIn.opt&POPT_EXT_FM) ? YM2612UpdateOne_940(buffer, length, stereo, is_buf_empty) : \
YM2612UpdateOne_(buffer, length, stereo, is_buf_empty); YM2612UpdateOne_(buffer, length, stereo, is_buf_empty); \
#define YM2612PicoStateLoad() { \ } while (0)
#define YM2612PicoStateLoad() do { \
if (PicoIn.opt&POPT_EXT_FM) YM2612PicoStateLoad_940(); \ if (PicoIn.opt&POPT_EXT_FM) YM2612PicoStateLoad_940(); \
else YM2612PicoStateLoad_(); \ else YM2612PicoStateLoad_(); \
} } while (0)
#endif /* __GP2X__ */ #endif /* __GP2X__ */

View file

@ -1,6 +1,7 @@
/* /*
* PicoDrive * PicoDrive
* (C) notaz, 2006 * (C) notaz, 2006
* (C) kub, 2020 added SSG-EG and simple output rate interpolation
* *
* This work is licensed under the terms of MAME license. * This work is licensed under the terms of MAME license.
* See COPYING file in the top-level directory. * See COPYING file in the top-level directory.
@ -18,7 +19,7 @@
.equiv SLOT2, 2 .equiv SLOT2, 2
.equiv SLOT3, 1 .equiv SLOT3, 1
.equiv SLOT4, 3 .equiv SLOT4, 3
.equiv SLOT_STRUCT_SIZE, 0x30 .equiv SLOT_STRUCT_SIZE, 0x38
.equiv TL_TAB_LEN, 0x1A00 .equiv TL_TAB_LEN, 0x1A00
@ -41,8 +42,10 @@
@ r5=slot, r1=eg_cnt, trashes: r0,r2,r3 @ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
@ writes output to routp, but only if vol_out changes @ writes output to routp, but only if vol_out changes
.macro update_eg_phase_slot slot .macro update_eg_phase_slot slot
ldrh r0, [r5,#0x32] @ vol_out
ldrb r2, [r5,#0x17] @ state ldrb r2, [r5,#0x17] @ state
add r3, r5, #0x1c add r3, r5, #0x1c
strh r0, [r5,#0x34] @ vol_ipol
tst r2, r2 tst r2, r2
beq 0f @ EG_OFF beq 0f @ EG_OFF
@ -60,11 +63,17 @@
add r3, r3, r3, lsl #1 add r3, r3, r3, lsl #1
mov r3, r2, lsr r3 mov r3, r2, lsr r3
and r3, r3, #7 @ eg_inc_val shift, may be 0 and r3, r3, #7 @ eg_inc_val shift, may be 0
ldrb r0, [r5,#0x30] @ ssg
ldrb r2, [r5,#0x17] @ state 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 cmp r2, #4 @ EG_ATT
ldrh r0, [r5,#0x1a] @ volume, unsigned (0-1023)
beq 4f beq 4f
cmp r2, #2 cmp r2, #2
mov r2, #1 mov r2, #1
mov r2, r2, lsl r3 mov r2, r2, lsl r3
@ -82,10 +91,9 @@
4: @ EG_ATT 4: @ EG_ATT
subs r3, r3, #1 @ eg_inc_val_shift - 1 subs r3, r3, #1 @ eg_inc_val_shift - 1
mov r2, #0
mvnpl r2, r0 mvnpl r2, r0
mov r2, r2, lsl r3 movpl r2, r2, lsl r3
add r0, r0, r2, asr #4 addpl r0, r0, r2, asr #4
cmp r0, #0 @ if (volume <= MIN_ATT_INDEX) cmp r0, #0 @ if (volume <= MIN_ATT_INDEX)
bgt 10f bgt 10f
ldr r2, [r5,#0x1c] ldr r2, [r5,#0x1c]
@ -112,24 +120,81 @@
strgeb r3, [r5,#0x17] @ state strgeb r3, [r5,#0x17] @ state
10: @ finish 10: @ finish
ldrh r3, [r5,#0x18] @ tl
strh r0, [r5,#0x1a] @ volume 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 .if \slot == SLOT1
mov r6, r6, lsr #16 mov r6, r6, lsr #16
add r0, r0, r3
orr r6, r0, r6, lsl #16 orr r6, r0, r6, lsl #16
.elseif \slot == SLOT2 .elseif \slot == SLOT2
mov r6, r6, lsl #16 mov r6, r6, lsl #16
add r0, r0, r3
mov r0, r0, lsl #16 mov r0, r0, lsl #16
orr r6, r0, r6, lsr #16 orr r6, r0, r6, lsr #16
.elseif \slot == SLOT3 .elseif \slot == SLOT3
mov r7, r7, lsr #16 mov r7, r7, lsr #16
add r0, r0, r3
orr r7, r0, r7, lsl #16 orr r7, r0, r7, lsl #16
.elseif \slot == SLOT4 .elseif \slot == SLOT4
mov r7, r7, lsl #16 mov r7, r7, lsl #16
add r0, r0, r3
mov r0, r0, lsl #16 mov r0, r0, lsl #16
orr r7, r0, r7, lsr #16 orr r7, r0, r7, lsr #16
.endif .endif
@ -137,6 +202,63 @@
0: @ EG_OFF 0: @ EG_OFF
.endm .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 @ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt, r3=scratch
.macro advance_lfo_m .macro advance_lfo_m
@ -532,187 +654,6 @@
.endm .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]) @ 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, @ 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 @ 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 add r0, lr, #0x44
ldmia r0, {r8,r9} @ eg_timer, eg_timer_add ldmia r0, {r8,r9} @ eg_timer, eg_timer_add
ldr r10, [lr, #0x54] @ op1_out 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? tst r12, #8 @ lfo?
beq crl_loop beq crl_loop
crl_loop_lfo: crl_loop_lfo:
add r0, lr, #0x30 add r0, lr, #0x30
ldmia r0, {r1,r2} ldmia r0, {r1,r2} @ lfo_cnt, lfo_inc
subs r4, r4, #0x100 subs r4, r4, #0x100
bmi crl_loop_end bmi crl_loop_end
@ -754,15 +702,29 @@ crl_loop:
subs r4, r4, #0x100 subs r4, r4, #0x100
bmi crl_loop_end 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 -- @ -- EG --
add r8, r8, r9 add r8, r8, r9
cmp r8, #EG_TIMER_OVERFLOW cmp r8, #EG_TIMER_OVERFLOW
bcc eg_done bcc eg_done
add r0, lr, #0x3c
ldmia r0, {r1,r5} @ eg_cnt, CH
eg_loop: eg_loop:
sub r8, r8, #EG_TIMER_OVERFLOW sub r8, r8, #EG_TIMER_OVERFLOW
add r1, r1, #1 add r1, r1, #1
cmp r1, #4096
movge r1, #1
@ SLOT1 (0) @ SLOT1 (0)
@ r5=slot, r1=eg_cnt, trashes: r0,r2,r3 @ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
update_eg_phase_slot SLOT1 update_eg_phase_slot SLOT1
@ -774,8 +736,8 @@ eg_loop:
update_eg_phase_slot SLOT4 update_eg_phase_slot SLOT4
cmp r8, #EG_TIMER_OVERFLOW cmp r8, #EG_TIMER_OVERFLOW
subcs r5, r5, #SLOT_STRUCT_SIZE*3 sub r5, r5, #SLOT_STRUCT_SIZE*3
bcs eg_loop bhs eg_loop
str r1, [lr, #0x3c] str r1, [lr, #0x3c]
eg_done: eg_done:
@ -787,6 +749,66 @@ eg_done:
cmp r0, #0x4 cmp r0, #0x4
beq crl_loop 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 -- @ -- SLOT1 --
PIC_LDR(r3, r2, ym_tl_tab) PIC_LDR(r3, r2, ym_tl_tab)