mirror of
https://github.com/RaySollium99/picodrive.git
synced 2025-09-05 07:17:45 -04:00
lowercasing filenames, part3
git-svn-id: file:///home/notaz/opt/svn/PicoDrive@576 be3aeb3a-fb24-0410-a615-afba39da0efa
This commit is contained in:
parent
d158df697d
commit
1cfc5cc4ce
71 changed files with 0 additions and 0 deletions
74
pico/sound/mix.c
Normal file
74
pico/sound/mix.c
Normal file
|
@ -0,0 +1,74 @@
|
|||
// some code for sample mixing
|
||||
// (c) Copyright 2006-2007, Grazvydas "notaz" Ignotas
|
||||
|
||||
#define MAXOUT (+32767)
|
||||
#define MINOUT (-32768)
|
||||
|
||||
/* limitter */
|
||||
#define Limit(val, max,min) { \
|
||||
if ( val > max ) val = max; \
|
||||
else if ( val < min ) val = min; \
|
||||
}
|
||||
|
||||
|
||||
void mix_32_to_16l_stereo(short *dest, int *src, int count)
|
||||
{
|
||||
int l, r;
|
||||
|
||||
for (; count > 0; count--)
|
||||
{
|
||||
l = r = *dest;
|
||||
l += *src++;
|
||||
r += *src++;
|
||||
Limit( l, MAXOUT, MINOUT );
|
||||
Limit( r, MAXOUT, MINOUT );
|
||||
*dest++ = l;
|
||||
*dest++ = r;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mix_32_to_16_mono(short *dest, int *src, int count)
|
||||
{
|
||||
int l;
|
||||
|
||||
for (; count > 0; count--)
|
||||
{
|
||||
l = *dest;
|
||||
l += *src++;
|
||||
Limit( l, MAXOUT, MINOUT );
|
||||
*dest++ = l;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void mix_16h_to_32(int *dest_buf, short *mp3_buf, int count)
|
||||
{
|
||||
while (count--)
|
||||
{
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
void mix_16h_to_32_s1(int *dest_buf, short *mp3_buf, int count)
|
||||
{
|
||||
count >>= 1;
|
||||
while (count--)
|
||||
{
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
mp3_buf += 1*2;
|
||||
}
|
||||
}
|
||||
|
||||
void mix_16h_to_32_s2(int *dest_buf, short *mp3_buf, int count)
|
||||
{
|
||||
count >>= 1;
|
||||
while (count--)
|
||||
{
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
*dest_buf++ += *mp3_buf++ >> 1;
|
||||
mp3_buf += 3*2;
|
||||
}
|
||||
}
|
||||
|
10
pico/sound/mix.h
Normal file
10
pico/sound/mix.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
//void mix_32_to_32(int *dest, int *src, int count);
|
||||
void mix_16h_to_32(int *dest, short *src, int count);
|
||||
void mix_16h_to_32_s1(int *dest, short *src, int count);
|
||||
void mix_16h_to_32_s2(int *dest, short *src, int count);
|
||||
void mix_32_to_16l_stereo(short *dest, int *src, int count);
|
||||
void mix_32_to_16_mono(short *dest, int *src, int count);
|
||||
|
||||
extern int mix_32_to_16l_level;
|
||||
void mix_32_to_16l_stereo_lvl(short *dest, int *src, int count);
|
367
pico/sound/mix_arm.s
Normal file
367
pico/sound/mix_arm.s
Normal file
|
@ -0,0 +1,367 @@
|
|||
@ vim:filetype=armasm
|
||||
|
||||
@ Generic routines for mixing audio samples
|
||||
@ (c) Copyright 2007, Grazvydas "notaz" Ignotas
|
||||
|
||||
|
||||
.text
|
||||
.align 4
|
||||
|
||||
@ this assumes src is word aligned
|
||||
.global mix_16h_to_32 @ int *dest, short *src, int count
|
||||
|
||||
mix_16h_to_32:
|
||||
stmfd sp!, {r4-r6,lr}
|
||||
/*
|
||||
tst r1, #2
|
||||
beq m16_32_mo_unalw
|
||||
ldrsh r4, [r1], #2
|
||||
ldr r3, [r0]
|
||||
sub r2, r2, #1
|
||||
add r3, r3, r4, asr #1
|
||||
str r3, [r0], #4
|
||||
*/
|
||||
m16_32_mo_unalw:
|
||||
subs r2, r2, #4
|
||||
bmi m16_32_end
|
||||
|
||||
m16_32_loop:
|
||||
ldmia r0, {r3-r6}
|
||||
ldmia r1!,{r12,lr}
|
||||
subs r2, r2, #4
|
||||
add r4, r4, r12,asr #17 @ we use half volume
|
||||
mov r12,r12,lsl #16
|
||||
add r3, r3, r12,asr #17
|
||||
add r6, r6, lr, asr #17
|
||||
mov lr, lr, lsl #16
|
||||
add r5, r5, lr, asr #17
|
||||
stmia r0!,{r3-r6}
|
||||
bpl m16_32_loop
|
||||
|
||||
m16_32_end:
|
||||
tst r2, #2
|
||||
beq m16_32_no_unal2
|
||||
ldr r5, [r1], #4
|
||||
ldmia r0, {r3,r4}
|
||||
mov r12,r5, lsl #16
|
||||
add r3, r3, r12,asr #17
|
||||
add r4, r4, r5, asr #17
|
||||
stmia r0!,{r3,r4}
|
||||
|
||||
m16_32_no_unal2:
|
||||
tst r2, #1
|
||||
ldmeqfd sp!, {r4-r6,pc}
|
||||
ldrsh r4, [r1], #2
|
||||
ldr r3, [r0]
|
||||
add r3, r3, r4, asr #1
|
||||
str r3, [r0], #4
|
||||
|
||||
ldmfd sp!, {r4-r6,lr}
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
.global mix_16h_to_32_s1 @ int *dest, short *src, int count
|
||||
|
||||
mix_16h_to_32_s1:
|
||||
stmfd sp!, {r4-r6,lr}
|
||||
|
||||
subs r2, r2, #4
|
||||
bmi m16_32_s1_end
|
||||
|
||||
m16_32_s1_loop:
|
||||
ldmia r0, {r3-r6}
|
||||
ldr r12,[r1], #8
|
||||
ldr lr, [r1], #8
|
||||
subs r2, r2, #4
|
||||
add r4, r4, r12,asr #17
|
||||
mov r12,r12,lsl #16
|
||||
add r3, r3, r12,asr #17 @ we use half volume
|
||||
add r6, r6, lr, asr #17
|
||||
mov lr, lr, lsl #16
|
||||
add r5, r5, lr, asr #17
|
||||
stmia r0!,{r3-r6}
|
||||
bpl m16_32_s1_loop
|
||||
|
||||
m16_32_s1_end:
|
||||
tst r2, #2
|
||||
beq m16_32_s1_no_unal2
|
||||
ldr r5, [r1], #8
|
||||
ldmia r0, {r3,r4}
|
||||
mov r12,r5, lsl #16
|
||||
add r3, r3, r12,asr #17
|
||||
add r4, r4, r5, asr #17
|
||||
stmia r0!,{r3,r4}
|
||||
|
||||
m16_32_s1_no_unal2:
|
||||
tst r2, #1
|
||||
ldmeqfd sp!, {r4-r6,pc}
|
||||
ldrsh r4, [r1], #2
|
||||
ldr r3, [r0]
|
||||
add r3, r3, r4, asr #1
|
||||
str r3, [r0], #4
|
||||
|
||||
ldmfd sp!, {r4-r6,lr}
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
.global mix_16h_to_32_s2 @ int *dest, short *src, int count
|
||||
|
||||
mix_16h_to_32_s2:
|
||||
stmfd sp!, {r4-r6,lr}
|
||||
|
||||
subs r2, r2, #4
|
||||
bmi m16_32_s2_end
|
||||
|
||||
m16_32_s2_loop:
|
||||
ldmia r0, {r3-r6}
|
||||
ldr r12,[r1], #16
|
||||
ldr lr, [r1], #16
|
||||
subs r2, r2, #4
|
||||
add r4, r4, r12,asr #17
|
||||
mov r12,r12,lsl #16
|
||||
add r3, r3, r12,asr #17 @ we use half volume
|
||||
add r6, r6, lr, asr #17
|
||||
mov lr, lr, lsl #16
|
||||
add r5, r5, lr, asr #17
|
||||
stmia r0!,{r3-r6}
|
||||
bpl m16_32_s2_loop
|
||||
|
||||
m16_32_s2_end:
|
||||
tst r2, #2
|
||||
beq m16_32_s2_no_unal2
|
||||
ldr r5, [r1], #16
|
||||
ldmia r0, {r3,r4}
|
||||
mov r12,r5, lsl #16
|
||||
add r3, r3, r12,asr #17
|
||||
add r4, r4, r5, asr #17
|
||||
stmia r0!,{r3,r4}
|
||||
|
||||
m16_32_s2_no_unal2:
|
||||
tst r2, #1
|
||||
ldmeqfd sp!, {r4-r6,pc}
|
||||
ldrsh r4, [r1], #2
|
||||
ldr r3, [r0]
|
||||
add r3, r3, r4, asr #1
|
||||
str r3, [r0], #4
|
||||
|
||||
ldmfd sp!, {r4-r6,lr}
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
@ limit
|
||||
@ reg=int_sample, lr=1, r3=tmp, kills flags
|
||||
.macro Limit reg
|
||||
add r3, lr, \reg, asr #15
|
||||
bics r3, r3, #1 @ in non-overflow conditions r3 is 0 or 1
|
||||
movne \reg, #0x8000
|
||||
subpl \reg, \reg, #1
|
||||
.endm
|
||||
|
||||
|
||||
@ limit and shift up by 16
|
||||
@ reg=int_sample, lr=1, r3=tmp, kills flags
|
||||
.macro Limitsh reg
|
||||
@ movs r4, r3, asr #16
|
||||
@ cmnne r4, #1
|
||||
@ beq c32_16_no_overflow
|
||||
@ tst r4, r4
|
||||
@ mov r3, #0x8000
|
||||
@ subpl r3, r3, #1
|
||||
|
||||
add r3, lr, \reg, asr #15
|
||||
bics r3, r3, #1 @ in non-overflow conditions r3 is 0 or 1
|
||||
moveq \reg, \reg, lsl #16
|
||||
movne \reg, #0x80000000
|
||||
subpl \reg, \reg, #0x00010000
|
||||
.endm
|
||||
|
||||
|
||||
@ mix 32bit audio (with 16bits really used, upper bits indicate overflow) with normal 16 bit audio with left channel only
|
||||
@ warning: this function assumes dest is word aligned
|
||||
.global mix_32_to_16l_stereo @ short *dest, int *src, int count
|
||||
|
||||
mix_32_to_16l_stereo:
|
||||
stmfd sp!, {r4-r8,lr}
|
||||
|
||||
mov lr, #1
|
||||
|
||||
mov r2, r2, lsl #1
|
||||
subs r2, r2, #4
|
||||
bmi m32_16l_st_end
|
||||
|
||||
m32_16l_st_loop:
|
||||
ldmia r0, {r8,r12}
|
||||
ldmia r1!, {r4-r7}
|
||||
mov r8, r8, lsl #16
|
||||
mov r12,r12,lsl #16
|
||||
add r4, r4, r8, asr #16
|
||||
add r5, r5, r8, asr #16
|
||||
add r6, r6, r12,asr #16
|
||||
add r7, r7, r12,asr #16
|
||||
Limitsh r4
|
||||
Limitsh r5
|
||||
Limitsh r6
|
||||
Limitsh r7
|
||||
subs r2, r2, #4
|
||||
orr r4, r5, r4, lsr #16
|
||||
orr r5, r7, r6, lsr #16
|
||||
stmia r0!, {r4,r5}
|
||||
bpl m32_16l_st_loop
|
||||
|
||||
m32_16l_st_end:
|
||||
@ check for remaining bytes to convert
|
||||
tst r2, #2
|
||||
beq m32_16l_st_no_unal2
|
||||
ldrsh r6, [r0]
|
||||
ldmia r1!,{r4,r5}
|
||||
add r4, r4, r6
|
||||
add r5, r5, r6
|
||||
Limitsh r4
|
||||
Limitsh r5
|
||||
orr r4, r5, r4, lsr #16
|
||||
str r4, [r0], #4
|
||||
|
||||
m32_16l_st_no_unal2:
|
||||
ldmfd sp!, {r4-r8,lr}
|
||||
bx lr
|
||||
|
||||
|
||||
@ mix 32bit audio (with 16bits really used, upper bits indicate overflow) with normal 16 bit audio (for mono sound)
|
||||
.global mix_32_to_16_mono @ short *dest, int *src, int count
|
||||
|
||||
mix_32_to_16_mono:
|
||||
stmfd sp!, {r4-r8,lr}
|
||||
|
||||
mov lr, #1
|
||||
|
||||
@ check if dest is word aligned
|
||||
tst r0, #2
|
||||
beq m32_16_mo_no_unalw
|
||||
ldrsh r5, [r0]
|
||||
ldr r4, [r1], #4
|
||||
sub r2, r2, #1
|
||||
add r4, r4, r5
|
||||
Limit r4
|
||||
strh r4, [r0], #2
|
||||
|
||||
m32_16_mo_no_unalw:
|
||||
subs r2, r2, #4
|
||||
bmi m32_16_mo_end
|
||||
|
||||
m32_16_mo_loop:
|
||||
ldmia r0, {r8,r12}
|
||||
ldmia r1!, {r4-r7}
|
||||
add r5, r5, r8, asr #16
|
||||
mov r8, r8, lsl #16
|
||||
add r4, r4, r8, asr #16
|
||||
add r7, r7, r12,asr #16
|
||||
mov r12,r12,lsl #16
|
||||
add r6, r6, r12,asr #16
|
||||
Limitsh r4
|
||||
Limitsh r5
|
||||
Limitsh r6
|
||||
Limitsh r7
|
||||
subs r2, r2, #4
|
||||
orr r4, r5, r4, lsr #16
|
||||
orr r5, r7, r6, lsr #16
|
||||
stmia r0!, {r4,r5}
|
||||
bpl m32_16_mo_loop
|
||||
|
||||
m32_16_mo_end:
|
||||
@ check for remaining bytes to convert
|
||||
tst r2, #2
|
||||
beq m32_16_mo_no_unal2
|
||||
ldr r6, [r0]
|
||||
ldmia r1!,{r4,r5}
|
||||
add r5, r5, r6, asr #16
|
||||
mov r6, r6, lsl #16
|
||||
add r4, r4, r6, asr #16
|
||||
Limitsh r4
|
||||
Limitsh r5
|
||||
orr r4, r5, r4, lsr #16
|
||||
str r4, [r0], #4
|
||||
|
||||
m32_16_mo_no_unal2:
|
||||
tst r2, #1
|
||||
ldmeqfd sp!, {r4-r8,pc}
|
||||
ldrsh r5, [r0]
|
||||
ldr r4, [r1], #4
|
||||
add r4, r4, r5
|
||||
Limit r4
|
||||
strh r4, [r0], #2
|
||||
|
||||
ldmfd sp!, {r4-r8,lr}
|
||||
bx lr
|
||||
|
||||
|
||||
|
||||
.data
|
||||
.align 4
|
||||
|
||||
.global mix_32_to_16l_level
|
||||
mix_32_to_16l_level:
|
||||
.word 0
|
||||
|
||||
.text
|
||||
.align 4
|
||||
|
||||
@ same as mix_32_to_16l_stereo, but with additional shift
|
||||
.global mix_32_to_16l_stereo_lvl @ short *dest, int *src, int count
|
||||
|
||||
mix_32_to_16l_stereo_lvl:
|
||||
stmfd sp!, {r4-r9,lr}
|
||||
|
||||
ldr r9, =mix_32_to_16l_level
|
||||
mov lr, #1
|
||||
ldr r9, [r9]
|
||||
|
||||
mov r2, r2, lsl #1
|
||||
subs r2, r2, #4
|
||||
bmi m32_16l_st_l_end
|
||||
|
||||
m32_16l_st_l_loop:
|
||||
ldmia r0, {r8,r12}
|
||||
ldmia r1!, {r4-r7}
|
||||
mov r8, r8, lsl #16
|
||||
mov r12,r12,lsl #16
|
||||
add r4, r4, r8, asr #16
|
||||
add r5, r5, r8, asr #16
|
||||
add r6, r6, r12,asr #16
|
||||
add r7, r7, r12,asr #16
|
||||
mov r4, r4, asr r9
|
||||
mov r5, r5, asr r9
|
||||
mov r6, r6, asr r9
|
||||
mov r7, r7, asr r9
|
||||
Limitsh r4
|
||||
Limitsh r5
|
||||
Limitsh r6
|
||||
Limitsh r7
|
||||
subs r2, r2, #4
|
||||
orr r4, r5, r4, lsr #16
|
||||
orr r5, r7, r6, lsr #16
|
||||
stmia r0!, {r4,r5}
|
||||
bpl m32_16l_st_l_loop
|
||||
|
||||
m32_16l_st_l_end:
|
||||
@ check for remaining bytes to convert
|
||||
tst r2, #2
|
||||
beq m32_16l_st_l_no_unal2
|
||||
ldrsh r6, [r0]
|
||||
ldmia r1!,{r4,r5}
|
||||
add r4, r4, r6
|
||||
add r5, r5, r6
|
||||
mov r4, r4, asr r9
|
||||
mov r5, r5, asr r9
|
||||
Limitsh r4
|
||||
Limitsh r5
|
||||
orr r4, r5, r4, lsr #16
|
||||
str r4, [r0], #4
|
||||
|
||||
m32_16l_st_l_no_unal2:
|
||||
ldmfd sp!, {r4-r9,lr}
|
||||
bx lr
|
||||
|
||||
|
344
pico/sound/sn76496.c
Normal file
344
pico/sound/sn76496.c
Normal file
|
@ -0,0 +1,344 @@
|
|||
/***************************************************************************
|
||||
|
||||
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;
|
||||
|
||||
if ((out /= STEP)) // will be optimized to shift; max 0x47ff = 18431
|
||||
*buffer += out;
|
||||
if(stereo) buffer+=2; // only left for stereo, to be mixed to right later
|
||||
else buffer++;
|
||||
|
||||
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
|
631
pico/sound/sound.c
Normal file
631
pico/sound/sound.c
Normal file
|
@ -0,0 +1,631 @@
|
|||
// This is part of Pico Library
|
||||
|
||||
// (c) Copyright 2004 Dave, All rights reserved.
|
||||
// (c) Copyright 2006,2007 notaz, All rights reserved.
|
||||
// Free for non-commercial use.
|
||||
|
||||
// For commercial use, separate licencing terms must be obtained.
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include "ym2612.h"
|
||||
#include "sn76496.h"
|
||||
#include "../pico_int.h"
|
||||
#include "../cd/pcm.h"
|
||||
#include "mix.h"
|
||||
|
||||
void (*PsndMix_32_to_16l)(short *dest, int *src, int count) = mix_32_to_16l_stereo;
|
||||
|
||||
// master int buffer to mix to
|
||||
static int PsndBuffer[2*44100/50];
|
||||
|
||||
// dac
|
||||
static unsigned short dac_info[312+4]; // pppppppp ppppllll, p - pos in buff, l - length to write for this sample
|
||||
|
||||
// cdda output buffer
|
||||
short cdda_out_buffer[2*1152];
|
||||
|
||||
// for Pico
|
||||
int PsndRate=0;
|
||||
int PsndLen=0; // number of mono samples, multiply by 2 for stereo
|
||||
int PsndLen_exc_add=0; // this is for non-integer sample counts per line, eg. 22050/60
|
||||
int PsndLen_exc_cnt=0;
|
||||
int PsndDacLine=0;
|
||||
short *PsndOut=NULL; // PCM data buffer
|
||||
|
||||
// timers
|
||||
int timer_a_next_oflow, timer_a_step; // in z80 cycles
|
||||
int timer_b_next_oflow, timer_b_step;
|
||||
|
||||
// sn76496
|
||||
extern int *sn76496_regs;
|
||||
|
||||
|
||||
static void dac_recalculate(void)
|
||||
{
|
||||
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 = -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;
|
||||
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++;
|
||||
if (PsndLen_exc_add) len++;
|
||||
dac_info[224] = (pos<<4)|len;
|
||||
}
|
||||
mid = (dac_info[lines-1] & 0xfff0) + ((dac_info[lines-1] & 0xf) << 4);
|
||||
for (i = lines; i < sizeof(dac_info) / sizeof(dac_info[0]); i++)
|
||||
dac_info[i] = mid;
|
||||
//for(i=len=0; i < lines; i++) {
|
||||
// printf("%03i : %03i : %i\n", i, dac_info[i]>>4, dac_info[i]&0xf);
|
||||
// len+=dac_info[i]&0xf;
|
||||
//}
|
||||
//printf("rate is %i, len %f\n", PsndRate, (double)PsndRate/(Pico.m.pal ? 50.0 : 60.0));
|
||||
//printf("len total: %i, last pos: %i\n", len, pos);
|
||||
//exit(8);
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL void PsndReset(void)
|
||||
{
|
||||
void *ym2612_regs;
|
||||
|
||||
// also clear the internal registers+addr line
|
||||
ym2612_regs = YM2612GetRegs();
|
||||
memset(ym2612_regs, 0, 0x200+4);
|
||||
timers_reset();
|
||||
|
||||
PsndRerate(0);
|
||||
}
|
||||
|
||||
|
||||
// to be called after changing sound rate or chips
|
||||
void PsndRerate(int preserve_state)
|
||||
{
|
||||
void *state = NULL;
|
||||
int target_fps = Pico.m.pal ? 50 : 60;
|
||||
|
||||
// not all rates are supported in MCD mode due to mp3 decoder limitations
|
||||
if (PicoAHW & PAHW_MCD) {
|
||||
if (PsndRate != 11025 && PsndRate != 22050 && PsndRate != 44100) PsndRate = 22050;
|
||||
PicoOpt |= POPT_EN_STEREO; // force stereo
|
||||
}
|
||||
|
||||
if (preserve_state) {
|
||||
state = malloc(0x200);
|
||||
if (state == NULL) return;
|
||||
memcpy(state, YM2612GetRegs(), 0x200);
|
||||
}
|
||||
YM2612Init(Pico.m.pal ? OSC_PAL/7 : OSC_NTSC/7, PsndRate);
|
||||
if (preserve_state) {
|
||||
// feed it back it's own registers, just like after loading state
|
||||
memcpy(YM2612GetRegs(), state, 0x200);
|
||||
ym2612_unpack_state();
|
||||
if ((PicoAHW & PAHW_MCD) && !(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))
|
||||
cdda_start_play();
|
||||
}
|
||||
|
||||
if (preserve_state) memcpy(state, sn76496_regs, 28*4); // remember old state
|
||||
SN76496_init(Pico.m.pal ? OSC_PAL/15 : OSC_NTSC/15, PsndRate);
|
||||
if (preserve_state) memcpy(sn76496_regs, state, 28*4); // restore old state
|
||||
|
||||
if (state)
|
||||
free(state);
|
||||
|
||||
// calculate PsndLen
|
||||
PsndLen=PsndRate / target_fps;
|
||||
PsndLen_exc_add=((PsndRate - PsndLen*target_fps)<<16) / target_fps;
|
||||
PsndLen_exc_cnt=0;
|
||||
|
||||
// recalculate dac info
|
||||
dac_recalculate();
|
||||
|
||||
if (PicoAHW & PAHW_MCD)
|
||||
pcm_set_rate(PsndRate);
|
||||
|
||||
// clear all buffers
|
||||
memset32(PsndBuffer, 0, sizeof(PsndBuffer)/4);
|
||||
memset(cdda_out_buffer, 0, sizeof(cdda_out_buffer));
|
||||
if (PsndOut)
|
||||
PsndClear();
|
||||
|
||||
// set mixer
|
||||
PsndMix_32_to_16l = (PicoOpt & POPT_EN_STEREO) ? mix_32_to_16l_stereo : mix_32_to_16_mono;
|
||||
|
||||
if (PicoAHW & PAHW_PICO)
|
||||
PicoReratePico();
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL void PsndDoDAC(int line_to)
|
||||
{
|
||||
int pos, pos1, len;
|
||||
int dout = ym2612.dacout;
|
||||
int line_from = PsndDacLine;
|
||||
|
||||
PsndDacLine = line_to + 1;
|
||||
|
||||
pos =dac_info[line_from]>>4;
|
||||
pos1=dac_info[line_to];
|
||||
len = ((pos1>>4)-pos) + (pos1&0xf);
|
||||
if (!len) return;
|
||||
|
||||
if (PicoOpt & POPT_EN_STEREO) {
|
||||
short *d = PsndOut + pos*2;
|
||||
for (; len > 0; len--, d+=2) *d = dout;
|
||||
} else {
|
||||
short *d = PsndOut + pos;
|
||||
for (; len > 0; len--, d++) *d = dout;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (do_pcm) {
|
||||
int *d = PsndBuffer;
|
||||
d += (PicoOpt&8) ? pos*2 : pos;
|
||||
pcm_update(d, len, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// cdda
|
||||
static pm_file *cdda_stream = NULL;
|
||||
|
||||
static void cdda_raw_update(int *buffer, int length)
|
||||
{
|
||||
int ret, cdda_bytes;
|
||||
if (cdda_stream == NULL) return;
|
||||
|
||||
cdda_bytes = length*4;
|
||||
if (PsndRate <= 22050) cdda_bytes *= 2;
|
||||
if (PsndRate < 22050) cdda_bytes *= 2;
|
||||
|
||||
ret = pm_read(cdda_out_buffer, cdda_bytes, cdda_stream);
|
||||
if (ret < cdda_bytes) {
|
||||
memset((char *)cdda_out_buffer + ret, 0, cdda_bytes - ret);
|
||||
cdda_stream = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// now mix
|
||||
switch (PsndRate) {
|
||||
case 44100: mix_16h_to_32(buffer, cdda_out_buffer, length*2); break;
|
||||
case 22050: mix_16h_to_32_s1(buffer, cdda_out_buffer, length*2); break;
|
||||
case 11025: mix_16h_to_32_s2(buffer, cdda_out_buffer, length*2); break;
|
||||
}
|
||||
}
|
||||
|
||||
PICO_INTERNAL void cdda_start_play(void)
|
||||
{
|
||||
int lba_offset, index, lba_length, i;
|
||||
|
||||
elprintf(EL_STATUS, "cdda play track #%i", Pico_mcd->scd.Cur_Track);
|
||||
|
||||
index = Pico_mcd->scd.Cur_Track - 1;
|
||||
|
||||
lba_offset = Pico_mcd->scd.Cur_LBA - Track_to_LBA(index + 1);
|
||||
if (lba_offset < 0) lba_offset = 0;
|
||||
lba_offset += Pico_mcd->TOC.Tracks[index].Offset;
|
||||
|
||||
// find the actual file for this track
|
||||
for (i = index; i >= 0; i--)
|
||||
if (Pico_mcd->TOC.Tracks[i].F != NULL) break;
|
||||
|
||||
if (Pico_mcd->TOC.Tracks[i].F == NULL) {
|
||||
elprintf(EL_STATUS|EL_ANOMALY, "no track?!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Pico_mcd->TOC.Tracks[i].ftype == TYPE_MP3)
|
||||
{
|
||||
int pos1024 = 0;
|
||||
|
||||
lba_length = Pico_mcd->TOC.Tracks[i].Length;
|
||||
for (i++; i < Pico_mcd->TOC.Last_Track; i++) {
|
||||
if (Pico_mcd->TOC.Tracks[i].F != NULL) break;
|
||||
lba_length += Pico_mcd->TOC.Tracks[i].Length;
|
||||
}
|
||||
|
||||
if (lba_offset)
|
||||
pos1024 = lba_offset * 1024 / lba_length;
|
||||
|
||||
mp3_start_play(Pico_mcd->TOC.Tracks[index].F, pos1024);
|
||||
return;
|
||||
}
|
||||
|
||||
cdda_stream = Pico_mcd->TOC.Tracks[i].F;
|
||||
PicoCDBufferFlush(); // buffering relies on fp not being touched
|
||||
pm_seek(cdda_stream, lba_offset * 2352, SEEK_SET);
|
||||
if (Pico_mcd->TOC.Tracks[i].ftype == TYPE_WAV)
|
||||
{
|
||||
// skip headers, assume it's 44kHz stereo uncompressed
|
||||
pm_seek(cdda_stream, 44, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL void PsndClear(void)
|
||||
{
|
||||
int len = PsndLen;
|
||||
if (PsndLen_exc_add) len++;
|
||||
if (PicoOpt & POPT_EN_STEREO)
|
||||
memset32((int *) PsndOut, 0, len); // assume PsndOut to be aligned
|
||||
else {
|
||||
short *out = PsndOut;
|
||||
if ((int)out & 2) { *out++ = 0; len--; }
|
||||
memset32((int *) out, 0, len/2);
|
||||
if (len & 1) out[len-1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PICO_INTERNAL int PsndRender(int offset, int length)
|
||||
{
|
||||
int buf32_updated = 0;
|
||||
int *buf32 = PsndBuffer+offset;
|
||||
int stereo = (PicoOpt & 8) >> 3;
|
||||
// emulating CD && PCM option enabled && PCM chip on && have enabled channels
|
||||
int do_pcm = (PicoAHW & PAHW_MCD) && (PicoOpt&POPT_EN_MCD_PCM) &&
|
||||
(Pico_mcd->pcm.control & 0x80) && Pico_mcd->pcm.enabled;
|
||||
offset <<= stereo;
|
||||
|
||||
#if !SIMPLE_WRITE_SOUND
|
||||
if (offset == 0) { // should happen once per frame
|
||||
// compensate for float part of PsndLen
|
||||
PsndLen_exc_cnt += PsndLen_exc_add;
|
||||
if (PsndLen_exc_cnt >= 0x10000) {
|
||||
PsndLen_exc_cnt -= 0x10000;
|
||||
length++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// PSG
|
||||
if (PicoOpt & POPT_EN_PSG)
|
||||
SN76496Update(PsndOut+offset, length, stereo);
|
||||
|
||||
if (PicoAHW & PAHW_PICO) {
|
||||
PicoPicoPCMUpdate(PsndOut+offset, length, stereo);
|
||||
return length;
|
||||
}
|
||||
|
||||
// Add in the stereo FM buffer
|
||||
if (PicoOpt & POPT_EN_FM) {
|
||||
buf32_updated = YM2612UpdateOne(buf32, length, stereo, 1);
|
||||
} else
|
||||
memset32(buf32, 0, length<<stereo);
|
||||
|
||||
//printf("active_chs: %02x\n", buf32_updated);
|
||||
|
||||
// CD: PCM sound
|
||||
if (do_pcm) {
|
||||
pcm_update(buf32, length, stereo);
|
||||
//buf32_updated = 1;
|
||||
}
|
||||
|
||||
// CD: CDDA audio
|
||||
// CD mode, cdda enabled, not data track, CDC is reading
|
||||
if ((PicoAHW & PAHW_MCD) && (PicoOpt & POPT_EN_MCD_CDDA) &&
|
||||
!(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))
|
||||
{
|
||||
// note: only 44, 22 and 11 kHz supported, with forced stereo
|
||||
int index = Pico_mcd->scd.Cur_Track - 1;
|
||||
|
||||
if (Pico_mcd->TOC.Tracks[index].ftype == TYPE_MP3)
|
||||
mp3_update(buf32, length, stereo);
|
||||
else
|
||||
cdda_raw_update(buf32, length);
|
||||
}
|
||||
|
||||
// convert + limit to normal 16bit output
|
||||
PsndMix_32_to_16l(PsndOut+offset, buf32, length);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// z80 stuff
|
||||
|
||||
#ifdef _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}
|
||||
};
|
||||
|
||||
int mz80_run(int cycles)
|
||||
{
|
||||
int ticks_pre = mz80GetElapsedTicks(0);
|
||||
mz80exec(cycles);
|
||||
return mz80GetElapsedTicks(0) - ticks_pre;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _USE_DRZ80
|
||||
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(_USE_DRZ80) || defined(_USE_CZ80)
|
||||
static unsigned char z80_in(unsigned short p)
|
||||
{
|
||||
elprintf(EL_ANOMALY, "Z80 port %04x read", p);
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
static void z80_out(unsigned short p,unsigned char d)
|
||||
{
|
||||
elprintf(EL_ANOMALY, "Z80 port %04x write %02x", p, d);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// z80 functionality wrappers
|
||||
PICO_INTERNAL void z80_init(void)
|
||||
{
|
||||
#ifdef _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);
|
||||
#endif
|
||||
#ifdef _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 =NULL;
|
||||
drZ80.z80_write8 =z80_write;
|
||||
drZ80.z80_write16 =NULL;
|
||||
drZ80.z80_in =z80_in;
|
||||
drZ80.z80_out =z80_out;
|
||||
drZ80.z80_irq_callback=NULL;
|
||||
#endif
|
||||
#ifdef _USE_CZ80
|
||||
memset(&CZ80, 0, sizeof(CZ80));
|
||||
Cz80_Init(&CZ80);
|
||||
Cz80_Set_Fetch(&CZ80, 0x0000, 0x1fff, (UINT32)Pico.zram); // main RAM
|
||||
Cz80_Set_Fetch(&CZ80, 0x2000, 0x3fff, (UINT32)Pico.zram); // mirror
|
||||
Cz80_Set_ReadB(&CZ80, (UINT8 (*)(UINT32 address))z80_read); // unused (hacked in)
|
||||
Cz80_Set_WriteB(&CZ80, z80_write);
|
||||
Cz80_Set_INPort(&CZ80, z80_in);
|
||||
Cz80_Set_OUTPort(&CZ80, z80_out);
|
||||
#endif
|
||||
}
|
||||
|
||||
PICO_INTERNAL void z80_reset(void)
|
||||
{
|
||||
#ifdef _USE_MZ80
|
||||
mz80reset();
|
||||
#endif
|
||||
#ifdef _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.z80irqvector = 0xff0000; // RST 38h
|
||||
drZ80.Z80PC = drZ80.z80_rebasePC(0);
|
||||
drZ80.Z80SP = drZ80.z80_rebaseSP(0x2000); // 0xf000 ?
|
||||
#endif
|
||||
#ifdef _USE_CZ80
|
||||
Cz80_Reset(&CZ80);
|
||||
Cz80_Set_Reg(&CZ80, CZ80_IX, 0xffff);
|
||||
Cz80_Set_Reg(&CZ80, CZ80_IY, 0xffff);
|
||||
Cz80_Set_Reg(&CZ80, CZ80_SP, 0x2000);
|
||||
#endif
|
||||
Pico.m.z80_fakeval = 0; // for faking when Z80 is disabled
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int had_irq = 0, z80_ppc, z80_ops_done = 0;
|
||||
int z80_cycles_left = 0;
|
||||
|
||||
void z80_int(void)
|
||||
{
|
||||
had_irq = 1;
|
||||
}
|
||||
|
||||
static void xfail(void)
|
||||
{
|
||||
printf("PC: %04x, %04x\n", Cz80_Get_Reg(&CZ80, CZ80_PC), z80_ppc);
|
||||
printf("z80_ops_done done: %i\n", z80_ops_done);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int z80_run(int cycles)
|
||||
{
|
||||
int fail = 0;
|
||||
int cdrz, ccz;
|
||||
|
||||
z80_cycles_left = cycles;
|
||||
z80_ppc = Cz80_Get_Reg(&CZ80, CZ80_PC);
|
||||
|
||||
if (had_irq) {
|
||||
printf("irq @ %04x\n", Cz80_Get_Reg(&CZ80, CZ80_PC));
|
||||
Cz80_Set_IRQ(&CZ80, 0, HOLD_LINE);
|
||||
drZ80.Z80_IRQ = 1;
|
||||
had_irq = 0;
|
||||
}
|
||||
|
||||
while (z80_cycles_left > 0)
|
||||
{
|
||||
ccz = Cz80_Exec(&CZ80, 1);
|
||||
cdrz = 1 - DrZ80Run(&drZ80, 1);
|
||||
|
||||
if (drZ80.Z80_IRQ && (drZ80.Z80IF&1))
|
||||
cdrz += 1 - DrZ80Run(&drZ80, 1); // cz80 processes IRQ after EI, DrZ80 does not
|
||||
|
||||
if (cdrz != ccz) {
|
||||
printf("cycles: %i vs %i\n", cdrz, ccz);
|
||||
fail = 1;
|
||||
}
|
||||
|
||||
if (drZ80.Z80PC - drZ80.Z80PC_BASE != Cz80_Get_Reg(&CZ80, CZ80_PC)) {
|
||||
printf("PC: %04x vs %04x\n", drZ80.Z80PC - drZ80.Z80PC_BASE, Cz80_Get_Reg(&CZ80, CZ80_PC));
|
||||
fail = 1;
|
||||
}
|
||||
|
||||
if (fail) xfail();
|
||||
|
||||
z80_ops_done++;
|
||||
z80_cycles_left -= ccz;
|
||||
z80_ppc = Cz80_Get_Reg(&CZ80, CZ80_PC);
|
||||
}
|
||||
|
||||
return co - z80_cycles_left;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
PICO_INTERNAL 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);
|
||||
#elif defined(_USE_CZ80)
|
||||
*(int *)data = 0x00007a43; // "Cz"
|
||||
*(int *)(data+4) = Cz80_Get_Reg(&CZ80, CZ80_PC);
|
||||
memcpy(data+8, &CZ80, (INT32)&CZ80.BasePC - (INT32)&CZ80);
|
||||
#endif
|
||||
}
|
||||
|
||||
PICO_INTERNAL 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?
|
||||
}
|
||||
#elif defined(_USE_CZ80)
|
||||
if (*(int *)data == 0x00007a43) { // "Cz" save?
|
||||
memcpy(&CZ80, data+8, (INT32)&CZ80.BasePC - (INT32)&CZ80);
|
||||
Cz80_Set_Reg(&CZ80, CZ80_PC, *(int *)(data+4));
|
||||
} else {
|
||||
z80_reset();
|
||||
z80_int();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
PICO_INTERNAL void z80_exit(void)
|
||||
{
|
||||
#if defined(_USE_MZ80)
|
||||
mz80shutdown();
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 1 // defined(__DEBUG_PRINT) || defined(__GP2X__) || defined(__GIZ__)
|
||||
PICO_INTERNAL void z80_debug(char *dstr)
|
||||
{
|
||||
#if defined(_USE_DRZ80)
|
||||
sprintf(dstr, "Z80 state: PC: %04x SP: %04x\n", drZ80.Z80PC-drZ80.Z80PC_BASE, drZ80.Z80SP-drZ80.Z80SP_BASE);
|
||||
#elif defined(_USE_CZ80)
|
||||
sprintf(dstr, "Z80 state: PC: %04x SP: %04x\n", CZ80.PC - CZ80.BasePC, CZ80.SP.W);
|
||||
#endif
|
||||
}
|
||||
#endif
|
2057
pico/sound/ym2612.c
Normal file
2057
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 | need_save */
|
||||
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 | need_save */
|
||||
UINT16 tl; /* #0x18 total level: TL << 3 */
|
||||
INT16 volume; /* #0x1a envelope counter | need_save */
|
||||
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; /* +00 algorithm */
|
||||
UINT8 FB; /* feedback shift */
|
||||
UINT8 pad[2];
|
||||
INT32 op1_out; /* op1 output for feedback */
|
||||
|
||||
INT32 mem_value; /* +08 delayed sample (MEM) value */
|
||||
|
||||
INT32 pms; /* channel PMS */
|
||||
UINT8 ams; /* channel AMS */
|
||||
|
||||
UINT8 kcode; /* +11 key code: */
|
||||
UINT8 fn_h; /* freq latch */
|
||||
UINT8 pad2;
|
||||
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 */
|
||||
UINT8 pad3[3];
|
||||
} FM_CH;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int clock; /* master clock (Hz) */
|
||||
int rate; /* sampling rate (Hz) */
|
||||
double freqbase; /* 08 frequency base */
|
||||
UINT8 address; /* 10 address register | need_save */
|
||||
UINT8 status; /* 11 status flag | need_save */
|
||||
UINT8 mode; /* mode CSM / 3SLOT */
|
||||
UINT8 pad;
|
||||
int TA; /* timer a */
|
||||
int TAC; /* timer a maxval */
|
||||
int TAT; /* timer a ticker | need_save */
|
||||
UINT8 TB; /* timer b */
|
||||
UINT8 pad2[3];
|
||||
int TBC; /* timer b maxval */
|
||||
int TBT; /* timer b ticker | need_save */
|
||||
/* 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; /* global envelope generator counter | need_save */
|
||||
UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 | need_save */
|
||||
UINT32 eg_timer_add; /* step of eg_timer */
|
||||
|
||||
/* LFO */
|
||||
UINT32 lfo_cnt; /* need_save */
|
||||
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 | need_save */
|
||||
|
||||
FM_CH CH[6]; /* channel state */
|
||||
|
||||
/* dac output (YM2612) */
|
||||
int dacen;
|
||||
INT32 dacout;
|
||||
|
||||
FM_OPN OPN; /* OPN state */
|
||||
|
||||
UINT32 slot_mask; /* active slot mask (performance hack) */
|
||||
} YM2612;
|
||||
#endif
|
||||
|
||||
#ifndef EXTERNAL_YM2612
|
||||
extern YM2612 ym2612;
|
||||
#endif
|
||||
|
||||
void YM2612Init_(int baseclock, int rate);
|
||||
void YM2612ResetChip_(void);
|
||||
int YM2612UpdateOne_(int *buffer, int length, int stereo, int is_buf_empty);
|
||||
|
||||
int YM2612Write_(unsigned int a, unsigned int v);
|
||||
//unsigned char YM2612Read_(void);
|
||||
|
||||
int YM2612PicoTick_(int n);
|
||||
void YM2612PicoStateLoad_(void);
|
||||
|
||||
void *YM2612GetRegs(void);
|
||||
void YM2612PicoStateSave2(int tat, int tbt);
|
||||
int YM2612PicoStateLoad2(int *tat, int *tbt);
|
||||
|
||||
#ifndef __GP2X__
|
||||
#define YM2612Init YM2612Init_
|
||||
#define YM2612ResetChip YM2612ResetChip_
|
||||
#define YM2612UpdateOne YM2612UpdateOne_
|
||||
#define YM2612PicoStateLoad YM2612PicoStateLoad_
|
||||
#else
|
||||
/* GP2X specific */
|
||||
#include "../../platform/gp2x/940ctl.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,is_buf_empty) \
|
||||
(PicoOpt&0x200) ? YM2612UpdateOne_940(buffer, length, stereo, is_buf_empty) : \
|
||||
YM2612UpdateOne_(buffer, length, stereo, is_buf_empty);
|
||||
#define YM2612PicoStateLoad() { \
|
||||
if (PicoOpt&0x200) YM2612PicoStateLoad_940(); \
|
||||
else YM2612PicoStateLoad_(); \
|
||||
}
|
||||
#endif /* __GP2X__ */
|
||||
|
||||
|
||||
#endif /* _H_FM_FM_ */
|
919
pico/sound/ym2612_arm.s
Normal file
919
pico/sound/ym2612_arm.s
Normal file
|
@ -0,0 +1,919 @@
|
|||
@ 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
|
||||
|
||||
@ vim:filetype=armasm
|
||||
|
||||
.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)|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
|
||||
.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
|
||||
orr r4, r4, #8 @ have_output
|
||||
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 r4, [lr, #0x50] @ was_update
|
||||
str r10, [lr, #0x54] @ op1_out
|
||||
ldmfd sp!, {r4-r11,pc}
|
||||
|
||||
.pool
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue