mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-06 15:48:05 -04:00
initial import
git-svn-id: file:///home/notaz/opt/svn/PicoDrive@2 be3aeb3a-fb24-0410-a615-afba39da0efa
This commit is contained in:
parent
2cadbd5e56
commit
cc68a136aa
341 changed files with 180839 additions and 0 deletions
347
Pico/sound/sn76496.c
Normal file
347
Pico/sound/sn76496.c
Normal 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
8
Pico/sound/sn76496.h
Normal 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
390
Pico/sound/sound.c
Normal 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
28
Pico/sound/sound.h
Normal 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
1899
Pico/sound/ym2612.c
Normal file
File diff suppressed because it is too large
Load diff
192
Pico/sound/ym2612.h
Normal file
192
Pico/sound/ym2612.h
Normal 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
915
Pico/sound/ym2612.s
Normal 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
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue