initial import

git-svn-id: file:///home/notaz/opt/svn/PicoDrive@2 be3aeb3a-fb24-0410-a615-afba39da0efa
This commit is contained in:
notaz 2006-12-19 20:53:21 +00:00
parent 2cadbd5e56
commit cc68a136aa
341 changed files with 180839 additions and 0 deletions

347
Pico/sound/sn76496.c Normal file
View file

@ -0,0 +1,347 @@
/***************************************************************************
sn76496.c
Routines to emulate the Texas Instruments SN76489 / SN76496 programmable
tone /noise generator. Also known as (or at least compatible with) TMS9919.
Noise emulation is not accurate due to lack of documentation. The noise
generator uses a shift register with a XOR-feedback network, but the exact
layout is unknown. It can be set for either period or white noise; again,
the details are unknown.
28/03/2005 : Sebastien Chevalier
Update th SN76496Write func, according to SN76489 doc found on SMSPower.
- On write with 0x80 set to 0, when LastRegister is other then TONE,
the function is similar than update with 0x80 set to 1
***************************************************************************/
#ifndef __GNUC__
#pragma warning (disable:4244)
#endif
#include "sn76496.h"
#define MAX_OUTPUT 0x47ff // was 0x7fff
#define STEP 0x10000
/* Formulas for noise generator */
/* bit0 = output */
/* noise feedback for white noise mode (verified on real SN76489 by John Kortink) */
#define FB_WNOISE 0x14002 /* (16bits) bit16 = bit0(out) ^ bit2 ^ bit15 */
/* noise feedback for periodic noise mode */
//#define FB_PNOISE 0x10000 /* 16bit rorate */
#define FB_PNOISE 0x08000 /* JH 981127 - fixes Do Run Run */
/*
0x08000 is definitely wrong. The Master System conversion of Marble Madness
uses periodic noise as a baseline. With a 15-bit rotate, the bassline is
out of tune.
The 16-bit rotate has been confirmed against a real PAL Sega Master System 2.
Hope that helps the System E stuff, more news on the PSG as and when!
*/
/* noise generator start preset (for periodic noise) */
#define NG_PRESET 0x0f35
struct SN76496
{
//sound_stream * Channel;
int SampleRate;
unsigned int UpdateStep;
int VolTable[16]; /* volume table */
int Register[8]; /* registers */
int LastRegister; /* last register written */
int Volume[4]; /* volume of voice 0-2 and noise */
unsigned int RNG; /* noise generator */
int NoiseFB; /* noise feedback mask */
int Period[4];
int Count[4];
int Output[4];
int pad[1];
};
static struct SN76496 ono_sn; // one and only SN76496
int *sn76496_regs;
//static
void SN76496Write(int data)
{
struct SN76496 *R = &ono_sn;
int n;
/* update the output buffer before changing the registers */
//stream_update(R->Channel,0);
if (data & 0x80)
{
int r = (data & 0x70) >> 4;
int c = r/2;
R->LastRegister = r;
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
switch (r)
{
case 0: /* tone 0 : frequency */
case 2: /* tone 1 : frequency */
case 4: /* tone 2 : frequency */
R->Period[c] = R->UpdateStep * R->Register[r];
if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;
if (r == 4)
{
/* update noise shift frequency */
if ((R->Register[6] & 0x03) == 0x03)
R->Period[3] = 2 * R->Period[2];
}
break;
case 1: /* tone 0 : volume */
case 3: /* tone 1 : volume */
case 5: /* tone 2 : volume */
case 7: /* noise : volume */
R->Volume[c] = R->VolTable[data & 0x0f];
break;
case 6: /* noise : frequency, mode */
{
int n = R->Register[6];
R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
n &= 3;
/* N/512,N/1024,N/2048,Tone #3 output */
R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+(n&3)));
/* reset noise shifter */
R->RNG = NG_PRESET;
R->Output[3] = R->RNG & 1;
}
break;
}
}
else
{
int r = R->LastRegister;
int c = r/2;
switch (r)
{
case 0: /* tone 0 : frequency */
case 2: /* tone 1 : frequency */
case 4: /* tone 2 : frequency */
R->Register[r] = (R->Register[r] & 0x0f) | ((data & 0x3f) << 4);
R->Period[c] = R->UpdateStep * R->Register[r];
if (R->Period[c] == 0) R->Period[c] = R->UpdateStep;
if (r == 4)
{
/* update noise shift frequency */
if ((R->Register[6] & 0x03) == 0x03)
R->Period[3] = 2 * R->Period[2];
}
break;
case 1: /* tone 0 : volume */
case 3: /* tone 1 : volume */
case 5: /* tone 2 : volume */
case 7: /* noise : volume */
R->Volume[c] = R->VolTable[data & 0x0f];
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
break;
case 6: /* noise : frequency, mode */
{
R->Register[r] = (R->Register[r] & 0x3f0) | (data & 0x0f);
n = R->Register[6];
R->NoiseFB = (n & 4) ? FB_WNOISE : FB_PNOISE;
n &= 3;
/* N/512,N/1024,N/2048,Tone #3 output */
R->Period[3] = ((n&3) == 3) ? 2 * R->Period[2] : (R->UpdateStep << (5+(n&3)));
/* reset noise shifter */
R->RNG = NG_PRESET;
R->Output[3] = R->RNG & 1;
}
break;
}
}
}
/*
WRITE8_HANDLER( SN76496_0_w ) { SN76496Write(0,data); }
WRITE8_HANDLER( SN76496_1_w ) { SN76496Write(1,data); }
WRITE8_HANDLER( SN76496_2_w ) { SN76496Write(2,data); }
WRITE8_HANDLER( SN76496_3_w ) { SN76496Write(3,data); }
WRITE8_HANDLER( SN76496_4_w ) { SN76496Write(4,data); }
*/
//static
void SN76496Update(short *buffer,int length,int stereo)
{
int i;
struct SN76496 *R = &ono_sn;
/* If the volume is 0, increase the counter */
for (i = 0;i < 4;i++)
{
if (R->Volume[i] == 0)
{
/* note that I do count += length, NOT count = length + 1. You might think */
/* it's the same since the volume is 0, but doing the latter could cause */
/* interferencies when the program is rapidly modulating the volume. */
if (R->Count[i] <= length*STEP) R->Count[i] += length*STEP;
}
}
while (length > 0)
{
int vol[4];
unsigned int out;
int left;
/* vol[] keeps track of how long each square wave stays */
/* in the 1 position during the sample period. */
vol[0] = vol[1] = vol[2] = vol[3] = 0;
for (i = 0;i < 3;i++)
{
if (R->Output[i]) vol[i] += R->Count[i];
R->Count[i] -= STEP;
/* Period[i] is the half period of the square wave. Here, in each */
/* loop I add Period[i] twice, so that at the end of the loop the */
/* square wave is in the same status (0 or 1) it was at the start. */
/* vol[i] is also incremented by Period[i], since the wave has been 1 */
/* exactly half of the time, regardless of the initial position. */
/* If we exit the loop in the middle, Output[i] has to be inverted */
/* and vol[i] incremented only if the exit status of the square */
/* wave is 1. */
while (R->Count[i] <= 0)
{
R->Count[i] += R->Period[i];
if (R->Count[i] > 0)
{
R->Output[i] ^= 1;
if (R->Output[i]) vol[i] += R->Period[i];
break;
}
R->Count[i] += R->Period[i];
vol[i] += R->Period[i];
}
if (R->Output[i]) vol[i] -= R->Count[i];
}
left = STEP;
do
{
int nextevent;
if (R->Count[3] < left) nextevent = R->Count[3];
else nextevent = left;
if (R->Output[3]) vol[3] += R->Count[3];
R->Count[3] -= nextevent;
if (R->Count[3] <= 0)
{
if (R->RNG & 1) R->RNG ^= R->NoiseFB;
R->RNG >>= 1;
R->Output[3] = R->RNG & 1;
R->Count[3] += R->Period[3];
if (R->Output[3]) vol[3] += R->Period[3];
}
if (R->Output[3]) vol[3] -= R->Count[3];
left -= nextevent;
} while (left > 0);
out = vol[0] * R->Volume[0] + vol[1] * R->Volume[1] +
vol[2] * R->Volume[2] + vol[3] * R->Volume[3];
if (out > MAX_OUTPUT * STEP) out = MAX_OUTPUT * STEP;
out /= STEP; // will be optimized to shift
if(stereo) {
// only left channel for stereo (will be copied to right by ym2612 mixing code)
*buffer += out;
buffer+=2;
} else
*buffer++ += out;
length--;
}
}
static void SN76496_set_clock(struct SN76496 *R,int clock)
{
/* the base clock for the tone generators is the chip clock divided by 16; */
/* for the noise generator, it is clock / 256. */
/* Here we calculate the number of steps which happen during one sample */
/* at the given sample rate. No. of events = sample rate / (clock/16). */
/* STEP is a multiplier used to turn the fraction into a fixed point */
/* number. */
R->UpdateStep = ((double)STEP * R->SampleRate * 16) / clock;
}
static void SN76496_set_gain(struct SN76496 *R,int gain)
{
int i;
double out;
gain &= 0xff;
/* increase max output basing on gain (0.2 dB per step) */
out = MAX_OUTPUT / 3;
while (gain-- > 0)
out *= 1.023292992; /* = (10 ^ (0.2/20)) */
/* build volume table (2dB per step) */
for (i = 0;i < 15;i++)
{
/* limit volume to avoid clipping */
if (out > MAX_OUTPUT / 3) R->VolTable[i] = MAX_OUTPUT / 3;
else R->VolTable[i] = out;
out /= 1.258925412; /* = 10 ^ (2/20) = 2dB */
}
R->VolTable[15] = 0;
}
//static
int SN76496_init(int clock,int sample_rate)
{
struct SN76496 *R = &ono_sn;
int i;
//R->Channel = stream_create(0,1, sample_rate,R,SN76496Update);
sn76496_regs = R->Register;
R->SampleRate = sample_rate;
SN76496_set_clock(R,clock);
for (i = 0;i < 4;i++) R->Volume[i] = 0;
R->LastRegister = 0;
for (i = 0;i < 8;i+=2)
{
R->Register[i] = 0;
R->Register[i + 1] = 0x0f; /* volume = 0 */
}
for (i = 0;i < 4;i++)
{
R->Output[i] = 0;
R->Period[i] = R->Count[i] = R->UpdateStep;
}
R->RNG = NG_PRESET;
R->Output[3] = R->RNG & 1;
// added
SN76496_set_gain(R, 0);
return 0;
}

8
Pico/sound/sn76496.h Normal file
View file

@ -0,0 +1,8 @@
#ifndef SN76496_H
#define SN76496_H
void SN76496Write(int data);
void SN76496Update(short *buffer,int length,int stereo);
int SN76496_init(int clock,int sample_rate);
#endif

390
Pico/sound/sound.c Normal file
View file

@ -0,0 +1,390 @@
// This is part of Pico Library
// (c) Copyright 2004 Dave, All rights reserved.
// (c) Copyright 2006 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#include <string.h>
#include "sound.h"
#include "ym2612.h"
#include "sn76496.h"
#ifndef __GNUC__
#pragma warning (disable:4244)
#endif
#if defined(_USE_MZ80)
#include "../../cpu/mz80/mz80.h"
#elif defined(_USE_DRZ80)
#include "../../cpu/DrZ80/drz80.h"
#endif
#include "../PicoInt.h"
//int z80CycleAim = 0;
// dac
short *dac_out;
unsigned short dac_info[312]; // pppppppp ppppllll, p - pos in buff, l - length to write for this sample
// for Pico
int PsndRate=0;
int PsndLen=0; // number of mono samples, multiply by 2 for stereo
short *PsndOut=NULL; // PCM data buffer
// from ym2612.c
extern int *ym2612_dacen;
extern INT32 *ym2612_dacout;
void YM2612TimerHandler(int c,int cnt);
// sn76496
extern int *sn76496_regs;
static void dac_recalculate()
{
int i, dac_cnt, pos, len, lines = Pico.m.pal ? 312 : 262, mid = Pico.m.pal ? 68 : 93;
if(PsndLen <= lines) {
// shrinking algo
dac_cnt = 0;//lines - PsndLen;
len=1; pos=0;
dac_info[225] = 1;
for(i=226; i != 225; i++) {
if (i >= lines) i = 0;
len = 0;
if(dac_cnt < 0) {
len=1;
pos++;
dac_cnt += lines;
}
dac_cnt -= PsndLen;
dac_info[i] = (pos<<4)|len;
}
} else {
// stretching
dac_cnt = PsndLen/2;
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 < PsndLen/2) {
dac_cnt -= lines;
len++;
}
dac_cnt += PsndLen;
dac_info[i] = (pos<<4)|len;
pos+=len;
}
// last sample
for(len = 0, i = pos; i < PsndLen; i++) len++;
dac_info[224] = (pos<<4)|len;
}
// dprintf("rate is %i, len %i", PsndRate, PsndLen);
// for(i=0; i < lines; i++)
// dprintf("%03i : %03i : %i", i, dac_info[i]>>4, dac_info[i]&0xf);
//exit(8);
}
void sound_reset()
{
extern int z80stopCycle;
void *ym2612_regs;
// init even if we are not going to use them, just in case we ever enable it
YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);
// also clear the internal registers+addr line
ym2612_regs = YM2612GetRegs();
memset(ym2612_regs, 0, 0x200+4);
SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);
// calculate PsndLen
PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);
// recalculate dac info
dac_recalculate();
z80stopCycle = 0;
}
// to be called after changing sound rate or chips
void sound_rerate()
{
unsigned int state[28];
YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);
// feed it back it's own registers, just like after loading state
YM2612PicoStateLoad();
memcpy(state, sn76496_regs, 28*4); // remember old state
SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);
memcpy(sn76496_regs, state, 28*4); // restore old state
// calculate PsndLen
PsndLen=PsndRate/(Pico.m.pal ? 50 : 60);
// recalculate dac info
dac_recalculate();
}
// This is called once per raster (aka line), but not necessarily for every line
int sound_timers_and_dac(int raster)
{
if(raster >= 0 && PsndOut && (PicoOpt&1) && *ym2612_dacen) {
short dout = (short) *ym2612_dacout;
int pos=dac_info[raster], len=pos&0xf;
short *d;
pos>>=4;
if(PicoOpt&8) { // only left channel for stereo (will be copied to right by ym2612 mixing code)
d=PsndOut+pos*2;
while(len--) { *d = dout; d += 2; }
} else {
d=PsndOut+pos;
while(len--) *d++ = dout;
}
}
//dprintf("s: %03i", raster);
// Our raster lasts 63.61323/64.102564 microseconds (NTSC/PAL)
YM2612PicoTick(1);
return 0;
}
int sound_render(int offset, int length)
{
int stereo = (PicoOpt & 8) >> 3;
offset <<= stereo;
// PSG
if(PicoOpt & 2)
SN76496Update(PsndOut+offset, length, stereo);
// Add in the stereo FM buffer
if(PicoOpt & 1) {
YM2612UpdateOne(PsndOut+offset, length, stereo);
} else {
// YM2612 upmixes to stereo, so we have to do this manually here
int i;
short *s = PsndOut+offset;
for (i = 0; i < length; i++) {
*(s+1) = *s; s+=2;
}
}
return 0;
}
#if defined(_USE_MZ80)
// memhandlers for mz80 core
unsigned char mz80_read(UINT32 a, struct MemoryReadByte *w) { return z80_read(a); }
void mz80_write(UINT32 a, UINT8 d, struct MemoryWriteByte *w) { z80_write(d, a); }
// structures for mz80 core
static struct MemoryReadByte mz80_mem_read[]=
{
{0x0000,0xffff,mz80_read},
{(UINT32) -1,(UINT32) -1,NULL}
};
static struct MemoryWriteByte mz80_mem_write[]=
{
{0x0000,0xffff,mz80_write},
{(UINT32) -1,(UINT32) -1,NULL}
};
static struct z80PortRead mz80_io_read[] ={
{(UINT16) -1,(UINT16) -1,NULL}
};
static struct z80PortWrite mz80_io_write[]={
{(UINT16) -1,(UINT16) -1,NULL}
};
#elif defined(_USE_DRZ80)
static struct DrZ80 drZ80;
static unsigned int DrZ80_rebasePC(unsigned short a)
{
drZ80.Z80PC_BASE = (unsigned int) Pico.zram;
return drZ80.Z80PC_BASE + a;
}
static unsigned int DrZ80_rebaseSP(unsigned short a)
{
drZ80.Z80SP_BASE = (unsigned int) Pico.zram;
return drZ80.Z80SP_BASE + a;
}
static unsigned char DrZ80_in(unsigned short p)
{
return 0xff;
}
static void DrZ80_out(unsigned short p,unsigned char d)
{
}
static void DrZ80_irq_callback()
{
drZ80.Z80_IRQ = 0; // lower irq when accepted
}
#endif
// z80 functionality wrappers
void z80_init()
{
#if defined(_USE_MZ80)
struct mz80context z80;
// z80
mz80init();
// Modify the default context
mz80GetContext(&z80);
// point mz80 stuff
z80.z80Base=Pico.zram;
z80.z80MemRead=mz80_mem_read;
z80.z80MemWrite=mz80_mem_write;
z80.z80IoRead=mz80_io_read;
z80.z80IoWrite=mz80_io_write;
mz80SetContext(&z80);
#elif defined(_USE_DRZ80)
memset(&drZ80, 0, sizeof(struct DrZ80));
drZ80.z80_rebasePC=DrZ80_rebasePC;
drZ80.z80_rebaseSP=DrZ80_rebaseSP;
drZ80.z80_read8 =z80_read;
drZ80.z80_read16 =z80_read16;
drZ80.z80_write8 =z80_write;
drZ80.z80_write16 =z80_write16;
drZ80.z80_in =DrZ80_in;
drZ80.z80_out =DrZ80_out;
drZ80.z80_irq_callback=DrZ80_irq_callback;
#endif
}
void z80_reset()
{
#if defined(_USE_MZ80)
mz80reset();
#elif defined(_USE_DRZ80)
memset(&drZ80, 0, 0x54);
drZ80.Z80F = (1<<2); // set ZFlag
drZ80.Z80F2 = (1<<2); // set ZFlag
drZ80.Z80IX = 0xFFFF << 16;
drZ80.Z80IY = 0xFFFF << 16;
drZ80.Z80IM = 0; // 1?
drZ80.Z80PC = drZ80.z80_rebasePC(0);
drZ80.Z80SP = drZ80.z80_rebaseSP(0x2000); // 0xf000 ?
#endif
Pico.m.z80_fakeval = 0; // for faking when Z80 is disabled
}
void z80_resetCycles()
{
#if defined(_USE_MZ80)
mz80GetElapsedTicks(1);
#endif
}
void z80_int()
{
#if defined(_USE_MZ80)
mz80int(0);
#elif defined(_USE_DRZ80)
drZ80.z80irqvector = 0xFF; // default IRQ vector RST opcode
drZ80.Z80_IRQ = 1;
#endif
}
// returns number of cycles actually executed
int z80_run(int cycles)
{
#if defined(_USE_MZ80)
int ticks_pre = mz80GetElapsedTicks(0);
mz80exec(cycles);
return mz80GetElapsedTicks(0) - ticks_pre;
#elif defined(_USE_DRZ80)
return cycles - DrZ80Run(&drZ80, cycles);
#else
return cycles;
#endif
}
void z80_pack(unsigned char *data)
{
#if defined(_USE_MZ80)
struct mz80context mz80;
*(int *)data = 0x00005A6D; // "mZ"
mz80GetContext(&mz80);
memcpy(data+4, &mz80.z80clockticks, sizeof(mz80)-5*4); // don't save base&memhandlers
#elif defined(_USE_DRZ80)
*(int *)data = 0x015A7244; // "DrZ" v1
drZ80.Z80PC = drZ80.z80_rebasePC(drZ80.Z80PC-drZ80.Z80PC_BASE);
drZ80.Z80SP = drZ80.z80_rebaseSP(drZ80.Z80SP-drZ80.Z80SP_BASE);
memcpy(data+4, &drZ80, 0x54);
#endif
}
void z80_unpack(unsigned char *data)
{
#if defined(_USE_MZ80)
if(*(int *)data == 0x00005A6D) { // "mZ" save?
struct mz80context mz80;
mz80GetContext(&mz80);
memcpy(&mz80.z80clockticks, data+4, sizeof(mz80)-5*4);
mz80SetContext(&mz80);
} else {
z80_reset();
z80_int();
}
#elif defined(_USE_DRZ80)
if(*(int *)data == 0x015A7244) { // "DrZ" v1 save?
memcpy(&drZ80, data+4, 0x54);
// update bases
drZ80.Z80PC = drZ80.z80_rebasePC(drZ80.Z80PC-drZ80.Z80PC_BASE);
drZ80.Z80SP = drZ80.z80_rebaseSP(drZ80.Z80SP-drZ80.Z80SP_BASE);
} else {
z80_reset();
drZ80.Z80IM = 1;
z80_int(); // try to goto int handler, maybe we won't execute trash there?
}
#endif
}
void z80_exit()
{
#if defined(_USE_MZ80)
mz80shutdown();
#endif
}
#if defined(__DEBUG_PRINT) || defined(WIN32)
void z80_debug(char *dstr)
{
#if defined(_USE_DRZ80)
sprintf(dstr, "%sZ80 state: PC: %04x SP: %04x\n", dstr, drZ80.Z80PC-drZ80.Z80PC_BASE, drZ80.Z80SP-drZ80.Z80SP_BASE);
#endif
}
#endif

28
Pico/sound/sound.h Normal file
View file

@ -0,0 +1,28 @@
// This is part of Pico Library
// (c) Copyright 2004 Dave, All rights reserved.
// (c) Copyright 2006 notaz, All rights reserved.
// Free for non-commercial use.
// For commercial use, separate licencing terms must be obtained.
#ifdef __cplusplus
extern "C" {
#endif
int sound_timers_and_dac(int raster);
int sound_render(int offset, int length);
//int YM2612PicoTick(int n);
// z80 functionality wrappers
void z80_init();
void z80_resetCycles();
void z80_int();
int z80_run(int cycles);
void z80_exit();
#ifdef __cplusplus
} // End of extern "C"
#endif

1899
Pico/sound/ym2612.c Normal file

File diff suppressed because it is too large Load diff

192
Pico/sound/ym2612.h Normal file
View file

@ -0,0 +1,192 @@
/*
header file for software emulation for FM sound generator
*/
#ifndef _H_FM_FM_
#define _H_FM_FM_
/* compiler dependence */
#ifndef UINT8
typedef unsigned char UINT8; /* unsigned 8bit */
typedef unsigned short UINT16; /* unsigned 16bit */
typedef unsigned int UINT32; /* unsigned 32bit */
#endif
#ifndef INT8
typedef signed char INT8; /* signed 8bit */
typedef signed short INT16; /* signed 16bit */
typedef signed int INT32; /* signed 32bit */
#endif
#if 1
/* struct describing a single operator (SLOT) */
typedef struct
{
INT32 *DT; /* #0x00 detune :dt_tab[DT] */
UINT8 ar; /* #0x04 attack rate */
UINT8 d1r; /* #0x05 decay rate */
UINT8 d2r; /* #0x06 sustain rate */
UINT8 rr; /* #0x07 release rate */
UINT32 mul; /* #0x08 multiple :ML_TABLE[ML] */
/* Phase Generator */
UINT32 phase; /* #0x0c phase counter */
UINT32 Incr; /* #0x10 phase step */
UINT8 KSR; /* #0x14 key scale rate :3-KSR */
UINT8 ksr; /* #0x15 key scale rate :kcode>>(3-KSR) */
UINT8 key; /* #0x16 0=last key was KEY OFF, 1=KEY ON */
/* Envelope Generator */
UINT8 state; /* #0x17 phase type: EG_OFF=0, EG_REL, EG_SUS, EG_DEC, EG_ATT */
UINT16 tl; /* #0x18 total level: TL << 3 */
INT16 volume; /* #0x1a envelope counter */
UINT32 sl; /* #0x1c sustain level:sl_table[SL] */
UINT32 eg_pack_ar; /* #0x20 (attack state) */
UINT32 eg_pack_d1r; /* #0x24 (decay state) */
UINT32 eg_pack_d2r; /* #0x28 (sustain state) */
UINT32 eg_pack_rr; /* #0x2c (release state) */
} FM_SLOT;
typedef struct
{
FM_SLOT SLOT[4]; /* four SLOTs (operators) */
UINT8 ALGO; /* algorithm */
UINT8 FB; /* feedback shift */
INT32 op1_out; /* op1 output for feedback */
INT32 mem_value; /* delayed sample (MEM) value */
INT32 pms; /* channel PMS */
UINT8 ams; /* channel AMS */
UINT8 kcode; /* key code: */
UINT32 fc; /* fnum,blk:adjusted to sample rate */
UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
/* LFO */
UINT8 AMmasks; /* AM enable flag */
} FM_CH;
typedef struct
{
int clock; /* master clock (Hz) */
int rate; /* sampling rate (Hz) */
double freqbase; /* frequency base */
UINT8 address; /* address register */
UINT8 status; /* status flag */
UINT8 mode; /* mode CSM / 3SLOT */
UINT8 fn_h; /* freq latch */
int TA; /* timer a */
int TAC; /* timer a maxval */
int TAT; /* timer a ticker */
UINT8 TB; /* timer b */
int TBC; /* timer b maxval */
int TBT; /* timer b ticker */
/* local time tables */
INT32 dt_tab[8][32];/* DeTune table */
} FM_ST;
/***********************************************************/
/* OPN unit */
/***********************************************************/
/* OPN 3slot struct */
typedef struct
{
UINT32 fc[3]; /* fnum3,blk3: calculated */
UINT8 fn_h; /* freq3 latch */
UINT8 kcode[3]; /* key code */
UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
} FM_3SLOT;
/* OPN/A/B common state */
typedef struct
{
FM_ST ST; /* general state */
FM_3SLOT SL3; /* 3 slot mode state */
UINT32 pan; /* fm channels output mask (bit 1 = enable) */
UINT32 eg_cnt; /* #0xb38 global envelope generator counter */
UINT32 eg_timer; /* #0xb3c global envelope generator counter works at frequency = chipclock/64/3 */
UINT32 eg_timer_add; /* #0xb40 step of eg_timer */
/* LFO */
UINT32 lfo_cnt;
UINT32 lfo_inc;
UINT32 lfo_freq[8]; /* LFO FREQ table */
} FM_OPN;
/* here's the virtual YM2612 */
typedef struct
{
UINT8 REGS[0x200]; /* registers (for save states) */
INT32 addr_A1; /* address line A1 */
FM_CH CH[6]; /* channel state (0x168 bytes each)? */
/* dac output (YM2612) */
int dacen;
INT32 dacout;
FM_OPN OPN; /* OPN state */
} YM2612;
#endif
void YM2612Init_(int baseclock, int rate);
void YM2612ResetChip_(void);
void YM2612UpdateOne_(short *buffer, int length, int stereo);
int YM2612Write_(unsigned int a, unsigned int v);
unsigned char YM2612Read_(void);
int YM2612PicoTick_(int n);
void YM2612PicoStateLoad_(void);
void *YM2612GetRegs(void);
#ifndef __GP2X__
#define YM2612Init YM2612Init_
#define YM2612ResetChip YM2612ResetChip_
#define YM2612UpdateOne YM2612UpdateOne_
#define YM2612Write YM2612Write_
#define YM2612Read YM2612Read_
#define YM2612PicoTick YM2612PicoTick_
#define YM2612PicoStateLoad YM2612PicoStateLoad_
#else
/* GP2X specific */
#include "../../platform/gp2x/940ctl_ym2612.h"
extern int PicoOpt;
#define YM2612Init(baseclock,rate) { \
if (PicoOpt&0x200) YM2612Init_940(baseclock, rate); \
else YM2612Init_(baseclock, rate); \
}
#define YM2612ResetChip() { \
if (PicoOpt&0x200) YM2612ResetChip_940(); \
else YM2612ResetChip_(); \
}
#define YM2612UpdateOne(buffer,length,stereo) { \
if (PicoOpt&0x200) YM2612UpdateOne_940(buffer, length, stereo); \
else YM2612UpdateOne_(buffer, length, stereo); \
}
#define YM2612Write(a,v) \
(PicoOpt&0x200) ? YM2612Write_940(a, v) : YM2612Write_(a, v)
#define YM2612Read() \
(PicoOpt&0x200) ? YM2612Read_940() : YM2612Read_()
#define YM2612PicoTick(n) \
(PicoOpt&0x200) ? YM2612PicoTick_940(n) : YM2612PicoTick_(n)
#define YM2612PicoStateLoad() { \
if (PicoOpt&0x200) YM2612PicoStateLoad_940(); \
else YM2612PicoStateLoad_(); \
}
#endif /* __GP2X__ */
#endif /* _H_FM_FM_ */

915
Pico/sound/ym2612.s Normal file
View file

@ -0,0 +1,915 @@
@ this is a rewrite of MAME's ym2612 code, in particular this is only the main sample-generatin loop.
@ it does not seem to give much performance increase (if any at all), so don't use it if it causes trouble.
@ - notaz, 2006
.equiv SLOT1, 0
.equiv SLOT2, 2
.equiv SLOT3, 1
.equiv SLOT4, 3
.equiv SLOT_STRUCT_SIZE, 0x30
.equiv TL_TAB_LEN, 0x1A00
.equiv EG_ATT, 4
.equiv EG_DEC, 3
.equiv EG_SUS, 2
.equiv EG_REL, 1
.equiv EG_OFF, 0
.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, 25 /* 7.25 fixed point (LFO calculations) */
.equiv ENV_QUIET, (2*13*256/8)/2
@ 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
ldrb r2, [r5,#0x17] @ state
mov r3, #1 @ 1ci
cmp r2, #1
blt 5f @ EG_OFF
beq 3f @ EG_REL
cmp r2, #3
blt 2f @ EG_SUS
beq 1f @ EG_DEC
0: @ EG_ATT
ldr r2, [r5,#0x20] @ eg_pack_ar (1ci)
mov r0, r2, lsr #24
mov r3, r3, lsl r0
sub r3, r3, #1
tst r1, r3
bne 5f @ do smth for tl problem (set on init?)
mov r3, r1, lsr r0
ldrh r0, [r5,#0x1a] @ volume, unsigned (0-1023)
and r3, r3, #7
add r3, r3, r3, lsl #1
mov r3, r2, lsr r3
and r3, r3, #7 @ shift for eg_inc calculation
mvn r2, r0
mov r2, r2, lsl r3
add r0, r0, r2, asr #5
cmp r0, #0 @ if (volume <= MIN_ATT_INDEX)
movle r3, #EG_DEC
strleb r3, [r5,#0x17] @ state
movle r0, #0
b 4f
1: @ EG_DEC
ldr r2, [r5,#0x24] @ eg_pack_d1r (1ci)
mov r0, r2, lsr #24
mov r3, r3, lsl r0
sub r3, r3, #1
tst r1, r3
bne 5f @ do smth for tl problem (set on init?)
mov r3, r1, lsr r0
ldrh r0, [r5,#0x1a] @ volume
and r3, r3, #7
add r3, r3, r3, lsl #1
mov r3, r2, lsr r3
and r3, r3, #7 @ shift for eg_inc calculation
mov r2, #1
mov r3, r2, lsl r3
ldr r2, [r5,#0x1c] @ sl (can be 16bit?)
add r0, r0, r3, asr #1
cmp r0, r2 @ if ( volume >= (INT32) SLOT->sl )
movge r3, #EG_SUS
strgeb r3, [r5,#0x17] @ state
b 4f
2: @ EG_SUS
ldr r2, [r5,#0x28] @ eg_pack_d2r (1ci)
mov r0, r2, lsr #24
mov r3, r3, lsl r0
sub r3, r3, #1
tst r1, r3
bne 5f @ do smth for tl problem (set on init?)
mov r3, r1, lsr r0
ldrh r0, [r5,#0x1a] @ volume
and r3, r3, #7
add r3, r3, r3, lsl #1
mov r3, r2, lsr r3
and r3, r3, #7 @ shift for eg_inc calculation
mov r2, #1
mov r3, r2, lsl r3
add r0, r0, r3, asr #1
mov r2, #1024
sub r2, r2, #1 @ r2 = MAX_ATT_INDEX
cmp r0, r2 @ if ( volume >= MAX_ATT_INDEX )
movge r0, r2
b 4f
3: @ EG_REL
ldr r2, [r5,#0x2c] @ eg_pack_rr (1ci)
mov r0, r2, lsr #24
mov r3, r3, lsl r0
sub r3, r3, #1
tst r1, r3
bne 5f @ do smth for tl problem (set on init?)
mov r3, r1, lsr r0
ldrh r0, [r5,#0x1a] @ volume
and r3, r3, #7
add r3, r3, r3, lsl #1
mov r3, r2, lsr r3
and r3, r3, #7 @ shift for eg_inc calculation
mov r2, #1
mov r3, r2, lsl r3
add r0, r0, r3, asr #1
mov r2, #1024
sub r2, r2, #1 @ r2 = MAX_ATT_INDEX
cmp r0, r2 @ if ( volume >= MAX_ATT_INDEX )
movge r0, r2
movge r3, #EG_OFF
strgeb r3, [r5,#0x17] @ state
4:
ldrh r3, [r5,#0x18] @ tl
strh r0, [r5,#0x1a] @ volume
.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
5:
.endm
@ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt, r3=scratch
.macro advance_lfo_m
mov r2, r2, lsr #LFO_SH
cmp r2, r1, lsr #LFO_SH
beq 0f
and r3, r2, #0x3f
cmp r2, #0x40
rsbge r3, r3, #0x3f
bic r12,r12, #0xff000000 @ lfo_ampm &= 0xff
orr r12,r12, r3, lsl #1+24
mov r2, r2, lsr #2
cmp r2, r1, lsr #LFO_SH+2
bicne r12,r12, #0xff0000
orrne r12,r12, r2, lsl #16
0:
.endm
@ result goes to r1, trashes r2
.macro make_eg_out slot
tst r12, #8
tstne r12, #(1<<(\slot+8))
.if \slot == SLOT1
mov r1, r6, lsl #16
mov r1, r1, lsr #17
.elseif \slot == SLOT2
mov r1, r6, lsr #17
.elseif \slot == SLOT3
mov r1, r7, lsl #16
mov r1, r1, lsr #17
.elseif \slot == SLOT4
mov r1, r7, lsr #17
.endif
andne r2, r12, #0xc0
movne r2, r2, lsr #6
addne r2, r2, #24
addne r1, r1, r12, lsr r2
.endm
.macro lookup_tl r
tst \r, #0x100
eorne \r, \r, #0xff @ if (sin & 0x100) sin = 0xff - (sin&0xff);
tst \r, #0x200
and \r, \r, #0xff
orr \r, \r, r1, lsl #8
mov \r, \r, lsl #1
ldrh \r, [r3, \r] @ 2ci if ne
rsbne \r, \r, #0
.endm
@ 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, r5=scratch, r6-r7=vol_out[4], r10=op1_out
.macro upd_algo0_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r2, [lr, #0x18]
ldr r0, [lr, #0x38] @ mem (signed)
mov r2, r2, lsr #16
add r0, r2, r0, lsr #1
lookup_tl r0 @ r0=c2
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
movcs r2, #0
bcs 2f
ldr r2, [lr, #0x14] @ 1ci
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2 @ r2=mem
2:
str r2, [lr, #0x38] @ mem
.endm
.macro upd_algo1_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r2, [lr, #0x18]
ldr r0, [lr, #0x38] @ mem (signed)
mov r2, r2, lsr #16
add r0, r2, r0, lsr #1
lookup_tl r0 @ r0=c2
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
movcs r2, #0
bcs 2f
ldr r2, [lr, #0x14] @ 1ci
mov r2, r2, lsr #16
lookup_tl r2 @ r2=mem
2:
add r2, r2, r10, asr #16
str r2, [lr, #0x38]
.endm
.macro upd_algo2_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r2, [lr, #0x18]
ldr r0, [lr, #0x38] @ mem (signed)
mov r2, r2, lsr #16
add r0, r2, r0, lsr #1
lookup_tl r0 @ r0=c2
0:
add r0, r0, r10, asr #16
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
movcs r2, #0
bcs 2f
ldr r2, [lr, #0x14]
mov r2, r2, lsr #16 @ 1ci
lookup_tl r2 @ r2=mem
2:
str r2, [lr, #0x38] @ mem
.endm
.macro upd_algo3_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
ldr r2, [lr, #0x38] @ mem (for future)
movcs r0, r2
bcs 0f
ldr r0, [lr, #0x18] @ 1ci
mov r0, r0, lsr #16
lookup_tl r0 @ r0=c2
0:
add r0, r0, r2
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
movcs r2, #0
bcs 2f
ldr r2, [lr, #0x14]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2 @ r2=mem
2:
str r2, [lr, #0x38] @ mem
.endm
.macro upd_algo4_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r0, [lr, #0x18]
mov r0, r0, lsr #16 @ 1ci
lookup_tl r0 @ r0=c2
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 1f
ldr r2, [lr, #0x1c]
mov r0, r0, lsr #1
add r0, r0, r2, lsr #16
lookup_tl r0 @ r0=output smp
1:
@ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
bcs 2f
ldr r2, [lr, #0x14]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2
add r0, r0, r2 @ add to smp
2:
.endm
.macro upd_algo5_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r2, [lr, #0x18]
ldr r0, [lr, #0x38] @ mem (signed)
mov r2, r2, lsr #16
add r0, r2, r0, lsr #1
lookup_tl r0 @ r0=output smp
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
bcs 1f
ldr r2, [lr, #0x1c]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2
add r0, r0, r2 @ add to smp
1: @ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
bcs 2f
ldr r2, [lr, #0x14]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2
add r0, r0, r2 @ add to smp
2:
mov r1, r10, asr #16
str r1, [lr, #0x38] @ mem
.endm
.macro upd_algo6_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r0, [lr, #0x18]
mov r0, r0, lsr #16 @ 1ci
lookup_tl r0 @ r0=output smp
0:
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
bcs 1f
ldr r2, [lr, #0x1c]
mov r2, r2, lsr #16 @ 1ci
lookup_tl r2
add r0, r0, r2 @ add to smp
1: @ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
bcs 2f
ldr r2, [lr, #0x14]
mov r5, r10, lsr #17
add r2, r5, r2, lsr #16
lookup_tl r2
add r0, r0, r2 @ add to smp
2:
.endm
.macro upd_algo7_m
@ SLOT3
make_eg_out SLOT3
cmp r1, #ENV_QUIET
movcs r0, #0
bcs 0f
ldr r0, [lr, #0x18]
mov r0, r0, lsr #16 @ 1ci
lookup_tl r0 @ r0=output smp
0:
add r0, r0, r10, asr #16
@ SLOT4
make_eg_out SLOT4
cmp r1, #ENV_QUIET
bcs 1f
ldr r2, [lr, #0x1c]
mov r2, r2, lsr #16 @ 1ci
lookup_tl r2
add r0, r0, r2 @ add to smp
1: @ SLOT2
make_eg_out SLOT2
cmp r1, #ENV_QUIET
bcs 2f
ldr r2, [lr, #0x14]
mov r2, r2, lsr #16 @ 1ci
lookup_tl r2
add r0, r0, r2 @ add to smp
2:
.endm
.macro upd_slot1_m
make_eg_out SLOT1
cmp r1, #ENV_QUIET
movcs r10, r10, lsl #16 @ ct->op1_out <<= 16; // op1_out0 = op1_out1; op1_out1 = 0;
bcs 0f
ands r2, r12, #0xf000
moveq r0, #0
movne r2, r2, lsr #12
addne r0, r10, r10, lsl #16
movne r0, r0, asr #16
movne r0, r0, lsl r2
ldr r2, [lr, #0x10]
mov r0, r0, lsr #16
add r0, r0, r2, lsr #16
lookup_tl r0
mov r10,r10,lsl #16 @ ct->op1_out <<= 16;
mov r0, r0, lsl #16
orr r10,r10, r0, lsr #16
0:
.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
ldr r3, =ym_sin_tab
ldr r5, =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
ldr r3, =ym_sin_tab
ldr r5, =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
ldr r3, =ym_sin_tab
ldr r5, =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
ldr r3, =ym_sin_tab
ldr r5, =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
ldr r3, =ym_sin_tab
ldr r5, =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
ldr r3, =ym_sin_tab
ldr r5, =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
ldr r3, =ym_sin_tab
ldr r5, =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
ldr r3, =ym_sin_tab
ldr r5, =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
ldr r3, =ym_sin_tab
ldr r5, =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)|algo, r5=tl_tab/slot,
@ r6-r7=vol_out[4], r8=eg_timer, r9=eg_timer_add[31:16], r10=op1_out, r11=buffer
.global chan_render_loop @ chan_rend_context *ct, int *buffer, int length
chan_render_loop:
stmfd sp!, {r4-r11,lr}
mov lr, r0
mov r4, r2, lsl #8 @ no more 24 bits here
ldr r12, [lr, #0x4c]
ldr r0, [lr, #0x50]
mov r11, r1
and r0, r0, #7
orr r4, r4, r0 @ (length<<8)|algo
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
tst r12, #8 @ lfo?
beq crl_loop
crl_loop_lfo:
add r0, lr, #0x30
ldmia r0, {r1,r2}
add r2, r2, r1
str r2, [lr, #0x30]
@ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt
advance_lfo_m
crl_loop:
subs r4, r4, #0x100
bmi crl_loop_end
@ -- 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
@ SLOT1 (0)
@ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
update_eg_phase_slot SLOT1
add r5, r5, #SLOT_STRUCT_SIZE*2 @ SLOT2 (2)
update_eg_phase_slot SLOT2
sub r5, r5, #SLOT_STRUCT_SIZE @ SLOT3 (1)
update_eg_phase_slot SLOT3
add r5, r5, #SLOT_STRUCT_SIZE*2 @ SLOT4 (3)
update_eg_phase_slot SLOT4
cmp r8, #EG_TIMER_OVERFLOW
subcs r5, r5, #SLOT_STRUCT_SIZE*3
bcs eg_loop
str r1, [lr, #0x3c]
eg_done:
@ -- disabled? --
and r0, r12, #0xC
cmp r0, #0xC
beq crl_loop_lfo
cmp r0, #0x4
beq crl_loop
@ -- SLOT1 --
ldr r3, =ym_tl_tab
@ 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=tl_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out
upd_slot1_m
@ -- SLOT2+ --
and r0, r4, #7
ldr pc, [pc, r0, lsl #2]
nop
.word crl_algo0
.word crl_algo1
.word crl_algo2
.word crl_algo3
.word crl_algo4
.word crl_algo5
.word crl_algo6
.word crl_algo7
.pool
crl_algo0:
upd_algo0_m
b crl_algo_done
.pool
crl_algo1:
upd_algo1_m
b crl_algo_done
.pool
crl_algo2:
upd_algo2_m
b crl_algo_done
.pool
crl_algo3:
upd_algo3_m
b crl_algo_done
.pool
crl_algo4:
upd_algo4_m
b crl_algo_done
.pool
crl_algo5:
upd_algo5_m
b crl_algo_done
.pool
crl_algo6:
upd_algo6_m
b crl_algo_done
.pool
crl_algo7:
upd_algo7_m
.pool
crl_algo_done:
@ -- WRITE SAMPLE --
tst r0, r0
beq ctl_sample_skip
tst r12, #1
beq ctl_sample_mono
tst r12, #0x20 @ L
ldrne r1, [r11]
addeq r11, r11, #4
addne r1, r0, r1
strne r1, [r11], #4
tst r12, #0x10 @ R
ldrne r1, [r11]
addeq r11, r11, #4
addne r1, r0, r1
strne r1, [r11], #4
b crl_do_phase
ctl_sample_skip:
and r1, r12, #1
add r1, r1, #1
add r11,r11, r1, lsl #2
b crl_do_phase
ctl_sample_mono:
ldr r1, [r11]
add r1, r0, r1
str r1, [r11], #4
crl_do_phase:
@ -- PHASE UPDATE --
add r5, lr, #0x10
ldmia r5, {r0-r1}
add r5, lr, #0x20
ldmia r5, {r2-r3}
add r5, lr, #0x10
add r0, r0, r2
add r1, r1, r3
stmia r5!,{r0-r1}
ldmia r5, {r0-r1}
add r5, lr, #0x28
ldmia r5, {r2-r3}
add r5, lr, #0x18
add r0, r0, r2
add r1, r1, r3
stmia r5, {r0-r1}
tst r12, #8
bne crl_loop_lfo
b crl_loop
crl_loop_end:
str r8, [lr, #0x44] @ eg_timer
str r12, [lr, #0x4c] @ pack (for lfo_ampm)
str r10, [lr, #0x54] @ op1_out
ldmfd sp!, {r4-r11,pc}
.pool