2016-04-28 07:09:44 +00:00
|
|
|
/****************************************************************************
|
2016-05-08 19:30:55 +00:00
|
|
|
* *
|
|
|
|
* Project64 - A Nintendo 64 emulator. *
|
|
|
|
* http://www.pj64-emu.com/ *
|
|
|
|
* Copyright (C) 2016 Project64. All rights reserved. *
|
|
|
|
* Copyright (C) 2014 Bobby Smiles *
|
|
|
|
* Copyright (C) 2009 Richard Goedeken *
|
|
|
|
* Copyright (C) 2002 Hacktarux *
|
|
|
|
* *
|
|
|
|
* License: *
|
|
|
|
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
|
2016-05-13 09:14:00 +00:00
|
|
|
* version 2 of the License, or (at your option) any later version. *
|
2016-05-08 19:30:55 +00:00
|
|
|
* *
|
|
|
|
****************************************************************************/
|
2016-04-28 07:09:44 +00:00
|
|
|
#include "stdafx.h"
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include "alist.h"
|
|
|
|
#include "common.h"
|
|
|
|
#include "hle.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "ucodes.h"
|
|
|
|
|
|
|
|
enum { DMEM_BASE = 0x5c0 };
|
|
|
|
|
|
|
|
/* helper functions */
|
|
|
|
static uint32_t get_address(CHle * hle, uint32_t so)
|
|
|
|
{
|
|
|
|
return alist_get_address(hle, so, hle->alist_audio().segments, N_SEGMENTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_address(CHle * hle, uint32_t so)
|
|
|
|
{
|
|
|
|
alist_set_address(hle, so, hle->alist_audio().segments, N_SEGMENTS);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clear_segments(CHle * hle)
|
|
|
|
{
|
|
|
|
memset(hle->alist_audio().segments, 0, N_SEGMENTS*sizeof(hle->alist_audio().segments[0]));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* audio commands definition */
|
|
|
|
static void SPNOOP(CHle * UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CLEARBUFF(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint16_t dmem = (w1 + DMEM_BASE) & 0xFFFF;
|
|
|
|
uint16_t count = w2;
|
|
|
|
|
|
|
|
if (count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
alist_clear(hle, dmem, align(count, 16));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ENVMIXER(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint8_t flags = (w1 >> 16) & 0xFF;
|
|
|
|
uint32_t address = get_address(hle, w2);
|
|
|
|
|
|
|
|
alist_envmix_exp(
|
|
|
|
hle,
|
|
|
|
flags & A_INIT,
|
|
|
|
flags & A_AUX,
|
|
|
|
hle->alist_audio().out, hle->alist_audio().dry_right,
|
|
|
|
hle->alist_audio().wet_left, hle->alist_audio().wet_right,
|
|
|
|
hle->alist_audio().in, hle->alist_audio().count,
|
|
|
|
hle->alist_audio().dry, hle->alist_audio().wet,
|
|
|
|
hle->alist_audio().vol,
|
|
|
|
hle->alist_audio().target,
|
|
|
|
hle->alist_audio().rate,
|
|
|
|
address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ENVMIXER_GE(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint8_t flags = (w1 >> 16);
|
|
|
|
uint32_t address = get_address(hle, w2);
|
|
|
|
|
|
|
|
alist_envmix_ge(
|
|
|
|
hle,
|
|
|
|
flags & A_INIT,
|
|
|
|
flags & A_AUX,
|
|
|
|
hle->alist_audio().out, hle->alist_audio().dry_right,
|
|
|
|
hle->alist_audio().wet_left, hle->alist_audio().wet_right,
|
|
|
|
hle->alist_audio().in, hle->alist_audio().count,
|
|
|
|
hle->alist_audio().dry, hle->alist_audio().wet,
|
|
|
|
hle->alist_audio().vol,
|
|
|
|
hle->alist_audio().target,
|
|
|
|
hle->alist_audio().rate,
|
|
|
|
address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void RESAMPLE(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint8_t flags = (w1 >> 16) & 0xFF;
|
|
|
|
uint16_t pitch = w1 & 0xFFFF;
|
|
|
|
uint32_t address = get_address(hle, w2);
|
|
|
|
|
|
|
|
alist_resample(
|
|
|
|
hle,
|
|
|
|
flags & 0x1,
|
|
|
|
flags & 0x2,
|
|
|
|
hle->alist_audio().out,
|
|
|
|
hle->alist_audio().in,
|
|
|
|
align(hle->alist_audio().count, 16),
|
|
|
|
pitch << 1,
|
|
|
|
address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SETVOL(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint8_t flags = (w1 >> 16) & 0xFF;
|
|
|
|
|
|
|
|
if (flags & A_AUX)
|
|
|
|
{
|
|
|
|
hle->alist_audio().dry = w1 & 0xFFFF;
|
|
|
|
hle->alist_audio().wet = w2 & 0xFFFF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned lr = (flags & A_LEFT) ? 0 : 1;
|
|
|
|
|
|
|
|
if (flags & A_VOL)
|
|
|
|
{
|
|
|
|
hle->alist_audio().vol[lr] = w1 & 0xFFFF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hle->alist_audio().target[lr] = w1 & 0xFFFF;
|
|
|
|
hle->alist_audio().rate[lr] = w2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SETLOOP(CHle * hle, uint32_t UNUSED(w1), uint32_t w2)
|
|
|
|
{
|
|
|
|
hle->alist_audio().loop = get_address(hle, w2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void ADPCM(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint8_t flags = (w1 >> 16) & 0xFF;
|
|
|
|
uint32_t address = get_address(hle, w2);
|
|
|
|
|
|
|
|
alist_adpcm(
|
|
|
|
hle,
|
|
|
|
flags & 0x1,
|
|
|
|
flags & 0x2,
|
|
|
|
false, /* unsupported in this ucode */
|
|
|
|
hle->alist_audio().out,
|
|
|
|
hle->alist_audio().in,
|
|
|
|
align(hle->alist_audio().count, 32),
|
|
|
|
hle->alist_audio().table,
|
|
|
|
hle->alist_audio().loop,
|
|
|
|
address);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void LOADBUFF(CHle * hle, uint32_t UNUSED(w1), uint32_t w2)
|
|
|
|
{
|
|
|
|
uint32_t address = get_address(hle, w2);
|
|
|
|
|
|
|
|
if (hle->alist_audio().count == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
alist_load(hle, hle->alist_audio().in, address, hle->alist_audio().count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SAVEBUFF(CHle * hle, uint32_t UNUSED(w1), uint32_t w2)
|
|
|
|
{
|
|
|
|
uint32_t address = get_address(hle, w2);
|
|
|
|
|
|
|
|
if (hle->alist_audio().count == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
alist_save(hle, hle->alist_audio().out, address, hle->alist_audio().count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SETBUFF(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint8_t flags = (w1 >> 16) & 0xFF;
|
|
|
|
|
|
|
|
if (flags & A_AUX)
|
|
|
|
{
|
|
|
|
hle->alist_audio().dry_right = (w1 + DMEM_BASE) & 0xFFFF;
|
|
|
|
hle->alist_audio().wet_left = (w2 >> 16) + DMEM_BASE;
|
|
|
|
hle->alist_audio().wet_right = (w2 + DMEM_BASE) & 0xFFFF;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hle->alist_audio().in = (w1 + DMEM_BASE) & 0xFFFF;
|
|
|
|
hle->alist_audio().out = ((w2 >> 16) + DMEM_BASE) & 0xFFFF;
|
|
|
|
hle->alist_audio().count = w2 & 0xFFFF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DMEMMOVE(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint16_t dmemi = (w1 + DMEM_BASE) & 0xFFFF;
|
|
|
|
uint16_t dmemo = (w2 >> 16) + DMEM_BASE;
|
|
|
|
uint16_t count = (w2)& 0xFFFF;
|
|
|
|
|
|
|
|
if (count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
alist_move(hle, dmemo, dmemi, align(count, 16));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void LOADADPCM(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint16_t count = (w1 & 0xFFFF);
|
|
|
|
uint32_t address = get_address(hle, w2);
|
|
|
|
|
|
|
|
dram_load_u16(hle, (uint16_t*)hle->alist_audio().table, address, align(count, 8) >> 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void INTERLEAVE(CHle * hle, uint32_t UNUSED(w1), uint32_t w2)
|
|
|
|
{
|
|
|
|
uint16_t left = (w2 >> 16) + DMEM_BASE;
|
|
|
|
uint16_t right = (w2 + DMEM_BASE) & 0xFFFF;
|
|
|
|
|
|
|
|
if (hle->alist_audio().count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
alist_interleave(hle, hle->alist_audio().out, left, right, align(hle->alist_audio().count, 16));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void MIXER(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
int16_t gain = (w1)& 0xFFFF;
|
|
|
|
uint16_t dmemi = ((w2 >> 16) + DMEM_BASE) & 0xFFFF;
|
|
|
|
uint16_t dmemo = (w2 + DMEM_BASE) & 0xFFFF;
|
|
|
|
|
|
|
|
if (hle->alist_audio().count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
alist_mix(hle, dmemo, dmemi, align(hle->alist_audio().count, 32), gain);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SEGMENT(CHle * hle, uint32_t UNUSED(w1), uint32_t w2)
|
|
|
|
{
|
|
|
|
set_address(hle, w2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void POLEF(CHle * hle, uint32_t w1, uint32_t w2)
|
|
|
|
{
|
|
|
|
uint8_t flags = (w1 >> 16);
|
|
|
|
uint16_t gain = w1;
|
|
|
|
uint32_t address = get_address(hle, w2);
|
|
|
|
|
|
|
|
if (hle->alist_audio().count == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
alist_polef(
|
|
|
|
hle,
|
|
|
|
flags & A_INIT,
|
|
|
|
hle->alist_audio().out,
|
|
|
|
hle->alist_audio().in,
|
|
|
|
align(hle->alist_audio().count, 16),
|
|
|
|
gain,
|
|
|
|
hle->alist_audio().table,
|
|
|
|
address);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* global functions */
|
|
|
|
void alist_process_audio(CHle * hle)
|
|
|
|
{
|
|
|
|
static const acmd_callback_t ABI[0x10] = {
|
|
|
|
SPNOOP, ADPCM, CLEARBUFF, ENVMIXER,
|
|
|
|
LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT,
|
|
|
|
SETBUFF, SETVOL, DMEMMOVE, LOADADPCM,
|
|
|
|
MIXER, INTERLEAVE, POLEF, SETLOOP
|
|
|
|
};
|
|
|
|
|
|
|
|
clear_segments(hle);
|
|
|
|
alist_process(hle, ABI, 0x10);
|
|
|
|
}
|
|
|
|
|
|
|
|
void alist_process_audio_ge(CHle * hle)
|
|
|
|
{
|
|
|
|
static const acmd_callback_t ABI[0x10] =
|
|
|
|
{
|
|
|
|
SPNOOP, ADPCM, CLEARBUFF, ENVMIXER_GE,
|
|
|
|
LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT,
|
|
|
|
SETBUFF, SETVOL, DMEMMOVE, LOADADPCM,
|
|
|
|
MIXER, INTERLEAVE, POLEF, SETLOOP
|
|
|
|
};
|
|
|
|
|
|
|
|
clear_segments(hle);
|
|
|
|
alist_process(hle, ABI, 0x10);
|
|
|
|
}
|
|
|
|
|
|
|
|
void alist_process_audio_bc(CHle * hle)
|
|
|
|
{
|
|
|
|
static const acmd_callback_t ABI[0x10] =
|
|
|
|
{
|
|
|
|
SPNOOP, ADPCM, CLEARBUFF, ENVMIXER_GE,
|
|
|
|
LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT,
|
|
|
|
SETBUFF, SETVOL, DMEMMOVE, LOADADPCM,
|
|
|
|
MIXER, INTERLEAVE, POLEF, SETLOOP
|
|
|
|
};
|
|
|
|
|
|
|
|
clear_segments(hle);
|
|
|
|
alist_process(hle, ABI, 0x10);
|
|
|
|
}
|