mirror of
https://github.com/libretro/parallel-n64.git
synced 2024-11-23 08:09:50 +00:00
Update to most recent HLE RSP.
This commit is contained in:
parent
b804ab1a19
commit
64c2a5023d
@ -63,6 +63,7 @@ SOURCES_C += $(RSPDIR)/src/alist.c \
|
||||
$(RSPDIR)/src/mp3.c \
|
||||
$(RSPDIR)/src/musyx.c \
|
||||
$(RSPDIR)/src/re2.c \
|
||||
$(RSPDIR)/src/hvqm.c \
|
||||
$(RSPDIR)/src/hle_plugin.c
|
||||
|
||||
SOURCES_C += $(CXD4DIR)/rsp.c
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - alist.h *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -22,9 +22,9 @@
|
||||
#ifndef ALIST_INTERNAL_H
|
||||
#define ALIST_INTERNAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <boolean.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct hle_t;
|
||||
|
||||
@ -142,13 +142,21 @@ void alist_polef(
|
||||
uint32_t address);
|
||||
|
||||
void alist_iirf(
|
||||
struct hle_t* hle,
|
||||
bool init,
|
||||
uint16_t dmemo,
|
||||
uint16_t dmemi,
|
||||
uint16_t count,
|
||||
int16_t* table,
|
||||
uint32_t address);
|
||||
struct hle_t* hle,
|
||||
bool init,
|
||||
uint16_t dmemo,
|
||||
uint16_t dmemi,
|
||||
uint16_t count,
|
||||
int16_t* table,
|
||||
uint32_t address);
|
||||
|
||||
void alist_overload(
|
||||
struct hle_t* hle,
|
||||
uint16_t dmem,
|
||||
int16_t count,
|
||||
int16_t gain,
|
||||
uint16_t attenuation);
|
||||
|
||||
/*
|
||||
* Audio flags
|
||||
*/
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - alist_audio.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* Copyright (C) 2009 Richard Goedeken *
|
||||
* Copyright (C) 2002 Hacktarux *
|
||||
@ -21,41 +21,33 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <boolean.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "alist.h"
|
||||
#include "common.h"
|
||||
#include "hle_internal.h"
|
||||
#include "memory.h"
|
||||
#include "ucodes.h"
|
||||
|
||||
enum
|
||||
{
|
||||
DMEM_BASE = 0x5c0
|
||||
};
|
||||
enum { DMEM_BASE = 0x5c0 };
|
||||
|
||||
/* helper functions */
|
||||
#define get_address(hle, so) (alist_get_address((hle), (so), (hle)->alist_audio.segments, N_SEGMENTS))
|
||||
#define set_address(hle, so) alist_set_address((hle), (so), (hle)->alist_audio.segments, N_SEGMENTS)
|
||||
#define clear_segments(hle) \
|
||||
(hle)->alist_audio.segments[0] = 0; \
|
||||
(hle)->alist_audio.segments[1] = 0; \
|
||||
(hle)->alist_audio.segments[2] = 0; \
|
||||
(hle)->alist_audio.segments[3] = 0; \
|
||||
(hle)->alist_audio.segments[4] = 0; \
|
||||
(hle)->alist_audio.segments[5] = 0; \
|
||||
(hle)->alist_audio.segments[6] = 0; \
|
||||
(hle)->alist_audio.segments[7] = 0; \
|
||||
(hle)->alist_audio.segments[8] = 0; \
|
||||
(hle)->alist_audio.segments[9] = 0; \
|
||||
(hle)->alist_audio.segments[10] = 0; \
|
||||
(hle)->alist_audio.segments[11] = 0; \
|
||||
(hle)->alist_audio.segments[12] = 0; \
|
||||
(hle)->alist_audio.segments[13] = 0; \
|
||||
(hle)->alist_audio.segments[14] = 0; \
|
||||
(hle)->alist_audio.segments[15] = 0
|
||||
static uint32_t get_address(struct hle_t* hle, uint32_t so)
|
||||
{
|
||||
return alist_get_address(hle, so, hle->alist_audio.segments, N_SEGMENTS);
|
||||
}
|
||||
|
||||
static void set_address(struct hle_t* hle, uint32_t so)
|
||||
{
|
||||
alist_set_address(hle, so, hle->alist_audio.segments, N_SEGMENTS);
|
||||
}
|
||||
|
||||
static void clear_segments(struct hle_t* hle)
|
||||
{
|
||||
memset(hle->alist_audio.segments, 0, N_SEGMENTS*sizeof(hle->alist_audio.segments[0]));
|
||||
}
|
||||
|
||||
/* audio commands definition */
|
||||
static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2))
|
||||
@ -64,195 +56,189 @@ static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUS
|
||||
|
||||
static void CLEARBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t dmem = w1 + DMEM_BASE;
|
||||
uint16_t count = w2 & 0xfff;
|
||||
uint16_t dmem = w1 + DMEM_BASE;
|
||||
uint16_t count = w2 & 0xfff;
|
||||
|
||||
if (count != 0)
|
||||
alist_clear(hle, dmem, align(count, 16));
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
alist_clear(hle, dmem, align(count, 16));
|
||||
}
|
||||
|
||||
static void ENVMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = get_address(hle, w2);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
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);
|
||||
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(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = get_address(hle, 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);
|
||||
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(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t pitch = w1;
|
||||
uint32_t address = get_address(hle, w2);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t pitch = w1;
|
||||
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);
|
||||
alist_resample(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
flags & 0x2,
|
||||
hle->alist_audio.out,
|
||||
hle->alist_audio.in,
|
||||
align(hle->alist_audio.count, 16),
|
||||
pitch << 1,
|
||||
address);
|
||||
}
|
||||
|
||||
static void SETVOL(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
unsigned lr;
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
|
||||
if (!hle)
|
||||
return;
|
||||
if (flags & A_AUX) {
|
||||
hle->alist_audio.dry = w1;
|
||||
hle->alist_audio.wet = w2;
|
||||
}
|
||||
else {
|
||||
unsigned lr = (flags & A_LEFT) ? 0 : 1;
|
||||
|
||||
if (flags & A_AUX)
|
||||
{
|
||||
hle->alist_audio.dry = w1;
|
||||
hle->alist_audio.wet = w2;
|
||||
return;
|
||||
}
|
||||
|
||||
lr = (flags & A_LEFT) ? 0 : 1;
|
||||
|
||||
if (flags & A_VOL)
|
||||
hle->alist_audio.vol[lr] = w1;
|
||||
else
|
||||
{
|
||||
hle->alist_audio.target[lr] = w1;
|
||||
hle->alist_audio.rate[lr] = w2;
|
||||
}
|
||||
if (flags & A_VOL)
|
||||
hle->alist_audio.vol[lr] = w1;
|
||||
else {
|
||||
hle->alist_audio.target[lr] = w1;
|
||||
hle->alist_audio.rate[lr] = w2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SETLOOP(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
{
|
||||
if (!hle)
|
||||
return;
|
||||
|
||||
hle->alist_audio.loop = get_address(hle, w2);
|
||||
hle->alist_audio.loop = get_address(hle, w2);
|
||||
}
|
||||
|
||||
static void ADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = get_address(hle, w2);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = get_address(hle, w2);
|
||||
|
||||
if (!hle)
|
||||
return;
|
||||
|
||||
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);
|
||||
alist_adpcm(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
flags & A_LOOP,
|
||||
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(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
{
|
||||
uint32_t address = get_address(hle, w2);
|
||||
uint32_t address = get_address(hle, w2);
|
||||
|
||||
if (hle->alist_audio.count != 0)
|
||||
alist_load(hle, hle->alist_audio.in, address, hle->alist_audio.count);
|
||||
if (hle->alist_audio.count == 0)
|
||||
return;
|
||||
|
||||
alist_load(hle, hle->alist_audio.in, address, hle->alist_audio.count);
|
||||
}
|
||||
|
||||
static void SAVEBUFF(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
{
|
||||
uint32_t address = get_address(hle, w2);
|
||||
uint32_t address = get_address(hle, w2);
|
||||
|
||||
if (hle->alist_audio.count != 0)
|
||||
alist_save(hle, hle->alist_audio.out, address, hle->alist_audio.count);
|
||||
if (hle->alist_audio.count == 0)
|
||||
return;
|
||||
|
||||
alist_save(hle, hle->alist_audio.out, address, hle->alist_audio.count);
|
||||
}
|
||||
|
||||
static void SETBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
|
||||
if (flags & A_AUX)
|
||||
{
|
||||
if (flags & A_AUX) {
|
||||
hle->alist_audio.dry_right = w1 + DMEM_BASE;
|
||||
hle->alist_audio.wet_left = (w2 >> 16) + DMEM_BASE;
|
||||
hle->alist_audio.wet_right = w2 + DMEM_BASE;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
hle->alist_audio.in = w1 + DMEM_BASE;
|
||||
hle->alist_audio.out = (w2 >> 16) + DMEM_BASE;
|
||||
hle->alist_audio.count = w2;
|
||||
}
|
||||
}
|
||||
|
||||
static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t count)
|
||||
static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t dmemi = w1 + DMEM_BASE;
|
||||
uint16_t dmemo = (count >> 16) + DMEM_BASE;
|
||||
uint16_t dmemi = w1 + DMEM_BASE;
|
||||
uint16_t dmemo = (w2 >> 16) + DMEM_BASE;
|
||||
uint16_t count = w2;
|
||||
|
||||
if (count != 0)
|
||||
alist_move(hle, dmemo, dmemi, align(count, 16));
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
alist_move(hle, dmemo, dmemi, align(count, 16));
|
||||
}
|
||||
|
||||
static void LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = w1;
|
||||
uint32_t address = get_address(hle, w2);
|
||||
uint16_t count = w1;
|
||||
uint32_t address = get_address(hle, w2);
|
||||
|
||||
if (!hle)
|
||||
return;
|
||||
|
||||
dram_load_u16(hle, (uint16_t*)hle->alist_audio.table, address, align(count, 8) >> 1);
|
||||
dram_load_u16(hle, (uint16_t*)hle->alist_audio.table, address, align(count, 8) >> 1);
|
||||
}
|
||||
|
||||
static void INTERLEAVE(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
{
|
||||
uint16_t left = (w2 >> 16) + DMEM_BASE;
|
||||
uint16_t right = w2 + DMEM_BASE;
|
||||
uint16_t left = (w2 >> 16) + DMEM_BASE;
|
||||
uint16_t right = w2 + DMEM_BASE;
|
||||
|
||||
if (hle->alist_audio.count != 0)
|
||||
alist_interleave(hle, hle->alist_audio.out,
|
||||
left, right, align(hle->alist_audio.count, 16));
|
||||
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(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
int16_t gain = w1;
|
||||
uint16_t dmemi = (w2 >> 16) + DMEM_BASE;
|
||||
uint16_t dmemo = w2 + DMEM_BASE;
|
||||
int16_t gain = w1;
|
||||
uint16_t dmemi = (w2 >> 16) + DMEM_BASE;
|
||||
uint16_t dmemo = w2 + DMEM_BASE;
|
||||
|
||||
if (hle->alist_audio.count != 0)
|
||||
alist_mix(hle, dmemo, dmemi, align(hle->alist_audio.count, 32), gain);
|
||||
if (hle->alist_audio.count == 0)
|
||||
return;
|
||||
|
||||
alist_mix(hle, dmemo, dmemi, align(hle->alist_audio.count, 32), gain);
|
||||
}
|
||||
|
||||
static void SEGMENT(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
@ -262,12 +248,14 @@ static void SEGMENT(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
|
||||
static void POLEF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t gain = w1;
|
||||
uint32_t address = get_address(hle, w2);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t gain = w1;
|
||||
uint32_t address = get_address(hle, w2);
|
||||
|
||||
if (hle->alist_audio.count != 0)
|
||||
alist_polef(
|
||||
if (hle->alist_audio.count == 0)
|
||||
return;
|
||||
|
||||
alist_polef(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
hle->alist_audio.out,
|
||||
@ -281,39 +269,42 @@ static void POLEF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
/* global functions */
|
||||
void alist_process_audio(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x10] = {
|
||||
SPNOOP, ADPCM , CLEARBUFF, ENVMIXER,
|
||||
LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT,
|
||||
SETBUFF, SETVOL, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, POLEF, SETLOOP
|
||||
};
|
||||
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);
|
||||
clear_segments(hle);
|
||||
alist_process(hle, ABI, 0x10);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_audio_ge(struct hle_t* 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
|
||||
};
|
||||
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);
|
||||
clear_segments(hle);
|
||||
alist_process(hle, ABI, 0x10);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_audio_bc(struct hle_t* 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
|
||||
};
|
||||
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);
|
||||
clear_segments(hle);
|
||||
alist_process(hle, ABI, 0x10);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - alist_naudio.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* Copyright (C) 2009 Richard Goedeken *
|
||||
* Copyright (C) 2002 Hacktarux *
|
||||
@ -21,24 +21,18 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <boolean.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "alist.h"
|
||||
#include "common.h"
|
||||
#include "hle_external.h"
|
||||
#include "hle_internal.h"
|
||||
#include "memory.h"
|
||||
#include "ucodes.h"
|
||||
|
||||
enum
|
||||
{
|
||||
NAUDIO_COUNT = 0x170
|
||||
}; /* ie 184 samples */
|
||||
|
||||
enum
|
||||
{
|
||||
enum { NAUDIO_COUNT = 0x170 }; /* ie 184 samples */
|
||||
enum {
|
||||
NAUDIO_MAIN = 0x4f0,
|
||||
NAUDIO_MAIN2 = 0x660,
|
||||
NAUDIO_DRY_LEFT = 0x9d0,
|
||||
@ -47,24 +41,23 @@ enum
|
||||
NAUDIO_WET_RIGHT = 0xe20
|
||||
};
|
||||
|
||||
|
||||
/* audio commands definition */
|
||||
static void UNKNOWN(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t acmd = (w1 >> 24);
|
||||
uint8_t acmd = (w1 >> 24);
|
||||
|
||||
HleWarnMessage(hle->user_defined,
|
||||
"Unknown audio command %d: %08x %08x",
|
||||
acmd, w1, w2);
|
||||
HleWarnMessage(hle->user_defined,
|
||||
"Unknown audio command %d: %08x %08x",
|
||||
acmd, w1, w2);
|
||||
}
|
||||
|
||||
|
||||
static void SPNOOP(struct hle_t* UNUSED(hle),
|
||||
uint32_t UNUSED(w1), uint32_t UNUSED(w2))
|
||||
static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2))
|
||||
{
|
||||
}
|
||||
|
||||
static void NAUDIO_0000(struct hle_t* hle,
|
||||
uint32_t w1, uint32_t w2)
|
||||
static void NAUDIO_0000(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
/* ??? */
|
||||
UNKNOWN(hle, w1, w2);
|
||||
@ -72,8 +65,6 @@ static void NAUDIO_0000(struct hle_t* hle,
|
||||
|
||||
static void NAUDIO_02B0(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
{
|
||||
if (!hle)
|
||||
return;
|
||||
/* emulate code at 0x12b0 (inside SETVOL), because PC always execute in IMEM */
|
||||
hle->alist_naudio.rate[1] &= ~0xffff;
|
||||
hle->alist_naudio.rate[1] |= (w2 & 0xffff);
|
||||
@ -81,187 +72,183 @@ static void NAUDIO_02B0(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
|
||||
static void NAUDIO_14(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t gain = w1;
|
||||
uint8_t select_main = (w2 >> 24);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint16_t dmem = (select_main == 0) ? NAUDIO_MAIN : NAUDIO_MAIN2;
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t gain = w1;
|
||||
uint8_t select_main = (w2 >> 24);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
if (hle->alist_naudio.table[0] == 0 && hle->alist_naudio.table[1] == 0)
|
||||
{
|
||||
alist_polef(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
dmem,
|
||||
dmem,
|
||||
NAUDIO_COUNT,
|
||||
gain,
|
||||
hle->alist_naudio.table,
|
||||
address);
|
||||
return;
|
||||
}
|
||||
uint16_t dmem = (select_main == 0) ? NAUDIO_MAIN : NAUDIO_MAIN2;
|
||||
|
||||
if (hle->alist_naudio.table[0] == 0 && hle->alist_naudio.table[1] == 0) {
|
||||
alist_polef(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
dmem,
|
||||
dmem,
|
||||
NAUDIO_COUNT,
|
||||
gain,
|
||||
hle->alist_naudio.table,
|
||||
address);
|
||||
}
|
||||
else
|
||||
{
|
||||
alist_iirf(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
dmem,
|
||||
dmem,
|
||||
NAUDIO_COUNT,
|
||||
hle->alist_naudio.table,
|
||||
address);
|
||||
}
|
||||
|
||||
alist_iirf(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
dmem,
|
||||
dmem,
|
||||
NAUDIO_COUNT,
|
||||
hle->alist_naudio.table,
|
||||
address);
|
||||
}
|
||||
|
||||
static void SETVOL(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
|
||||
if (flags & 0x4)
|
||||
{
|
||||
if (flags & 0x2)
|
||||
{
|
||||
hle->alist_naudio.vol[0] = w1;
|
||||
hle->alist_naudio.dry = (w2 >> 16);
|
||||
hle->alist_naudio.wet = w2;
|
||||
}
|
||||
else
|
||||
{
|
||||
hle->alist_naudio.target[1] = w1;
|
||||
hle->alist_naudio.rate[1] = w2;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
hle->alist_naudio.target[0] = w1;
|
||||
hle->alist_naudio.rate[0] = w2;
|
||||
if (flags & A_VOL) {
|
||||
if (flags & A_LEFT) {
|
||||
hle->alist_naudio.vol[0] = w1;
|
||||
hle->alist_naudio.dry = (w2 >> 16);
|
||||
hle->alist_naudio.wet = w2;
|
||||
}
|
||||
else { /* A_RIGHT */
|
||||
hle->alist_naudio.target[1] = w1;
|
||||
hle->alist_naudio.rate[1] = w2;
|
||||
}
|
||||
}
|
||||
else { /* A_RATE */
|
||||
hle->alist_naudio.target[0] = w1;
|
||||
hle->alist_naudio.rate[0] = w2;
|
||||
}
|
||||
}
|
||||
|
||||
static void ENVMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
hle->alist_naudio.vol[1] = w1;
|
||||
hle->alist_naudio.vol[1] = w1;
|
||||
|
||||
alist_envmix_lin(
|
||||
hle,
|
||||
flags & 0x1,
|
||||
NAUDIO_DRY_LEFT,
|
||||
NAUDIO_DRY_RIGHT,
|
||||
NAUDIO_WET_LEFT,
|
||||
NAUDIO_WET_RIGHT,
|
||||
NAUDIO_MAIN,
|
||||
NAUDIO_COUNT,
|
||||
hle->alist_naudio.dry,
|
||||
hle->alist_naudio.wet,
|
||||
hle->alist_naudio.vol,
|
||||
hle->alist_naudio.target,
|
||||
hle->alist_naudio.rate,
|
||||
address);
|
||||
alist_envmix_lin(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
NAUDIO_DRY_LEFT,
|
||||
NAUDIO_DRY_RIGHT,
|
||||
NAUDIO_WET_LEFT,
|
||||
NAUDIO_WET_RIGHT,
|
||||
NAUDIO_MAIN,
|
||||
NAUDIO_COUNT,
|
||||
hle->alist_naudio.dry,
|
||||
hle->alist_naudio.wet,
|
||||
hle->alist_naudio.vol,
|
||||
hle->alist_naudio.target,
|
||||
hle->alist_naudio.rate,
|
||||
address);
|
||||
}
|
||||
|
||||
static void CLEARBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t dmem = w1 + NAUDIO_MAIN;
|
||||
uint16_t count = w2 & 0xfff;
|
||||
uint16_t dmem = w1 + NAUDIO_MAIN;
|
||||
uint16_t count = w2 & 0xfff;
|
||||
|
||||
alist_clear(hle, dmem, count);
|
||||
alist_clear(hle, dmem, count);
|
||||
}
|
||||
|
||||
static void MIXER(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
int16_t gain = w1;
|
||||
uint16_t dmemi = (w2 >> 16) + NAUDIO_MAIN;
|
||||
uint16_t dmemo = w2 + NAUDIO_MAIN;
|
||||
int16_t gain = w1;
|
||||
uint16_t dmemi = (w2 >> 16) + NAUDIO_MAIN;
|
||||
uint16_t dmemo = w2 + NAUDIO_MAIN;
|
||||
|
||||
alist_mix(hle, dmemo, dmemi, NAUDIO_COUNT, gain);
|
||||
alist_mix(hle, dmemo, dmemi, NAUDIO_COUNT, gain);
|
||||
}
|
||||
|
||||
static void LOADBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = (w1 >> 12) & 0xfff;
|
||||
uint16_t dmem = (w1 & 0xfff) + NAUDIO_MAIN;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint16_t count = (w1 >> 12) & 0xfff;
|
||||
uint16_t dmem = (w1 & 0xfff) + NAUDIO_MAIN;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
alist_load(hle, dmem, address, count);
|
||||
alist_load(hle, dmem, address, count);
|
||||
}
|
||||
|
||||
static void SAVEBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = (w1 >> 12) & 0xfff;
|
||||
uint16_t dmem = (w1 & 0xfff) + NAUDIO_MAIN;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint16_t count = (w1 >> 12) & 0xfff;
|
||||
uint16_t dmem = (w1 & 0xfff) + NAUDIO_MAIN;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
alist_save(hle, dmem, address, count);
|
||||
alist_save(hle, dmem, address, count);
|
||||
}
|
||||
|
||||
static void NAUDIO_LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
static void LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = w1;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint16_t count = w1;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
if (!hle)
|
||||
return;
|
||||
|
||||
dram_load_u16(hle, (uint16_t*)hle->alist_naudio.table, address, count >> 1);
|
||||
dram_load_u16(hle, (uint16_t*)hle->alist_naudio.table, address, count >> 1);
|
||||
}
|
||||
|
||||
static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t dmemi = w1 + NAUDIO_MAIN;
|
||||
uint16_t dmemo = (w2 >> 16) + NAUDIO_MAIN;
|
||||
uint16_t count = w2;
|
||||
uint16_t dmemi = w1 + NAUDIO_MAIN;
|
||||
uint16_t dmemo = (w2 >> 16) + NAUDIO_MAIN;
|
||||
uint16_t count = w2;
|
||||
|
||||
alist_move(hle, dmemo, dmemi, (count + 3) & ~3);
|
||||
alist_move(hle, dmemo, dmemi, (count + 3) & ~3);
|
||||
}
|
||||
|
||||
static void SETLOOP(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
{
|
||||
hle->alist_naudio.loop = (w2 & 0xffffff);
|
||||
hle->alist_naudio.loop = (w2 & 0xffffff);
|
||||
}
|
||||
|
||||
static void ADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint32_t address = (w1 & 0xffffff);
|
||||
uint8_t flags = (w2 >> 28);
|
||||
uint16_t count = (w2 >> 16) & 0xfff;
|
||||
uint16_t dmemi = ((w2 >> 12) & 0xf) + NAUDIO_MAIN;
|
||||
uint16_t dmemo = (w2 & 0xfff) + NAUDIO_MAIN;
|
||||
uint32_t address = (w1 & 0xffffff);
|
||||
uint8_t flags = (w2 >> 28);
|
||||
uint16_t count = (w2 >> 16) & 0xfff;
|
||||
uint16_t dmemi = ((w2 >> 12) & 0xf) + NAUDIO_MAIN;
|
||||
uint16_t dmemo = (w2 & 0xfff) + NAUDIO_MAIN;
|
||||
|
||||
alist_adpcm(
|
||||
hle,
|
||||
flags & 0x1,
|
||||
flags & 0x2,
|
||||
false, /* unsuported by this ucode */
|
||||
dmemo,
|
||||
dmemi,
|
||||
(count + 0x1f) & ~0x1f,
|
||||
hle->alist_naudio.table,
|
||||
hle->alist_naudio.loop,
|
||||
address);
|
||||
alist_adpcm(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
flags & A_LOOP,
|
||||
false, /* unsuported by this ucode */
|
||||
dmemo,
|
||||
dmemi,
|
||||
(count + 0x1f) & ~0x1f,
|
||||
hle->alist_naudio.table,
|
||||
hle->alist_naudio.loop,
|
||||
address);
|
||||
}
|
||||
|
||||
static void RESAMPLE(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint32_t address = (w1 & 0xffffff);
|
||||
uint8_t flags = (w2 >> 30);
|
||||
uint16_t pitch = (w2 >> 14);
|
||||
uint16_t dmemi = ((w2 >> 2) & 0xfff) + NAUDIO_MAIN;
|
||||
uint16_t dmemo = (w2 & 0x3) ? NAUDIO_MAIN2 : NAUDIO_MAIN;
|
||||
uint32_t address = (w1 & 0xffffff);
|
||||
uint8_t flags = (w2 >> 30);
|
||||
uint16_t pitch = (w2 >> 14);
|
||||
uint16_t dmemi = ((w2 >> 2) & 0xfff) + NAUDIO_MAIN;
|
||||
uint16_t dmemo = (w2 & 0x3) ? NAUDIO_MAIN2 : NAUDIO_MAIN;
|
||||
|
||||
alist_resample(
|
||||
hle,
|
||||
flags & 0x1,
|
||||
false, /* TODO: check which ABI supports it */
|
||||
dmemo,
|
||||
dmemi,
|
||||
NAUDIO_COUNT,
|
||||
pitch << 1,
|
||||
address);
|
||||
alist_resample(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
false, /* TODO: check which ABI supports it */
|
||||
dmemo,
|
||||
dmemi,
|
||||
NAUDIO_COUNT,
|
||||
pitch << 1,
|
||||
address);
|
||||
}
|
||||
|
||||
static void INTERLEAVE(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t UNUSED(w2))
|
||||
{
|
||||
alist_interleave(hle, NAUDIO_MAIN, NAUDIO_DRY_LEFT, NAUDIO_DRY_RIGHT, NAUDIO_COUNT);
|
||||
alist_interleave(hle, NAUDIO_MAIN, NAUDIO_DRY_LEFT, NAUDIO_DRY_RIGHT, NAUDIO_COUNT);
|
||||
}
|
||||
|
||||
static void MP3ADDY(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2))
|
||||
@ -270,10 +257,20 @@ static void MP3ADDY(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNU
|
||||
|
||||
static void MP3(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
unsigned index = (w1 & 0x1e);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
unsigned index = (w1 & 0x1e);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
mp3_task(hle, index, address);
|
||||
mp3_task(hle, index, address);
|
||||
}
|
||||
|
||||
static void OVERLOAD(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
/* Overload distortion effect for Conker's Bad Fur Day */
|
||||
uint16_t dmem = (w1 & 0xfff) + NAUDIO_MAIN;
|
||||
int16_t gain = (int16_t)(uint16_t)w2;
|
||||
uint16_t attenuation = w2 >> 16;
|
||||
|
||||
alist_overload(hle, dmem, NAUDIO_COUNT, gain, attenuation);
|
||||
}
|
||||
|
||||
/* global functions */
|
||||
@ -282,11 +279,12 @@ void alist_process_naudio(struct hle_t* hle)
|
||||
static const acmd_callback_t ABI[0x10] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, ENVMIXER,
|
||||
LOADBUFF, RESAMPLE, SAVEBUFF, NAUDIO_0000,
|
||||
NAUDIO_0000, SETVOL, DMEMMOVE, NAUDIO_LOADADPCM,
|
||||
NAUDIO_0000, SETVOL, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, NAUDIO_02B0, SETLOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x10);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_naudio_bk(struct hle_t* hle)
|
||||
@ -295,11 +293,12 @@ void alist_process_naudio_bk(struct hle_t* hle)
|
||||
static const acmd_callback_t ABI[0x10] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, ENVMIXER,
|
||||
LOADBUFF, RESAMPLE, SAVEBUFF, NAUDIO_0000,
|
||||
NAUDIO_0000, SETVOL, DMEMMOVE, NAUDIO_LOADADPCM,
|
||||
NAUDIO_0000, SETVOL, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, NAUDIO_02B0, SETLOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x10);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_naudio_dk(struct hle_t* hle)
|
||||
@ -308,34 +307,48 @@ void alist_process_naudio_dk(struct hle_t* hle)
|
||||
static const acmd_callback_t ABI[0x10] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, ENVMIXER,
|
||||
LOADBUFF, RESAMPLE, SAVEBUFF, MIXER,
|
||||
MIXER, SETVOL, DMEMMOVE, NAUDIO_LOADADPCM,
|
||||
MIXER, SETVOL, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, NAUDIO_02B0, SETLOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x10);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_naudio_mp3(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x10] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, ENVMIXER,
|
||||
OVERLOAD, ADPCM, CLEARBUFF, ENVMIXER,
|
||||
LOADBUFF, RESAMPLE, SAVEBUFF, MP3,
|
||||
MP3ADDY, SETVOL, DMEMMOVE, NAUDIO_LOADADPCM,
|
||||
MP3ADDY, SETVOL, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, NAUDIO_14, SETLOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x10);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_naudio_cbfd(struct hle_t* hle)
|
||||
{
|
||||
/* TODO: see what differs from alist_process_naudio_mp3 */
|
||||
/* What differs from alist_process_naudio_mp3?
|
||||
*
|
||||
* JoshW: It appears that despite being a newer game, CBFD appears to have a slightly older ucode version
|
||||
* compared to JFG, B.T. et al.
|
||||
* For naudio_mp3, the functions DMEM parameters have an additional protective AND on them
|
||||
* (basically dmem & 0xffff).
|
||||
* But there are minor differences are in the RESAMPLE and ENVMIXER functions.
|
||||
* I don't think it is making any noticeable difference, as it could be just a simplification of the logic.
|
||||
*
|
||||
* bsmiles32: The only difference I could remember between mp3 and cbfd variants is in the MP3ADDY command.
|
||||
* And the MP3 overlay is also different.
|
||||
*/
|
||||
static const acmd_callback_t ABI[0x10] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, ENVMIXER,
|
||||
OVERLOAD, ADPCM, CLEARBUFF, ENVMIXER,
|
||||
LOADBUFF, RESAMPLE, SAVEBUFF, MP3,
|
||||
MP3ADDY, SETVOL, DMEMMOVE, NAUDIO_LOADADPCM,
|
||||
MP3ADDY, SETVOL, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, NAUDIO_14, SETLOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x10);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - alist_nead.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* Copyright (C) 2009 Richard Goedeken *
|
||||
* Copyright (C) 2002 Hacktarux *
|
||||
@ -21,15 +21,15 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <boolean.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "alist.h"
|
||||
#include "common.h"
|
||||
#include "hle_external.h"
|
||||
#include "hle_internal.h"
|
||||
#include "memory.h"
|
||||
#include "ucodes.h"
|
||||
|
||||
/* remove windows define to 0x06 */
|
||||
#ifdef DUPLICATE
|
||||
@ -39,11 +39,11 @@
|
||||
/* audio commands definition */
|
||||
static void UNKNOWN(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t acmd = (w1 >> 24);
|
||||
uint8_t acmd = (w1 >> 24);
|
||||
|
||||
HleWarnMessage(hle->user_defined,
|
||||
"Unknown audio command %d: %08x %08x",
|
||||
acmd, w1, w2);
|
||||
HleWarnMessage(hle->user_defined,
|
||||
"Unknown audio command %d: %08x %08x",
|
||||
acmd, w1, w2);
|
||||
}
|
||||
|
||||
|
||||
@ -51,298 +51,302 @@ static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUS
|
||||
{
|
||||
}
|
||||
|
||||
static void NEAD_LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
static void LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = w1;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint16_t count = w1;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
if (!hle)
|
||||
return;
|
||||
|
||||
dram_load_u16(hle, (uint16_t*)hle->alist_nead.table, address, count >> 1);
|
||||
dram_load_u16(hle, (uint16_t*)hle->alist_nead.table, address, count >> 1);
|
||||
}
|
||||
|
||||
static void SETLOOP(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
{
|
||||
hle->alist_nead.loop = w2 & 0xffffff;
|
||||
hle->alist_nead.loop = w2 & 0xffffff;
|
||||
}
|
||||
|
||||
static void SETBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
hle->alist_nead.in = w1;
|
||||
hle->alist_nead.out = (w2 >> 16);
|
||||
hle->alist_nead.count = w2;
|
||||
hle->alist_nead.in = w1;
|
||||
hle->alist_nead.out = (w2 >> 16);
|
||||
hle->alist_nead.count = w2;
|
||||
}
|
||||
|
||||
static void ADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
alist_adpcm(
|
||||
hle,
|
||||
flags & 0x1,
|
||||
flags & 0x2,
|
||||
flags & 0x4,
|
||||
hle->alist_nead.out,
|
||||
hle->alist_nead.in,
|
||||
(hle->alist_nead.count + 0x1f) & ~0x1f,
|
||||
hle->alist_nead.table,
|
||||
hle->alist_nead.loop,
|
||||
address);
|
||||
alist_adpcm(
|
||||
hle,
|
||||
flags & 0x1,
|
||||
flags & 0x2,
|
||||
flags & 0x4,
|
||||
hle->alist_nead.out,
|
||||
hle->alist_nead.in,
|
||||
(hle->alist_nead.count + 0x1f) & ~0x1f,
|
||||
hle->alist_nead.table,
|
||||
hle->alist_nead.loop,
|
||||
address);
|
||||
}
|
||||
|
||||
static void CLEARBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t dmem = w1;
|
||||
uint16_t count = w2 & 0xfff;
|
||||
uint16_t dmem = w1;
|
||||
uint16_t count = w2 & 0xfff;
|
||||
|
||||
if (count != 0)
|
||||
alist_clear(hle, dmem, count);
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
alist_clear(hle, dmem, count);
|
||||
}
|
||||
|
||||
static void LOADBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = (w1 >> 12) & 0xfff;
|
||||
uint16_t dmem = (w1 & 0xfff);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint16_t count = (w1 >> 12) & 0xfff;
|
||||
uint16_t dmem = (w1 & 0xfff);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
alist_load(hle, dmem, address, count);
|
||||
alist_load(hle, dmem, address, count);
|
||||
}
|
||||
|
||||
static void SAVEBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = (w1 >> 12) & 0xfff;
|
||||
uint16_t dmem = (w1 & 0xfff);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint16_t count = (w1 >> 12) & 0xfff;
|
||||
uint16_t dmem = (w1 & 0xfff);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
alist_save(hle, dmem, address, count);
|
||||
alist_save(hle, dmem, address, count);
|
||||
}
|
||||
|
||||
static void MIXER(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = (w1 >> 12) & 0xff0;
|
||||
int16_t gain = w1;
|
||||
uint16_t dmemi = (w2 >> 16);
|
||||
uint16_t dmemo = w2;
|
||||
uint16_t count = (w1 >> 12) & 0xff0;
|
||||
int16_t gain = w1;
|
||||
uint16_t dmemi = (w2 >> 16);
|
||||
uint16_t dmemo = w2;
|
||||
|
||||
alist_mix(hle, dmemo, dmemi, count, gain);
|
||||
alist_mix(hle, dmemo, dmemi, count, gain);
|
||||
}
|
||||
|
||||
|
||||
static void RESAMPLE(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t pitch = w1;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t pitch = w1;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
alist_resample(
|
||||
hle,
|
||||
flags & 0x1,
|
||||
false, /* TODO: check which ABI supports it */
|
||||
hle->alist_nead.out,
|
||||
hle->alist_nead.in,
|
||||
(hle->alist_nead.count + 0xf) & ~0xf,
|
||||
pitch << 1,
|
||||
address);
|
||||
alist_resample(
|
||||
hle,
|
||||
flags & 0x1,
|
||||
false, /* TODO: check which ABI supports it */
|
||||
hle->alist_nead.out,
|
||||
hle->alist_nead.in,
|
||||
(hle->alist_nead.count + 0xf) & ~0xf,
|
||||
pitch << 1,
|
||||
address);
|
||||
}
|
||||
|
||||
static void RESAMPLE_ZOH(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t pitch = w1;
|
||||
uint16_t pitch_accu = w2;
|
||||
uint16_t pitch = w1;
|
||||
uint16_t pitch_accu = w2;
|
||||
|
||||
alist_resample_zoh(
|
||||
hle,
|
||||
hle->alist_nead.out,
|
||||
hle->alist_nead.in,
|
||||
hle->alist_nead.count,
|
||||
pitch << 1,
|
||||
pitch_accu);
|
||||
alist_resample_zoh(
|
||||
hle,
|
||||
hle->alist_nead.out,
|
||||
hle->alist_nead.in,
|
||||
hle->alist_nead.count,
|
||||
pitch << 1,
|
||||
pitch_accu);
|
||||
}
|
||||
|
||||
static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t count)
|
||||
static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t dmemi = w1;
|
||||
uint16_t dmemo = (count >> 16);
|
||||
uint16_t dmemi = w1;
|
||||
uint16_t dmemo = (w2 >> 16);
|
||||
uint16_t count = w2;
|
||||
|
||||
if (count != 0)
|
||||
alist_move(hle, dmemo, dmemi, (count + 3) & ~3);
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
alist_move(hle, dmemo, dmemi, (count + 3) & ~3);
|
||||
}
|
||||
|
||||
static void ENVSETUP1_MK(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
hle->alist_nead.env_values[2] = (w1 >> 8) & 0xff00;
|
||||
hle->alist_nead.env_steps[2] = 0;
|
||||
hle->alist_nead.env_steps[0] = (w2 >> 16);
|
||||
hle->alist_nead.env_steps[1] = w2;
|
||||
hle->alist_nead.env_values[2] = (w1 >> 8) & 0xff00;
|
||||
hle->alist_nead.env_steps[2] = 0;
|
||||
hle->alist_nead.env_steps[0] = (w2 >> 16);
|
||||
hle->alist_nead.env_steps[1] = w2;
|
||||
}
|
||||
|
||||
static void ENVSETUP1(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
hle->alist_nead.env_values[2] = (w1 >> 8) & 0xff00;
|
||||
hle->alist_nead.env_steps[2] = w1;
|
||||
hle->alist_nead.env_steps[0] = (w2 >> 16);
|
||||
hle->alist_nead.env_steps[1] = w2;
|
||||
hle->alist_nead.env_values[2] = (w1 >> 8) & 0xff00;
|
||||
hle->alist_nead.env_steps[2] = w1;
|
||||
hle->alist_nead.env_steps[0] = (w2 >> 16);
|
||||
hle->alist_nead.env_steps[1] = w2;
|
||||
}
|
||||
|
||||
static void ENVSETUP2(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
{
|
||||
hle->alist_nead.env_values[0] = (w2 >> 16);
|
||||
hle->alist_nead.env_values[1] = w2;
|
||||
hle->alist_nead.env_values[0] = (w2 >> 16);
|
||||
hle->alist_nead.env_values[1] = w2;
|
||||
}
|
||||
|
||||
static void ENVMIXER_MK(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
int16_t xors[4];
|
||||
int16_t xors[4];
|
||||
|
||||
uint16_t dmemi = (w1 >> 12) & 0xff0;
|
||||
uint8_t count = (w1 >> 8) & 0xff;
|
||||
uint16_t dmem_dl = (w2 >> 20) & 0xff0;
|
||||
uint16_t dmem_dr = (w2 >> 12) & 0xff0;
|
||||
uint16_t dmem_wl = (w2 >> 4) & 0xff0;
|
||||
uint16_t dmem_wr = (w2 << 4) & 0xff0;
|
||||
uint16_t dmemi = (w1 >> 12) & 0xff0;
|
||||
uint8_t count = (w1 >> 8) & 0xff;
|
||||
uint16_t dmem_dl = (w2 >> 20) & 0xff0;
|
||||
uint16_t dmem_dr = (w2 >> 12) & 0xff0;
|
||||
uint16_t dmem_wl = (w2 >> 4) & 0xff0;
|
||||
uint16_t dmem_wr = (w2 << 4) & 0xff0;
|
||||
|
||||
xors[2] = 0; /* unsupported by this ucode */
|
||||
xors[3] = 0; /* unsupported by this ucode */
|
||||
xors[0] = 0 - (int16_t)((w1 & 0x2) >> 1);
|
||||
xors[1] = 0 - (int16_t)((w1 & 0x1) );
|
||||
xors[2] = 0; /* unsupported by this ucode */
|
||||
xors[3] = 0; /* unsupported by this ucode */
|
||||
xors[0] = 0 - (int16_t)((w1 & 0x2) >> 1);
|
||||
xors[1] = 0 - (int16_t)((w1 & 0x1) );
|
||||
|
||||
alist_envmix_nead(
|
||||
hle,
|
||||
false, /* unsupported by this ucode */
|
||||
dmem_dl, dmem_dr,
|
||||
dmem_wl, dmem_wr,
|
||||
dmemi, count,
|
||||
hle->alist_nead.env_values,
|
||||
hle->alist_nead.env_steps,
|
||||
xors);
|
||||
alist_envmix_nead(
|
||||
hle,
|
||||
false, /* unsupported by this ucode */
|
||||
dmem_dl, dmem_dr,
|
||||
dmem_wl, dmem_wr,
|
||||
dmemi, count,
|
||||
hle->alist_nead.env_values,
|
||||
hle->alist_nead.env_steps,
|
||||
xors);
|
||||
}
|
||||
|
||||
static void ENVMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
int16_t xors[4];
|
||||
int16_t xors[4];
|
||||
|
||||
uint16_t dmemi = (w1 >> 12) & 0xff0;
|
||||
uint8_t count = (w1 >> 8) & 0xff;
|
||||
bool swap_wet_LR = (w1 >> 4) & 0x1;
|
||||
uint16_t dmem_dl = (w2 >> 20) & 0xff0;
|
||||
uint16_t dmem_dr = (w2 >> 12) & 0xff0;
|
||||
uint16_t dmem_wl = (w2 >> 4) & 0xff0;
|
||||
uint16_t dmem_wr = (w2 << 4) & 0xff0;
|
||||
uint16_t dmemi = (w1 >> 12) & 0xff0;
|
||||
uint8_t count = (w1 >> 8) & 0xff;
|
||||
bool swap_wet_LR = (w1 >> 4) & 0x1;
|
||||
uint16_t dmem_dl = (w2 >> 20) & 0xff0;
|
||||
uint16_t dmem_dr = (w2 >> 12) & 0xff0;
|
||||
uint16_t dmem_wl = (w2 >> 4) & 0xff0;
|
||||
uint16_t dmem_wr = (w2 << 4) & 0xff0;
|
||||
|
||||
xors[2] = 0 - (int16_t)((w1 & 0x8) >> 1);
|
||||
xors[3] = 0 - (int16_t)((w1 & 0x4) >> 1);
|
||||
xors[0] = 0 - (int16_t)((w1 & 0x2) >> 1);
|
||||
xors[1] = 0 - (int16_t)((w1 & 0x1) );
|
||||
xors[2] = 0 - (int16_t)((w1 & 0x8) >> 1);
|
||||
xors[3] = 0 - (int16_t)((w1 & 0x4) >> 1);
|
||||
xors[0] = 0 - (int16_t)((w1 & 0x2) >> 1);
|
||||
xors[1] = 0 - (int16_t)((w1 & 0x1) );
|
||||
|
||||
alist_envmix_nead(
|
||||
hle,
|
||||
swap_wet_LR,
|
||||
dmem_dl, dmem_dr,
|
||||
dmem_wl, dmem_wr,
|
||||
dmemi, count,
|
||||
hle->alist_nead.env_values,
|
||||
hle->alist_nead.env_steps,
|
||||
xors);
|
||||
alist_envmix_nead(
|
||||
hle,
|
||||
swap_wet_LR,
|
||||
dmem_dl, dmem_dr,
|
||||
dmem_wl, dmem_wr,
|
||||
dmemi, count,
|
||||
hle->alist_nead.env_values,
|
||||
hle->alist_nead.env_steps,
|
||||
xors);
|
||||
}
|
||||
|
||||
static void DUPLICATE(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t count = (w1 >> 16);
|
||||
uint16_t dmemi = w1;
|
||||
uint16_t dmemo = (w2 >> 16);
|
||||
uint8_t count = (w1 >> 16);
|
||||
uint16_t dmemi = w1;
|
||||
uint16_t dmemo = (w2 >> 16);
|
||||
|
||||
alist_repeat64(hle, dmemo, dmemi, count);
|
||||
alist_repeat64(hle, dmemo, dmemi, count);
|
||||
}
|
||||
|
||||
static void INTERL(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = w1;
|
||||
uint16_t dmemi = (w2 >> 16);
|
||||
uint16_t dmemo = w2;
|
||||
uint16_t count = w1;
|
||||
uint16_t dmemi = (w2 >> 16);
|
||||
uint16_t dmemo = w2;
|
||||
|
||||
alist_copy_every_other_sample(hle, dmemo, dmemi, count);
|
||||
alist_copy_every_other_sample(hle, dmemo, dmemi, count);
|
||||
}
|
||||
|
||||
static void INTERLEAVE_MK(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2)
|
||||
{
|
||||
uint16_t left = (w2 >> 16);
|
||||
uint16_t right = w2;
|
||||
uint16_t left = (w2 >> 16);
|
||||
uint16_t right = w2;
|
||||
|
||||
if (hle->alist_nead.count != 0)
|
||||
alist_interleave(hle, hle->alist_nead.out, left, right, hle->alist_nead.count);
|
||||
if (hle->alist_nead.count == 0)
|
||||
return;
|
||||
|
||||
alist_interleave(hle, hle->alist_nead.out, left, right, hle->alist_nead.count);
|
||||
}
|
||||
|
||||
static void INTERLEAVE(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = ((w1 >> 12) & 0xff0);
|
||||
uint16_t dmemo = w1;
|
||||
uint16_t left = (w2 >> 16);
|
||||
uint16_t right = w2;
|
||||
uint16_t count = ((w1 >> 12) & 0xff0);
|
||||
uint16_t dmemo = w1;
|
||||
uint16_t left = (w2 >> 16);
|
||||
uint16_t right = w2;
|
||||
|
||||
alist_interleave(hle, dmemo, left, right, count);
|
||||
alist_interleave(hle, dmemo, left, right, count);
|
||||
}
|
||||
|
||||
static void ADDMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t count = (w1 >> 12) & 0xff0;
|
||||
uint16_t dmemi = (w2 >> 16);
|
||||
uint16_t dmemo = w2;
|
||||
uint16_t count = (w1 >> 12) & 0xff0;
|
||||
uint16_t dmemi = (w2 >> 16);
|
||||
uint16_t dmemo = w2;
|
||||
|
||||
alist_add(hle, dmemo, dmemi, count);
|
||||
alist_add(hle, dmemo, dmemi, count);
|
||||
}
|
||||
|
||||
static void HILOGAIN(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
int8_t gain = (w1 >> 16); /* Q4.4 signed */
|
||||
uint16_t count = w1 & 0xfff;
|
||||
uint16_t dmem = (w2 >> 16);
|
||||
int8_t gain = (w1 >> 16); /* Q4.4 signed */
|
||||
uint16_t count = w1 & 0xfff;
|
||||
uint16_t dmem = (w2 >> 16);
|
||||
|
||||
alist_multQ44(hle, dmem, count, gain);
|
||||
alist_multQ44(hle, dmem, count, gain);
|
||||
}
|
||||
|
||||
static void FILTER(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint16_t dmem = w1;
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
if (flags > 1)
|
||||
{
|
||||
hle->alist_nead.filter_count = w1;
|
||||
hle->alist_nead.filter_lut_address[0] = address; /* t6 */
|
||||
return;
|
||||
}
|
||||
if (flags > 1) {
|
||||
hle->alist_nead.filter_count = w1;
|
||||
hle->alist_nead.filter_lut_address[0] = address; /* t6 */
|
||||
}
|
||||
else {
|
||||
uint16_t dmem = w1;
|
||||
|
||||
hle->alist_nead.filter_lut_address[1] = address + 0x10; /* t5 */
|
||||
alist_filter(hle, dmem, hle->alist_nead.filter_count,
|
||||
address, hle->alist_nead.filter_lut_address);
|
||||
hle->alist_nead.filter_lut_address[1] = address + 0x10; /* t5 */
|
||||
alist_filter(hle, dmem, hle->alist_nead.filter_count, address, hle->alist_nead.filter_lut_address);
|
||||
}
|
||||
}
|
||||
|
||||
static void SEGMENT(struct hle_t* UNUSED(hle),
|
||||
uint32_t UNUSED(w1), uint32_t UNUSED(w2))
|
||||
static void SEGMENT(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2))
|
||||
{
|
||||
}
|
||||
|
||||
static void NEAD_16(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t count = (w1 >> 16);
|
||||
uint16_t dmemi = w1;
|
||||
uint16_t dmemo = (w2 >> 16);
|
||||
uint16_t block_size = w2;
|
||||
uint8_t count = (w1 >> 16);
|
||||
uint16_t dmemi = w1;
|
||||
uint16_t dmemo = (w2 >> 16);
|
||||
uint16_t block_size = w2;
|
||||
|
||||
alist_copy_blocks(hle, dmemo, dmemi, block_size, count);
|
||||
alist_copy_blocks(hle, dmemo, dmemi, block_size, count);
|
||||
}
|
||||
|
||||
static void POLEF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
{
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t gain = w1;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
uint8_t flags = (w1 >> 16);
|
||||
uint16_t gain = w1;
|
||||
uint32_t address = (w2 & 0xffffff);
|
||||
|
||||
if (hle->alist_nead.count != 0)
|
||||
alist_polef(
|
||||
if (hle->alist_nead.count == 0)
|
||||
return;
|
||||
|
||||
alist_polef(
|
||||
hle,
|
||||
flags & A_INIT,
|
||||
hle->alist_nead.out,
|
||||
@ -356,164 +360,197 @@ static void POLEF(struct hle_t* hle, uint32_t w1, uint32_t w2)
|
||||
|
||||
void alist_process_nead_mk(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, SPNOOP,
|
||||
SPNOOP, RESAMPLE, SPNOOP, SEGMENT,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE_MK, POLEF, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1_MK, ENVMIXER_MK,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, SPNOOP,
|
||||
SPNOOP, RESAMPLE, SPNOOP, SEGMENT,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE_MK, POLEF, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1_MK, ENVMIXER_MK,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x20);
|
||||
alist_process(hle, ABI, 0x20);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_sf(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE_MK, POLEF, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, SPNOOP,
|
||||
HILOGAIN, UNKNOWN, DUPLICATE, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE_MK, POLEF, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, SPNOOP,
|
||||
HILOGAIN, UNKNOWN, DUPLICATE, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x20);
|
||||
alist_process(hle, ABI, 0x20);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_sfj(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE_MK, POLEF, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN,
|
||||
HILOGAIN, UNKNOWN, DUPLICATE, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE_MK, POLEF, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN,
|
||||
HILOGAIN, UNKNOWN, DUPLICATE, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x20);
|
||||
alist_process(hle, ABI, 0x20);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_fz(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, SPNOOP, SPNOOP,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE, SPNOOP, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN,
|
||||
SPNOOP, UNKNOWN, DUPLICATE, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, SPNOOP, SPNOOP,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, SPNOOP, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN,
|
||||
SPNOOP, UNKNOWN, DUPLICATE, SPNOOP,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x20);
|
||||
alist_process(hle, ABI, 0x20);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_wrjb(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, UNKNOWN,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE, SPNOOP, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN,
|
||||
HILOGAIN, UNKNOWN, DUPLICATE, FILTER,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
static const acmd_callback_t ABI[0x20] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, UNKNOWN,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP,
|
||||
SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, SPNOOP, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN,
|
||||
HILOGAIN, UNKNOWN, DUPLICATE, FILTER,
|
||||
SPNOOP, SPNOOP, SPNOOP, SPNOOP
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x20);
|
||||
alist_process(hle, ABI, 0x20);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_ys(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x18);
|
||||
alist_process(hle, ABI, 0x18);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_1080(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x18);
|
||||
alist_process(hle, ABI, 0x18);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_oot(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x18);
|
||||
alist_process(hle, ABI, 0x18);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_mm(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x18);
|
||||
alist_process(hle, ABI, 0x18);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_mmb(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
SPNOOP, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x18);
|
||||
alist_process(hle, ABI, 0x18);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_ac(struct hle_t* hle)
|
||||
{
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, NEAD_LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
static const acmd_callback_t ABI[0x18] = {
|
||||
UNKNOWN, ADPCM, CLEARBUFF, SPNOOP,
|
||||
ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER,
|
||||
SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM,
|
||||
MIXER, INTERLEAVE, HILOGAIN, SETLOOP,
|
||||
NEAD_16, INTERL, ENVSETUP1, ENVMIXER,
|
||||
LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN
|
||||
};
|
||||
|
||||
alist_process(hle, ABI, 0x18);
|
||||
alist_process(hle, ABI, 0x18);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void alist_process_nead_mats(struct hle_t* hle)
|
||||
{
|
||||
/* FIXME: implement proper ucode
|
||||
* Forward the task if possible,
|
||||
* otherwise better to have no sound than garbage sound
|
||||
*/
|
||||
if (HleForwardTask(hle->user_defined) != 0) {
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
}
|
||||
|
||||
void alist_process_nead_efz(struct hle_t* hle)
|
||||
{
|
||||
/* FIXME: implement proper ucode
|
||||
* Forward the task if possible,
|
||||
* otherwise use FZero ucode which should be very similar
|
||||
*/
|
||||
if (HleForwardTask(hle->user_defined) != 0) {
|
||||
alist_process_nead_fz(hle);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - arithmetics.h *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -22,11 +22,11 @@
|
||||
#ifndef ARITHMETICS_H
|
||||
#define ARITHMETICS_H
|
||||
|
||||
#include <retro_inline.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static INLINE int16_t clamp_s16(int_fast32_t x)
|
||||
#include "common.h"
|
||||
|
||||
static inline int16_t clamp_s16(int_fast32_t x)
|
||||
{
|
||||
x = (x < INT16_MIN) ? INT16_MIN: x;
|
||||
x = (x > INT16_MAX) ? INT16_MAX: x;
|
||||
@ -34,9 +34,9 @@ static INLINE int16_t clamp_s16(int_fast32_t x)
|
||||
return x;
|
||||
}
|
||||
|
||||
static INLINE int32_t vmulf(int16_t x, int16_t y)
|
||||
static inline int32_t vmulf(int16_t x, int16_t y)
|
||||
{
|
||||
return (((int32_t)(x))*((int32_t)(y))+0x4000)>>15;
|
||||
return (((int32_t)(x))*((int32_t)(y))+0x4000)>>15;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - audio.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -94,29 +94,35 @@ const int16_t RESAMPLE_LUT[64 * 4] = {
|
||||
|
||||
int32_t rdot(size_t n, const int16_t *x, const int16_t *y)
|
||||
{
|
||||
int32_t accu = 0;
|
||||
int32_t accu = 0;
|
||||
|
||||
while (n-- != 0)
|
||||
accu += *(x++) * *(--y);
|
||||
y += n;
|
||||
|
||||
return accu;
|
||||
while (n != 0) {
|
||||
accu += *(x++) * *(--y);
|
||||
--n;
|
||||
}
|
||||
|
||||
return accu;
|
||||
}
|
||||
|
||||
void adpcm_compute_residuals(int16_t* dst, const int16_t* src,
|
||||
const int16_t* cb_entry, const int16_t* last_samples, size_t count)
|
||||
{
|
||||
size_t i;
|
||||
const int16_t* const book1 = cb_entry;
|
||||
const int16_t* const book2 = cb_entry + 8;
|
||||
const int16_t* const book1 = cb_entry;
|
||||
const int16_t* const book2 = cb_entry + 8;
|
||||
|
||||
const int16_t l1 = last_samples[0];
|
||||
const int16_t l2 = last_samples[1];
|
||||
const int16_t l1 = last_samples[0];
|
||||
const int16_t l2 = last_samples[1];
|
||||
|
||||
for(i = 0; i < count; ++i)
|
||||
{
|
||||
int32_t accu = (int32_t)src[i] << 11;
|
||||
accu += book1[i]*l1 + book2[i]*l2 + rdot(i, book2, src + i);
|
||||
dst[i] = clamp_s16(accu >> 11);
|
||||
size_t i;
|
||||
|
||||
assert(count <= 8);
|
||||
|
||||
for(i = 0; i < count; ++i) {
|
||||
int32_t accu = (int32_t)src[i] << 11;
|
||||
accu += book1[i]*l1 + book2[i]*l2 + rdot(i, book2, src);
|
||||
dst[i] = clamp_s16(accu >> 11);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - audio.h *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -25,11 +25,19 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
extern const int16_t RESAMPLE_LUT[64 * 4];
|
||||
|
||||
int32_t rdot(size_t n, const int16_t *x, const int16_t *y);
|
||||
|
||||
#define adpcm_predict_sample(byte, mask, lshift, rshift) (((int16_t)(((uint16_t)((byte) & (mask)) << (lshift))) >> (rshift)))
|
||||
static inline int16_t adpcm_predict_sample(uint8_t byte, uint8_t mask,
|
||||
unsigned lshift, unsigned rshift)
|
||||
{
|
||||
int16_t sample = (uint16_t)(byte & mask) << lshift;
|
||||
sample >>= rshift; /* signed */
|
||||
return sample;
|
||||
}
|
||||
|
||||
void adpcm_compute_residuals(int16_t* dst, const int16_t* src,
|
||||
const int16_t* cb_entry, const int16_t* last_samples, size_t count);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - cicx105.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2012 Bobby Smiles *
|
||||
* Copyright (C) 2009 Richard Goedeken *
|
||||
* Copyright (C) 2002 Hacktarux *
|
||||
@ -35,20 +35,22 @@
|
||||
**/
|
||||
void cicx105_ucode(struct hle_t* hle)
|
||||
{
|
||||
/* memcpy is okay to use because access constrains are met (alignment, size) */
|
||||
unsigned int i;
|
||||
unsigned char *dst = hle->dram + 0x2fb1f0;
|
||||
unsigned char *src = hle->imem + 0x120;
|
||||
/* memcpy is okay to use because access constrains are met (alignment, size) */
|
||||
unsigned int i;
|
||||
unsigned char *dst = hle->dram + 0x2fb1f0;
|
||||
unsigned char *src = hle->imem + 0x120;
|
||||
|
||||
/* dma_read(0x1120, 0x1e8, 0x1e8) */
|
||||
memcpy(hle->imem + 0x120, hle->dram + 0x1e8, 0x1f0);
|
||||
/* dma_read(0x1120, 0x1e8, 0x1e8) */
|
||||
memcpy(hle->imem + 0x120, hle->dram + 0x1e8, 0x1f0);
|
||||
|
||||
/* dma_write(0x1120, 0x2fb1f0, 0xfe817000) */
|
||||
for (i = 0; i < 24; ++i)
|
||||
{
|
||||
memcpy(dst, src, 8);
|
||||
dst += 0xff0;
|
||||
src += 0x8;
|
||||
}
|
||||
/* dma_write(0x1120, 0x2fb1f0, 0xfe817000) */
|
||||
for (i = 0; i < 24; ++i) {
|
||||
memcpy(dst, src, 8);
|
||||
dst += 0xff0;
|
||||
src += 0x8;
|
||||
|
||||
}
|
||||
|
||||
rsp_break(hle, 0);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - common.h *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -29,5 +29,10 @@
|
||||
# define UNUSED(x) UNUSED_ ## x
|
||||
#endif
|
||||
|
||||
/* macro for inline keyword */
|
||||
#ifdef _MSC_VER
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - hle.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2012 Bobby Smiles *
|
||||
* Copyright (C) 2009 Richard Goedeken *
|
||||
* Copyright (C) 2002 Hacktarux *
|
||||
@ -21,44 +21,41 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <boolean.h>
|
||||
|
||||
#ifdef ENABLE_TASK_DUMP
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#include "hle_external.h"
|
||||
#include "hle_internal.h"
|
||||
#include "memory.h"
|
||||
#include "m64p_plugin.h"
|
||||
|
||||
#include "ucodes.h"
|
||||
|
||||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
/* some rsp status flags */
|
||||
#define SP_STATUS_HALT 0x1
|
||||
#define SP_STATUS_BROKE 0x2
|
||||
#define SP_STATUS_INTR_ON_BREAK 0x40
|
||||
#define SP_STATUS_TASKDONE 0x200
|
||||
|
||||
/* some rdp status flags */
|
||||
#define DP_STATUS_FREEZE 0x2
|
||||
|
||||
/* some mips interface interrupt flags */
|
||||
#define MI_INTR_SP 0x1
|
||||
|
||||
|
||||
/* helper functions prototypes */
|
||||
static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size);
|
||||
static void rsp_break(struct hle_t* hle, unsigned int setbits);
|
||||
static void forward_gfx_task(struct hle_t* hle);
|
||||
static bool try_fast_audio_dispatching(struct hle_t* hle);
|
||||
static bool try_fast_task_dispatching(struct hle_t* hle);
|
||||
static void normal_task_dispatching(struct hle_t* hle);
|
||||
static void non_task_dispatching(struct hle_t* hle);
|
||||
static bool is_task(struct hle_t* hle);
|
||||
static void send_dlist_to_gfx_plugin(struct hle_t* hle);
|
||||
static ucode_func_t try_audio_task_detection(struct hle_t* hle);
|
||||
static ucode_func_t try_normal_task_detection(struct hle_t* hle);
|
||||
static ucode_func_t non_task_detection(struct hle_t* hle);
|
||||
static ucode_func_t task_detection(struct hle_t* hle);
|
||||
|
||||
extern RSP_INFO rsp_info;
|
||||
|
||||
/* local variables */
|
||||
static const bool FORWARD_AUDIO = false, FORWARD_GFX = true;
|
||||
#ifdef ENABLE_TASK_DUMP
|
||||
static void dump_binary(struct hle_t* hle, const char *const filename,
|
||||
const unsigned char *const bytes, unsigned int size);
|
||||
static void dump_task(struct hle_t* hle, const char *const filename);
|
||||
static void dump_unknown_task(struct hle_t* hle, unsigned int uc_start);
|
||||
static void dump_unknown_non_task(struct hle_t* hle, unsigned int uc_start);
|
||||
#endif
|
||||
|
||||
/* Global functions */
|
||||
void hle_init(struct hle_t* hle,
|
||||
@ -109,30 +106,40 @@ void hle_init(struct hle_t* hle,
|
||||
hle->user_defined = user_defined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to figure if the RSP was launched using osSpTask* functions
|
||||
* and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless).
|
||||
*
|
||||
* Previously, the ucode_size field was used to determine this,
|
||||
* but it is not robust enough (hi Pokemon Stadium !) because games could write anything
|
||||
* in this field : most ucode_boot discard the value and just use 0xf7f anyway.
|
||||
*
|
||||
* Using ucode_boot_size should be more robust in this regard.
|
||||
**/
|
||||
#define is_task(hle) ((*dmem_u32((hle), TASK_UCODE_BOOT_SIZE) <= 0x1000))
|
||||
|
||||
void hle_execute(struct hle_t* hle)
|
||||
{
|
||||
if (is_task(hle))
|
||||
{
|
||||
if (!try_fast_task_dispatching(hle))
|
||||
normal_task_dispatching(hle);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
return;
|
||||
}
|
||||
uint32_t uc_start = *dmem_u32(hle, TASK_UCODE);
|
||||
uint32_t uc_dstart = *dmem_u32(hle, TASK_UCODE_DATA);
|
||||
uint32_t uc_dsize = *dmem_u32(hle, TASK_UCODE_DATA_SIZE);
|
||||
|
||||
non_task_dispatching(hle);
|
||||
rsp_break(hle, 0);
|
||||
bool match = false;
|
||||
struct cached_ucodes_t * cached_ucodes = &hle->cached_ucodes;
|
||||
struct ucode_info_t *info = NULL;
|
||||
if (cached_ucodes->count > 0)
|
||||
info = &cached_ucodes->infos[cached_ucodes->count-1];
|
||||
for (int i = 0; i < cached_ucodes->count; i++)
|
||||
{
|
||||
if (info->uc_start == uc_start && info->uc_dstart == uc_dstart && info->uc_dsize == uc_dsize)
|
||||
{
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
info--;
|
||||
}
|
||||
|
||||
if (!match)
|
||||
{
|
||||
info = &cached_ucodes->infos[cached_ucodes->count];
|
||||
info->uc_start = uc_start;
|
||||
info->uc_dstart = uc_dstart;
|
||||
info->uc_dsize = uc_dsize;
|
||||
info->uc_pfunc = task_detection(hle);
|
||||
cached_ucodes->count++;
|
||||
assert(cached_ucodes->count <= CACHED_UCODES_MAX_SIZE);
|
||||
assert(info->uc_pfunc != NULL);
|
||||
}
|
||||
|
||||
info->uc_pfunc(hle);
|
||||
}
|
||||
|
||||
/* local functions */
|
||||
@ -147,233 +154,371 @@ static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size)
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
static void rsp_break(struct hle_t* hle, unsigned int setbits)
|
||||
/**
|
||||
* Try to figure if the RSP was launched using osSpTask* functions
|
||||
* and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless).
|
||||
*
|
||||
* Previously, the ucode_size field was used to determine this,
|
||||
* but it is not robust enough (hi Pokemon Stadium !) because games could write anything
|
||||
* in this field : most ucode_boot discard the value and just use 0xf7f anyway.
|
||||
*
|
||||
* Using ucode_boot_size should be more robust in this regard.
|
||||
**/
|
||||
static bool is_task(struct hle_t* hle)
|
||||
{
|
||||
*hle->sp_status |= setbits | SP_STATUS_BROKE | SP_STATUS_HALT;
|
||||
|
||||
if ((*hle->sp_status & SP_STATUS_INTR_ON_BREAK))
|
||||
{
|
||||
*hle->mi_intr |= MI_INTR_SP;
|
||||
if (rsp_info.CheckInterrupts)
|
||||
rsp_info.CheckInterrupts();
|
||||
}
|
||||
return (*dmem_u32(hle, TASK_UCODE_BOOT_SIZE) <= 0x1000);
|
||||
}
|
||||
|
||||
static void forward_gfx_task(struct hle_t* hle)
|
||||
void rsp_break(struct hle_t* hle, unsigned int setbits)
|
||||
{
|
||||
if (rsp_info.ProcessDlistList)
|
||||
rsp_info.ProcessDlistList();
|
||||
*hle->sp_status |= setbits | SP_STATUS_BROKE | SP_STATUS_HALT;
|
||||
|
||||
if ((*hle->sp_status & SP_STATUS_INTR_ON_BREAK)) {
|
||||
*hle->mi_intr |= MI_INTR_SP;
|
||||
HleCheckInterrupts(hle->user_defined);
|
||||
}
|
||||
}
|
||||
|
||||
static bool try_fast_audio_dispatching(struct hle_t* hle)
|
||||
static void send_alist_to_audio_plugin(struct hle_t* hle)
|
||||
{
|
||||
HleProcessAlistList(hle->user_defined);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
static void send_dlist_to_gfx_plugin(struct hle_t* hle)
|
||||
{
|
||||
/* Since GFX_INFO version 2, these bits are set before calling the ProcessDlistList function.
|
||||
* And the GFX plugin is responsible to unset them if needed.
|
||||
* For GFX_INFO version < 2, the GFX plugin didn't have access to sp_status so
|
||||
* it doesn't matter if we set these bits before calling ProcessDlistList function.
|
||||
*/
|
||||
*hle->sp_status |= SP_STATUS_TASKDONE | SP_STATUS_BROKE | SP_STATUS_HALT;
|
||||
|
||||
HleProcessDlistList(hle->user_defined);
|
||||
|
||||
if ((*hle->sp_status & SP_STATUS_INTR_ON_BREAK) && (*hle->sp_status & (SP_STATUS_TASKDONE | SP_STATUS_BROKE | SP_STATUS_HALT))) {
|
||||
*hle->mi_intr |= MI_INTR_SP;
|
||||
HleCheckInterrupts(hle->user_defined);
|
||||
}
|
||||
}
|
||||
|
||||
static void task_done(struct hle_t* hle)
|
||||
{
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
static void unknown_ucode(struct hle_t* hle)
|
||||
{
|
||||
/* Forward task to RSP Fallback.
|
||||
* If task is not forwarded, use the regular "unknown ucode" path */
|
||||
if (HleForwardTask(hle->user_defined) != 0) {
|
||||
|
||||
uint32_t uc_start = *dmem_u32(hle, TASK_UCODE);
|
||||
HleWarnMessage(hle->user_defined, "unknown RSP code: uc_start: %x PC:%x", uc_start, *hle->sp_pc);
|
||||
#ifdef ENABLE_TASK_DUMP
|
||||
dump_unknown_non_task(hle, uc_start);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void unknown_task(struct hle_t* hle)
|
||||
{
|
||||
/* Forward task to RSP Fallback.
|
||||
* If task is not forwarded, use the regular "unknown task" path */
|
||||
if (HleForwardTask(hle->user_defined) != 0) {
|
||||
|
||||
/* Send task_done signal for unknown ucodes to allow further processings */
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
|
||||
uint32_t uc_start = *dmem_u32(hle, TASK_UCODE);
|
||||
HleWarnMessage(hle->user_defined, "unknown OSTask: uc_start: %x PC:%x", uc_start, *hle->sp_pc);
|
||||
#ifdef ENABLE_TASK_DUMP
|
||||
dump_unknown_task(hle, uc_start);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static ucode_func_t try_audio_task_detection(struct hle_t* hle)
|
||||
{
|
||||
uint32_t v;
|
||||
/* identify audio ucode by using the content of ucode_data */
|
||||
uint32_t ucode_data = *dmem_u32(hle, TASK_UCODE_DATA);
|
||||
uint32_t v;
|
||||
|
||||
if (*dram_u32(hle, ucode_data) == 0x00000001)
|
||||
{
|
||||
if (*dram_u32(hle, ucode_data + 0x30) == 0xf0000f00)
|
||||
{
|
||||
v = *dram_u32(hle, ucode_data + 0x28);
|
||||
switch(v)
|
||||
{
|
||||
case 0x1e24138c: /* audio ABI (most common) */
|
||||
alist_process_audio(hle); return true;
|
||||
case 0x1dc8138c: /* GoldenEye */
|
||||
alist_process_audio_ge(hle); return true;
|
||||
case 0x1e3c1390: /* BlastCorp, DiddyKongRacing */
|
||||
alist_process_audio_bc(hle); return true;
|
||||
default:
|
||||
HleWarnMessage(hle->user_defined, "ABI1 identification regression: v=%08x", v);
|
||||
}
|
||||
if (*dram_u32(hle, ucode_data) == 0x00000001) {
|
||||
if (*dram_u32(hle, ucode_data + 0x30) == 0xf0000f00) {
|
||||
v = *dram_u32(hle, ucode_data + 0x28);
|
||||
switch(v)
|
||||
{
|
||||
case 0x1e24138c: /* audio ABI (most common) */
|
||||
return &alist_process_audio;
|
||||
case 0x1dc8138c: /* GoldenEye */
|
||||
return &alist_process_audio_ge;
|
||||
case 0x1e3c1390: /* BlastCorp, DiddyKongRacing */
|
||||
return &alist_process_audio_bc;
|
||||
default:
|
||||
HleWarnMessage(hle->user_defined, "ABI1 identification regression: v=%08x", v);
|
||||
}
|
||||
} else {
|
||||
v = *dram_u32(hle, ucode_data + 0x10);
|
||||
switch(v)
|
||||
{
|
||||
case 0x11181350: /* MarioKart, WaveRace (E) */
|
||||
return &alist_process_nead_mk;
|
||||
case 0x111812e0: /* StarFox (J) */
|
||||
return &alist_process_nead_sfj;
|
||||
case 0x110412ac: /* WaveRace (J RevB) */
|
||||
return &alist_process_nead_wrjb;
|
||||
case 0x110412cc: /* StarFox/LylatWars (except J) */
|
||||
return &alist_process_nead_sf;
|
||||
case 0x1cd01250: /* FZeroX */
|
||||
return &alist_process_nead_fz;
|
||||
case 0x1f08122c: /* YoshisStory */
|
||||
return &alist_process_nead_ys;
|
||||
case 0x1f38122c: /* 1080° Snowboarding */
|
||||
return &alist_process_nead_1080;
|
||||
case 0x1f681230: /* Zelda OoT / Zelda MM (J, J RevA) */
|
||||
return &alist_process_nead_oot;
|
||||
case 0x1f801250: /* Zelda MM (except J, J RevA, E Beta), PokemonStadium 2 */
|
||||
return &alist_process_nead_mm;
|
||||
case 0x109411f8: /* Zelda MM (E Beta) */
|
||||
return &alist_process_nead_mmb;
|
||||
case 0x1eac11b8: /* AnimalCrossing */
|
||||
return &alist_process_nead_ac;
|
||||
case 0x00010010: /* MusyX v2 (IndianaJones, BattleForNaboo) */
|
||||
return &musyx_v2_task;
|
||||
case 0x1f701238: /* Mario Artist Talent Studio */
|
||||
return &alist_process_nead_mats;
|
||||
case 0x1f4c1230: /* FZeroX Expansion */
|
||||
return &alist_process_nead_efz;
|
||||
default:
|
||||
HleWarnMessage(hle->user_defined, "ABI2 identification regression: v=%08x", v);
|
||||
}
|
||||
}
|
||||
else
|
||||
} else {
|
||||
v = *dram_u32(hle, ucode_data + 0x10);
|
||||
switch(v)
|
||||
{
|
||||
v = *dram_u32(hle, ucode_data + 0x10);
|
||||
switch(v)
|
||||
{
|
||||
case 0x11181350: /* MarioKart, WaveRace (E) */
|
||||
alist_process_nead_mk(hle); return true;
|
||||
case 0x111812e0: /* StarFox (J) */
|
||||
alist_process_nead_sfj(hle); return true;
|
||||
case 0x110412ac: /* WaveRace (J RevB) */
|
||||
alist_process_nead_wrjb(hle); return true;
|
||||
case 0x110412cc: /* StarFox/LylatWars (except J) */
|
||||
alist_process_nead_sf(hle); return true;
|
||||
case 0x1cd01250: /* FZeroX */
|
||||
alist_process_nead_fz(hle); return true;
|
||||
case 0x1f08122c: /* YoshisStory */
|
||||
alist_process_nead_ys(hle); return true;
|
||||
case 0x1f38122c: /* 1080° Snowboarding */
|
||||
alist_process_nead_1080(hle); return true;
|
||||
case 0x1f681230: /* Zelda OoT / Zelda MM (J, J RevA) */
|
||||
alist_process_nead_oot(hle); return true;
|
||||
case 0x1f801250: /* Zelda MM (except J, J RevA, E Beta), PokemonStadium 2 */
|
||||
alist_process_nead_mm(hle); return true;
|
||||
case 0x109411f8: /* Zelda MM (E Beta) */
|
||||
alist_process_nead_mmb(hle); return true;
|
||||
case 0x1eac11b8: /* AnimalCrossing */
|
||||
alist_process_nead_ac(hle); return true;
|
||||
case 0x00010010: /* MusyX v2 (IndianaJones, BattleForNaboo) */
|
||||
musyx_v2_task(hle); return true;
|
||||
case 0x00000001: /* MusyX v1
|
||||
RogueSquadron, ResidentEvil2, PolarisSnoCross,
|
||||
TheWorldIsNotEnough, RugratsInParis, NBAShowTime,
|
||||
HydroThunder, Tarzan, GauntletLegend, Rush2049 */
|
||||
return &musyx_v1_task;
|
||||
case 0x0000127c: /* naudio (many games) */
|
||||
return &alist_process_naudio;
|
||||
case 0x00001280: /* BanjoKazooie */
|
||||
return &alist_process_naudio_bk;
|
||||
case 0x1c58126c: /* DonkeyKong */
|
||||
return &alist_process_naudio_dk;
|
||||
case 0x1ae8143c: /* BanjoTooie, JetForceGemini, MickeySpeedWayUSA, PerfectDark */
|
||||
return &alist_process_naudio_mp3;
|
||||
case 0x1ab0140c: /* ConkerBadFurDay */
|
||||
return &alist_process_naudio_cbfd;
|
||||
|
||||
default:
|
||||
HleWarnMessage(hle->user_defined, "ABI2 identification regression: v=%08x", v);
|
||||
}
|
||||
default:
|
||||
HleWarnMessage(hle->user_defined, "ABI3 identification regression: v=%08x", v);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
v = *dram_u32(hle, ucode_data + 0x10);
|
||||
switch(v)
|
||||
{
|
||||
/* -- MusyX v1 --
|
||||
Star Wars: Rogue Squadron
|
||||
Resident Evil 2
|
||||
Polaris SnoCross
|
||||
007: The World Is Not Enough
|
||||
Rugrats In Paris
|
||||
NBA ShowTime
|
||||
Hydro Thunder
|
||||
Tarzan
|
||||
Gauntlet Legends
|
||||
Rush 2049
|
||||
*/
|
||||
case 0x00000001:
|
||||
musyx_v1_task(hle);
|
||||
return true;
|
||||
/* NAUDIO (many games) */
|
||||
case 0x0000127c:
|
||||
alist_process_naudio(hle);
|
||||
return true;
|
||||
/* Banjo Kazooie */
|
||||
case 0x00001280:
|
||||
alist_process_naudio_bk(hle);
|
||||
return true;
|
||||
/* Donkey Kong 64 */
|
||||
case 0x1c58126c:
|
||||
alist_process_naudio_dk(hle);
|
||||
return true;
|
||||
/* Banjo Tooie
|
||||
* Jet Force Gemini
|
||||
* Mickey's SpeedWay USA
|
||||
* Perfect Dark */
|
||||
case 0x1ae8143c:
|
||||
alist_process_naudio_mp3(hle);
|
||||
return true;
|
||||
case 0x1ab0140c:
|
||||
/* Conker's Bad Fur Day */
|
||||
alist_process_naudio_cbfd(hle);
|
||||
return true;
|
||||
default:
|
||||
HleWarnMessage(hle->user_defined, "ABI3 identification regression: v=%08x", v);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ucode_func_t try_normal_task_detection(struct hle_t* hle)
|
||||
{
|
||||
unsigned int sum =
|
||||
sum_bytes((void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE)), min(*dmem_u32(hle, TASK_UCODE_SIZE), 0xf80) >> 1);
|
||||
|
||||
switch (sum) {
|
||||
/* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */
|
||||
case 0x278:
|
||||
/* Nothing to emulate */
|
||||
return &task_done;
|
||||
|
||||
/* GFX: Twintris [misleading task->type == 0] */
|
||||
case 0x212ee:
|
||||
if (hle->hle_gfx) {
|
||||
return &send_dlist_to_gfx_plugin;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
/* JPEG: found in Pokemon Stadium J */
|
||||
case 0x2c85a:
|
||||
return &jpeg_decode_PS0;
|
||||
|
||||
/* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */
|
||||
case 0x2caa6:
|
||||
return &jpeg_decode_PS;
|
||||
|
||||
/* JPEG: found in Ogre Battle, Bottom of the 9th */
|
||||
case 0x130de:
|
||||
case 0x278b0:
|
||||
return &jpeg_decode_OB;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/* Resident Evil 2 */
|
||||
sum = sum_bytes((void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE)), 256);
|
||||
switch (sum) {
|
||||
|
||||
static bool try_fast_task_dispatching(struct hle_t* hle)
|
||||
{
|
||||
/* identify task ucode by its type */
|
||||
switch (*dmem_u32(hle, TASK_TYPE))
|
||||
{
|
||||
case 1:
|
||||
/* Resident evil 2 */
|
||||
if (*dmem_u32(hle, TASK_DATA_PTR) == 0)
|
||||
return false;
|
||||
case 0x450f:
|
||||
return &resize_bilinear_task;
|
||||
|
||||
if (FORWARD_GFX)
|
||||
{
|
||||
forward_gfx_task(hle);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 0x3b44:
|
||||
return &decode_video_frame_task;
|
||||
|
||||
case 2:
|
||||
if (FORWARD_AUDIO)
|
||||
{
|
||||
if (rsp_info.ProcessAlistList)
|
||||
rsp_info.ProcessAlistList();
|
||||
return true;
|
||||
}
|
||||
if (try_fast_audio_dispatching(hle))
|
||||
return true;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
if (rsp_info.ShowCFB)
|
||||
rsp_info.ShowCFB();
|
||||
return true;
|
||||
case 0x3d84:
|
||||
return &fill_video_double_buffer_task;
|
||||
}
|
||||
|
||||
return false;
|
||||
/* HVQM */
|
||||
sum = sum_bytes((void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE)), 1488);
|
||||
switch (sum) {
|
||||
case 0x19495:
|
||||
return &hvqm2_decode_sp1_task;
|
||||
|
||||
case 0x19728:
|
||||
return &hvqm2_decode_sp2_task;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void normal_task_dispatching(struct hle_t* hle)
|
||||
static ucode_func_t non_task_detection(struct hle_t* hle)
|
||||
{
|
||||
const unsigned int sum =
|
||||
sum_bytes((void*)dram_u32(hle,
|
||||
*dmem_u32(hle, TASK_UCODE)),
|
||||
min(*dmem_u32(hle, TASK_UCODE_SIZE), 0xf80) >> 1);
|
||||
const unsigned int sum = sum_bytes(hle->imem, 44);
|
||||
|
||||
switch (sum)
|
||||
{
|
||||
/* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */
|
||||
case 0x278:
|
||||
/* Nothing to emulate */
|
||||
return;
|
||||
|
||||
/* GFX: Twintris [misleading task->type == 0] */
|
||||
case 0x212ee:
|
||||
if (FORWARD_GFX)
|
||||
{
|
||||
forward_gfx_task(hle);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
/* JPEG: found in Pokemon Stadium J */
|
||||
case 0x2c85a:
|
||||
jpeg_decode_PS0(hle);
|
||||
return;
|
||||
|
||||
/* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */
|
||||
case 0x2caa6:
|
||||
jpeg_decode_PS(hle);
|
||||
return;
|
||||
|
||||
/* JPEG: found in Ogre Battle, Bottom of the 9th */
|
||||
case 0x130de:
|
||||
case 0x278b0:
|
||||
jpeg_decode_OB(hle);
|
||||
return;
|
||||
|
||||
/* Resident evil 2 */
|
||||
case 0x29a20: /* USA */
|
||||
case 0x298c5: /* Europe */
|
||||
case 0x298b8: /* USA Rev A */
|
||||
case 0x296d9: /* J */
|
||||
resize_bilinear_task(hle);
|
||||
return;
|
||||
}
|
||||
|
||||
HleWarnMessage(hle->user_defined, "unknown OSTask: sum: %x PC:%x", sum, *hle->sp_pc);
|
||||
if (sum == 0x9e2)
|
||||
{
|
||||
/* CIC x105 ucode (used during boot of CIC x105 games) */
|
||||
return &cicx105_ucode;
|
||||
}
|
||||
return &unknown_ucode;
|
||||
}
|
||||
|
||||
static void non_task_dispatching(struct hle_t* hle)
|
||||
static ucode_func_t task_detection(struct hle_t* hle)
|
||||
{
|
||||
const unsigned int sum = sum_bytes(hle->imem, 44);
|
||||
if (is_task(hle)) {
|
||||
ucode_func_t uc_pfunc;
|
||||
uint32_t type = *dmem_u32(hle, TASK_TYPE);
|
||||
|
||||
if (sum == 0x9e2)
|
||||
{
|
||||
/* CIC x105 ucode (used during boot of CIC x105 games) */
|
||||
cicx105_ucode(hle);
|
||||
return;
|
||||
}
|
||||
if (type == 2) {
|
||||
if (hle->hle_aud) {
|
||||
return &send_alist_to_audio_plugin;
|
||||
}
|
||||
uc_pfunc = try_audio_task_detection(hle);
|
||||
if (uc_pfunc)
|
||||
return uc_pfunc;
|
||||
}
|
||||
|
||||
HleWarnMessage(hle->user_defined, "unknown RSP code: sum: %x PC:%x", sum, *hle->sp_pc);
|
||||
uc_pfunc = try_normal_task_detection(hle);
|
||||
if (uc_pfunc)
|
||||
return uc_pfunc;
|
||||
|
||||
if (type == 1) {
|
||||
if (hle->hle_gfx) {
|
||||
return &send_dlist_to_gfx_plugin;
|
||||
}
|
||||
}
|
||||
|
||||
return &unknown_task;
|
||||
}
|
||||
else {
|
||||
return non_task_detection(hle);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_TASK_DUMP
|
||||
static void dump_unknown_task(struct hle_t* hle, unsigned int uc_start)
|
||||
{
|
||||
char filename[256];
|
||||
uint32_t ucode = *dmem_u32(hle, TASK_UCODE);
|
||||
uint32_t ucode_data = *dmem_u32(hle, TASK_UCODE_DATA);
|
||||
uint32_t data_ptr = *dmem_u32(hle, TASK_DATA_PTR);
|
||||
|
||||
sprintf(&filename[0], "task_%x.log", uc_start);
|
||||
dump_task(hle, filename);
|
||||
|
||||
/* dump ucode_boot */
|
||||
sprintf(&filename[0], "ucode_boot_%x.bin", uc_start);
|
||||
dump_binary(hle, filename, (void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE_BOOT)), *dmem_u32(hle, TASK_UCODE_BOOT_SIZE));
|
||||
|
||||
/* dump ucode */
|
||||
if (ucode != 0) {
|
||||
sprintf(&filename[0], "ucode_%x.bin", uc_start);
|
||||
dump_binary(hle, filename, (void*)dram_u32(hle, ucode), 0xf80);
|
||||
}
|
||||
|
||||
/* dump ucode_data */
|
||||
if (ucode_data != 0) {
|
||||
sprintf(&filename[0], "ucode_data_%x.bin", uc_start);
|
||||
dump_binary(hle, filename, (void*)dram_u32(hle, ucode_data), *dmem_u32(hle, TASK_UCODE_DATA_SIZE));
|
||||
}
|
||||
|
||||
/* dump data */
|
||||
if (data_ptr != 0) {
|
||||
sprintf(&filename[0], "data_%x.bin", uc_start);
|
||||
dump_binary(hle, filename, (void*)dram_u32(hle, data_ptr), *dmem_u32(hle, TASK_DATA_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_unknown_non_task(struct hle_t* hle, unsigned int uc_start)
|
||||
{
|
||||
char filename[256];
|
||||
|
||||
/* dump IMEM & DMEM for further analysis */
|
||||
sprintf(&filename[0], "imem_%x.bin", uc_start);
|
||||
dump_binary(hle, filename, hle->imem, 0x1000);
|
||||
|
||||
sprintf(&filename[0], "dmem_%x.bin", uc_start);
|
||||
dump_binary(hle, filename, hle->dmem, 0x1000);
|
||||
}
|
||||
|
||||
static void dump_binary(struct hle_t* hle, const char *const filename,
|
||||
const unsigned char *const bytes, unsigned int size)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
/* if file already exists, do nothing */
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
/* else we write bytes to the file */
|
||||
f = fopen(filename, "wb");
|
||||
if (f != NULL) {
|
||||
if (fwrite(bytes, 1, size, f) != size)
|
||||
HleErrorMessage(hle->user_defined, "Writing error on %s", filename);
|
||||
fclose(f);
|
||||
} else
|
||||
HleErrorMessage(hle->user_defined, "Couldn't open %s for writing !", filename);
|
||||
} else
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static void dump_task(struct hle_t* hle, const char *const filename)
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
f = fopen(filename, "w");
|
||||
fprintf(f,
|
||||
"type = %d\n"
|
||||
"flags = %d\n"
|
||||
"ucode_boot = %#08x size = %#x\n"
|
||||
"ucode = %#08x size = %#x\n"
|
||||
"ucode_data = %#08x size = %#x\n"
|
||||
"dram_stack = %#08x size = %#x\n"
|
||||
"output_buff = %#08x *size = %#x\n"
|
||||
"data = %#08x size = %#x\n"
|
||||
"yield_data = %#08x size = %#x\n",
|
||||
*dmem_u32(hle, TASK_TYPE),
|
||||
*dmem_u32(hle, TASK_FLAGS),
|
||||
*dmem_u32(hle, TASK_UCODE_BOOT), *dmem_u32(hle, TASK_UCODE_BOOT_SIZE),
|
||||
*dmem_u32(hle, TASK_UCODE), *dmem_u32(hle, TASK_UCODE_SIZE),
|
||||
*dmem_u32(hle, TASK_UCODE_DATA), *dmem_u32(hle, TASK_UCODE_DATA_SIZE),
|
||||
*dmem_u32(hle, TASK_DRAM_STACK), *dmem_u32(hle, TASK_DRAM_STACK_SIZE),
|
||||
*dmem_u32(hle, TASK_OUTPUT_BUFF), *dmem_u32(hle, TASK_OUTPUT_BUFF_SIZE),
|
||||
*dmem_u32(hle, TASK_DATA_PTR), *dmem_u32(hle, TASK_DATA_SIZE),
|
||||
*dmem_u32(hle, TASK_YIELD_DATA_PTR), *dmem_u32(hle, TASK_YIELD_DATA_SIZE));
|
||||
fclose(f);
|
||||
} else
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - hle.h *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - hle_external.h *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -22,17 +22,25 @@
|
||||
#ifndef HLE_EXTERNAL_H
|
||||
#define HLE_EXTERNAL_H
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define ATTR_FMT(fmtpos, attrpos) __attribute__ ((format (printf, fmtpos, attrpos)))
|
||||
#else
|
||||
#define ATTR_FMT(fmtpos, attrpos)
|
||||
#endif
|
||||
|
||||
/* users of the hle core are expected to define these functions */
|
||||
|
||||
void HleVerboseMessage(void* user_defined, const char *message, ...);
|
||||
void HleErrorMessage(void* user_defined, const char *message, ...);
|
||||
void HleWarnMessage(void* user_defined, const char *message, ...);
|
||||
void HleVerboseMessage(void* user_defined, const char *message, ...) ATTR_FMT(2, 3);
|
||||
void HleInfoMessage(void* user_defined, const char *message, ...) ATTR_FMT(2, 3);
|
||||
void HleErrorMessage(void* user_defined, const char *message, ...) ATTR_FMT(2, 3);
|
||||
void HleWarnMessage(void* user_defined, const char *message, ...) ATTR_FMT(2, 3);
|
||||
|
||||
void HleCheckInterrupts(void* user_defined);
|
||||
void HleProcessDlistList(void* user_defined);
|
||||
void HleProcessAlistList(void* user_defined);
|
||||
void HleProcessRdpList(void* user_defined);
|
||||
void HleShowCFB(void* user_defined);
|
||||
int HleForwardTask(void* user_defined);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - hle_internal.h *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -57,6 +57,9 @@ struct hle_t
|
||||
/* for user convenience, this will be passed to "external" functions */
|
||||
void* user_defined;
|
||||
|
||||
int hle_gfx;
|
||||
int hle_aud;
|
||||
|
||||
/* alist.c */
|
||||
uint8_t alist_buffer[0x1000];
|
||||
|
||||
@ -71,7 +74,20 @@ struct hle_t
|
||||
|
||||
/* mp3.c */
|
||||
uint8_t mp3_buffer[0x1000];
|
||||
|
||||
struct cached_ucodes_t cached_ucodes;
|
||||
};
|
||||
|
||||
/* some mips interface interrupt flags */
|
||||
#define MI_INTR_SP 0x1
|
||||
|
||||
/* some rsp status flags */
|
||||
#define SP_STATUS_HALT 0x1
|
||||
#define SP_STATUS_BROKE 0x2
|
||||
#define SP_STATUS_INTR_ON_BREAK 0x40
|
||||
#define SP_STATUS_TASKDONE 0x200
|
||||
|
||||
void rsp_break(struct hle_t* hle, unsigned int setbits);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - memory.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -26,30 +26,49 @@
|
||||
/* Global functions */
|
||||
void load_u8(uint8_t* dst, const unsigned char* buffer, unsigned address, size_t count)
|
||||
{
|
||||
while (count)
|
||||
{
|
||||
*(dst++) = *u8(buffer, address);
|
||||
address += 1;
|
||||
--count;
|
||||
}
|
||||
while (count != 0) {
|
||||
*(dst++) = *u8(buffer, address);
|
||||
address += 1;
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
void load_u16(uint16_t* dst, const unsigned char* buffer, unsigned address, size_t count)
|
||||
{
|
||||
while (count)
|
||||
{
|
||||
*(dst++) = *u16(buffer, address);
|
||||
address += 2;
|
||||
--count;
|
||||
}
|
||||
while (count != 0) {
|
||||
*(dst++) = *u16(buffer, address);
|
||||
address += 2;
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
void load_u32(uint32_t* dst, const unsigned char* buffer, unsigned address, size_t count)
|
||||
{
|
||||
/* Optimization for uint32_t */
|
||||
memcpy(dst, u32(buffer, address), count * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
void store_u8(unsigned char* buffer, unsigned address, const uint8_t* src, size_t count)
|
||||
{
|
||||
while (count != 0) {
|
||||
*u8(buffer, address) = *(src++);
|
||||
address += 1;
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
void store_u16(unsigned char* buffer, unsigned address, const uint16_t* src, size_t count)
|
||||
{
|
||||
while (count)
|
||||
{
|
||||
*u16(buffer, address) = *(src++);
|
||||
address += 2;
|
||||
--count;
|
||||
}
|
||||
while (count != 0) {
|
||||
*u16(buffer, address) = *(src++);
|
||||
address += 2;
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
void store_u32(unsigned char* buffer, unsigned address, const uint32_t* src, size_t count)
|
||||
{
|
||||
/* Optimization for uint32_t */
|
||||
memcpy(u32(buffer, address), src, count * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - plugin.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* Copyright (C) 2009 Richard Goedeken *
|
||||
* Copyright (C) 2002 Hacktarux *
|
||||
@ -23,62 +23,39 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "hle.h"
|
||||
#include "hle_internal.h"
|
||||
#include "hle_external.h"
|
||||
|
||||
#define M64P_PLUGIN_PROTOTYPES 1
|
||||
#include "m64p_types.h"
|
||||
#include "m64p_common.h"
|
||||
#include "m64p_config.h"
|
||||
#include "m64p_frontend.h"
|
||||
#include "m64p_plugin.h"
|
||||
#include "m64p_types.h"
|
||||
|
||||
#define RSP_HLE_VERSION 0x020000
|
||||
#define CONFIG_API_VERSION 0x020100
|
||||
#define CONFIG_PARAM_VERSION 1.00
|
||||
|
||||
#define RSP_API_VERSION 0x20000
|
||||
#define RSP_HLE_VERSION 0x020509
|
||||
#define RSP_PLUGIN_API_VERSION 0x020000
|
||||
|
||||
/* local variables */
|
||||
static struct hle_t g_hle;
|
||||
static void (*l_CheckInterrupts)(void) = NULL;
|
||||
static void (*l_ProcessDlistList)(void) = NULL;
|
||||
static void (*l_ProcessAlistList)(void) = NULL;
|
||||
static void (*l_ProcessRdpList)(void) = NULL;
|
||||
static void (*l_ShowCFB)(void) = NULL;
|
||||
static void (*l_DebugCallback)(void *, int, const char *) = NULL;
|
||||
static void *l_DebugCallContext = NULL;
|
||||
static int l_PluginInit = 0;
|
||||
|
||||
static unsigned l_PluginInit = 0;
|
||||
|
||||
/* local function */
|
||||
static void DebugMessage(int level, const char *message, va_list args)
|
||||
{
|
||||
char msgbuf[1024];
|
||||
|
||||
vsprintf(msgbuf, message, args);
|
||||
}
|
||||
|
||||
/* Global functions needed by HLE core */
|
||||
void HleVerboseMessage(void* UNUSED(user_defined), const char *message, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
DebugMessage(M64MSG_VERBOSE, message, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void HleWarnMessage(void* UNUSED(user_defined), const char *message, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
DebugMessage(M64MSG_WARNING, message, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
/* DLL-exported functions */
|
||||
m64p_error hlePluginStartup(m64p_dynlib_handle UNUSED(CoreLibHandle), void *Context, void (*DebugCallback)(void *, int, const char *))
|
||||
{
|
||||
l_PluginInit = 1;
|
||||
return M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
m64p_error hlePluginShutdown(void)
|
||||
{
|
||||
l_PluginInit = 0;
|
||||
return M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
m64p_error hlePluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
|
||||
EXPORT m64p_error CALL hlePluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
|
||||
{
|
||||
/* set version info */
|
||||
if (PluginType != NULL)
|
||||
@ -99,48 +76,164 @@ m64p_error hlePluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion,
|
||||
return M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
extern RSP_INFO rsp_info;
|
||||
|
||||
void hleInitiateRSP(RSP_INFO Rsp_Info, unsigned int* UNUSED(CycleCount))
|
||||
/* local function */
|
||||
static void DebugMessage(int level, const char *message, va_list args)
|
||||
{
|
||||
hle_init(&g_hle,
|
||||
rsp_info.RDRAM,
|
||||
rsp_info.DMEM,
|
||||
rsp_info.IMEM,
|
||||
rsp_info.MI_INTR_REG,
|
||||
rsp_info.SP_MEM_ADDR_REG,
|
||||
rsp_info.SP_DRAM_ADDR_REG,
|
||||
rsp_info.SP_RD_LEN_REG,
|
||||
rsp_info.SP_WR_LEN_REG,
|
||||
rsp_info.SP_STATUS_REG,
|
||||
rsp_info.SP_DMA_FULL_REG,
|
||||
rsp_info.SP_DMA_BUSY_REG,
|
||||
rsp_info.SP_PC_REG,
|
||||
rsp_info.SP_SEMAPHORE_REG,
|
||||
rsp_info.DPC_START_REG,
|
||||
rsp_info.DPC_END_REG,
|
||||
rsp_info.DPC_CURRENT_REG,
|
||||
rsp_info.DPC_STATUS_REG,
|
||||
rsp_info.DPC_CLOCK_REG,
|
||||
rsp_info.DPC_BUFBUSY_REG,
|
||||
rsp_info.DPC_PIPEBUSY_REG,
|
||||
rsp_info.DPC_TMEM_REG,
|
||||
NULL);
|
||||
char msgbuf[1024];
|
||||
|
||||
if (l_DebugCallback == NULL)
|
||||
return;
|
||||
|
||||
vsprintf(msgbuf, message, args);
|
||||
|
||||
(*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
|
||||
}
|
||||
|
||||
/* Global functions needed by HLE core */
|
||||
void HleVerboseMessage(void* UNUSED(user_defined), const char *message, ...)
|
||||
{
|
||||
}
|
||||
|
||||
void HleErrorMessage(void* UNUSED(user_defined), const char *message, ...)
|
||||
{
|
||||
}
|
||||
|
||||
void HleWarnMessage(void* UNUSED(user_defined), const char *message, ...)
|
||||
{
|
||||
}
|
||||
|
||||
void HleCheckInterrupts(void* UNUSED(user_defined))
|
||||
{
|
||||
if (l_CheckInterrupts == NULL)
|
||||
return;
|
||||
|
||||
(*l_CheckInterrupts)();
|
||||
}
|
||||
|
||||
void HleProcessDlistList(void* UNUSED(user_defined))
|
||||
{
|
||||
if (l_ProcessDlistList == NULL)
|
||||
return;
|
||||
|
||||
(*l_ProcessDlistList)();
|
||||
}
|
||||
|
||||
void HleProcessAlistList(void* UNUSED(user_defined))
|
||||
{
|
||||
if (l_ProcessAlistList == NULL)
|
||||
return;
|
||||
|
||||
(*l_ProcessAlistList)();
|
||||
}
|
||||
|
||||
void HleProcessRdpList(void* UNUSED(user_defined))
|
||||
{
|
||||
if (l_ProcessRdpList == NULL)
|
||||
return;
|
||||
|
||||
(*l_ProcessRdpList)();
|
||||
}
|
||||
|
||||
void HleShowCFB(void* UNUSED(user_defined))
|
||||
{
|
||||
if (l_ShowCFB == NULL)
|
||||
return;
|
||||
|
||||
(*l_ShowCFB)();
|
||||
}
|
||||
|
||||
int HleForwardTask(void* user_defined)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* DLL-exported functions */
|
||||
EXPORT m64p_error CALL hlePluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
|
||||
void (*DebugCallback)(void *, int, const char *))
|
||||
{
|
||||
if (l_PluginInit)
|
||||
return M64ERR_ALREADY_INIT;
|
||||
|
||||
/* first thing is to set the callback function for debug info */
|
||||
l_DebugCallback = DebugCallback;
|
||||
l_DebugCallContext = Context;
|
||||
|
||||
/* this plugin doesn't use any Core library functions (ex for Configuration), so no need to keep the CoreLibHandle */
|
||||
|
||||
l_PluginInit = 1;
|
||||
return M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
unsigned int hleDoRspCycles(unsigned int Cycles)
|
||||
EXPORT m64p_error CALL hlePluginShutdown(void)
|
||||
{
|
||||
if (!l_PluginInit)
|
||||
hleInitiateRSP(rsp_info, NULL);
|
||||
if (!l_PluginInit)
|
||||
return M64ERR_NOT_INIT;
|
||||
|
||||
hle_execute(&g_hle);
|
||||
return Cycles;
|
||||
/* reset some local variable */
|
||||
l_DebugCallback = NULL;
|
||||
l_DebugCallContext = NULL;
|
||||
|
||||
l_PluginInit = 0;
|
||||
return M64ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void hleRomClosed(void)
|
||||
EXPORT unsigned int CALL hleDoRspCycles(unsigned int Cycles)
|
||||
{
|
||||
/* do nothing */
|
||||
hle_execute(&g_hle);
|
||||
return Cycles;
|
||||
}
|
||||
|
||||
EXPORT void CALL hleInitiateRSP(RSP_INFO Rsp_Info, unsigned int* CycleCount)
|
||||
{
|
||||
hle_init(&g_hle,
|
||||
Rsp_Info.RDRAM,
|
||||
Rsp_Info.DMEM,
|
||||
Rsp_Info.IMEM,
|
||||
Rsp_Info.MI_INTR_REG,
|
||||
Rsp_Info.SP_MEM_ADDR_REG,
|
||||
Rsp_Info.SP_DRAM_ADDR_REG,
|
||||
Rsp_Info.SP_RD_LEN_REG,
|
||||
Rsp_Info.SP_WR_LEN_REG,
|
||||
Rsp_Info.SP_STATUS_REG,
|
||||
Rsp_Info.SP_DMA_FULL_REG,
|
||||
Rsp_Info.SP_DMA_BUSY_REG,
|
||||
Rsp_Info.SP_PC_REG,
|
||||
Rsp_Info.SP_SEMAPHORE_REG,
|
||||
Rsp_Info.DPC_START_REG,
|
||||
Rsp_Info.DPC_END_REG,
|
||||
Rsp_Info.DPC_CURRENT_REG,
|
||||
Rsp_Info.DPC_STATUS_REG,
|
||||
Rsp_Info.DPC_CLOCK_REG,
|
||||
Rsp_Info.DPC_BUFBUSY_REG,
|
||||
Rsp_Info.DPC_PIPEBUSY_REG,
|
||||
Rsp_Info.DPC_TMEM_REG,
|
||||
NULL);
|
||||
|
||||
l_CheckInterrupts = Rsp_Info.CheckInterrupts;
|
||||
l_ProcessDlistList = Rsp_Info.ProcessDlistList;
|
||||
l_ProcessAlistList = Rsp_Info.ProcessAlistList;
|
||||
l_ProcessRdpList = Rsp_Info.ProcessRdpList;
|
||||
l_ShowCFB = Rsp_Info.ShowCFB;
|
||||
|
||||
// Is the DoCommand really needed? It's upstream
|
||||
m64p_rom_header rom_header;
|
||||
CoreDoCommand(M64CMD_ROM_GET_HEADER, sizeof(rom_header), &rom_header);
|
||||
|
||||
g_hle.hle_gfx = 1;
|
||||
g_hle.hle_aud = 0;
|
||||
|
||||
/* notify fallback plugin */
|
||||
/*if (l_InitiateRSP) {
|
||||
l_InitiateRSP(Rsp_Info, CycleCount);
|
||||
}*/
|
||||
}
|
||||
|
||||
EXPORT void CALL hleRomClosed(void)
|
||||
{
|
||||
g_hle.cached_ucodes.count = 0;
|
||||
|
||||
/* notify fallback plugin */
|
||||
/*if (l_RomClosed) {
|
||||
l_RomClosed();
|
||||
}*/
|
||||
}
|
||||
|
354
mupen64plus-rsp-hle/src/hvqm.c
Normal file
354
mupen64plus-rsp-hle/src/hvqm.c
Normal file
@ -0,0 +1,354 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - hvqm.c *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2020 Gilles Siberlin *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hle_external.h"
|
||||
#include "hle_internal.h"
|
||||
#include "memory.h"
|
||||
|
||||
/* Nest size */
|
||||
#define HVQM2_NESTSIZE_L 70 /* Number of elements on long side */
|
||||
#define HVQM2_NESTSIZE_S 38 /* Number of elements on short side */
|
||||
#define HVQM2_NESTSIZE (HVQM2_NESTSIZE_L * HVQM2_NESTSIZE_S)
|
||||
|
||||
struct HVQM2Block {
|
||||
uint8_t nbase;
|
||||
uint8_t dc;
|
||||
uint8_t dc_l;
|
||||
uint8_t dc_r;
|
||||
uint8_t dc_u;
|
||||
uint8_t dc_d;
|
||||
};
|
||||
|
||||
struct HVQM2Basis {
|
||||
uint8_t sx;
|
||||
uint8_t sy;
|
||||
int16_t scale;
|
||||
uint16_t offset;
|
||||
uint16_t lineskip;
|
||||
};
|
||||
|
||||
struct HVQM2Arg {
|
||||
uint32_t info;
|
||||
uint32_t buf;
|
||||
uint16_t buf_width;
|
||||
uint8_t chroma_step_h;
|
||||
uint8_t chroma_step_v;
|
||||
uint16_t hmcus;
|
||||
uint16_t vmcus;
|
||||
uint8_t alpha;
|
||||
uint32_t nest;
|
||||
};
|
||||
|
||||
struct RGBA {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t a;
|
||||
};
|
||||
|
||||
static struct HVQM2Arg arg;
|
||||
|
||||
static const int16_t constant[5][16] = {
|
||||
{0x0006,0x0008,0x0008,0x0006,0x0008,0x000A,0x000A,0x0008,0x0008,0x000A,0x000A,0x0008,0x0006,0x0008,0x0008,0x0006},
|
||||
{0x0002,0x0000,0xFFFF,0xFFFF,0x0002,0x0000,0xFFFF,0xFFFF,0x0002,0x0000,0xFFFF,0xFFFF,0x0002,0x0000,0xFFFF,0xFFFF},
|
||||
{0xFFFF,0xFFFF,0x0000,0x0002,0xFFFF,0xFFFF,0x0000,0x0002,0xFFFF,0xFFFF,0x0000,0x0002,0xFFFF,0xFFFF,0x0000,0x0002},
|
||||
{0x0002,0x0002,0x0002,0x0002,0x0000,0x0000,0x0000,0x0000,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF},
|
||||
{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x0000,0x0000,0x0000,0x0000,0x0002,0x0002,0x0002,0x0002}
|
||||
};
|
||||
|
||||
static int process_info(struct hle_t* hle, uint8_t* base, int16_t* out)
|
||||
{
|
||||
struct HVQM2Block block;
|
||||
uint8_t nbase = *base;
|
||||
|
||||
dram_load_u8(hle, (uint8_t*)&block, arg.info, sizeof(struct HVQM2Block));
|
||||
arg.info += 8;
|
||||
|
||||
*base = block.nbase & 0x7;
|
||||
|
||||
if ((block.nbase & nbase) != 0)
|
||||
return 0;
|
||||
|
||||
if (block.nbase == 0)
|
||||
{
|
||||
//LABEL8
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
out[i] = constant[0][i] * block.dc;
|
||||
out[i] += constant[1][i] * block.dc_l;
|
||||
out[i] += constant[2][i] * block.dc_r;
|
||||
out[i] += constant[3][i] * block.dc_u;
|
||||
out[i] += constant[4][i] * block.dc_d;
|
||||
out[i] += 4;
|
||||
out[i] >>= 3;
|
||||
}
|
||||
}
|
||||
else if ((block.nbase & 0xf) == 0)
|
||||
{
|
||||
//LABEL7
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
out[i] = *dram_u8(hle, arg.info);
|
||||
arg.info++;
|
||||
}
|
||||
}
|
||||
else if (*base == 0)
|
||||
{
|
||||
//LABEL6
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
out[i] = *(int8_t*)dram_u8(hle, arg.info) + block.dc;
|
||||
arg.info++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//LABEL5
|
||||
struct HVQM2Basis basis;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
out[i] = block.dc;
|
||||
|
||||
for (; *base != 0; (*base)--)
|
||||
{
|
||||
basis.sx = *dram_u8(hle, arg.info);
|
||||
arg.info++;
|
||||
basis.sy = *dram_u8(hle, arg.info);
|
||||
arg.info++;
|
||||
basis.scale = *dram_u16(hle, arg.info);
|
||||
arg.info += 2;
|
||||
basis.offset = *dram_u16(hle, arg.info);
|
||||
arg.info += 2;
|
||||
basis.lineskip = *dram_u16(hle, arg.info);
|
||||
arg.info += 2;
|
||||
|
||||
int16_t vec[16];
|
||||
uint32_t addr = arg.nest + basis.offset;
|
||||
int shift = (basis.sx != 0) ? 1 : 0;
|
||||
|
||||
//LABEL9
|
||||
//LABEL10
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
vec[i] = *dram_u8(hle, addr);
|
||||
vec[i + 1] = *dram_u8(hle, addr + (1 << shift));
|
||||
vec[i + 2] = *dram_u8(hle, addr + (2 << shift));
|
||||
vec[i + 3] = *dram_u8(hle, addr + (3 << shift));
|
||||
addr += basis.lineskip;
|
||||
}
|
||||
|
||||
//LABEL11
|
||||
int16_t sum = 0x8;
|
||||
for (int i = 0; i < 16; i++)
|
||||
sum += vec[i];
|
||||
|
||||
sum >>= 4;
|
||||
|
||||
int16_t max = 0;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
vec[i] -= sum;
|
||||
max = (abs(vec[i]) > max) ? abs(vec[i]) : max;
|
||||
}
|
||||
|
||||
double dmax = 0.0;
|
||||
if (max > 0)
|
||||
dmax = (double)(basis.scale << 2) / (double)max;
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
out[i] += (vec[i] < 0) ? (int16_t)((double)vec[i] * dmax - 0.5) : (int16_t)((double)vec[i] * dmax + 0.5);
|
||||
|
||||
block.nbase &= 8;
|
||||
}
|
||||
|
||||
assert(block.nbase == 0);
|
||||
//if(block.nbase != 0)
|
||||
// LABEL6
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define SATURATE8(x) ((unsigned int) x <= 255 ? x : (x < 0 ? 0: 255))
|
||||
static struct RGBA YCbCr_to_RGBA(int16_t Y, int16_t Cb, int16_t Cr, uint8_t alpha)
|
||||
{
|
||||
struct RGBA color;
|
||||
|
||||
//Format S10.6
|
||||
int r = (int)(((double)Y + 0.5) + (1.765625 * (double)(Cr - 128)));
|
||||
int g = (int)(((double)Y + 0.5) - (0.34375 * (double)(Cr - 128)) - (0.71875 * (double)(Cb - 128)));
|
||||
int b = (int)(((double)Y + 0.5) + (1.40625 * (double)(Cb - 128)));
|
||||
|
||||
color.r = SATURATE8(r);
|
||||
color.g = SATURATE8(g);
|
||||
color.b = SATURATE8(b);
|
||||
color.a = alpha;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void store_rgba5551(struct hle_t* hle, struct RGBA color, uint32_t * addr)
|
||||
{
|
||||
uint16_t pixel = ((color.b >> 3) << 11) | ((color.g >> 3) << 6) | ((color.r >> 3) << 1) | (color.a & 1);
|
||||
dram_store_u16(hle, &pixel, *addr, 1);
|
||||
*addr += 2;
|
||||
}
|
||||
|
||||
void store_rgba8888(struct hle_t* hle, struct RGBA color, uint32_t * addr)
|
||||
{
|
||||
uint32_t pixel = (color.b << 24) | (color.g << 16) | (color.r << 8) | color.a;
|
||||
dram_store_u32(hle, &pixel, *addr, 1);
|
||||
*addr += 4;
|
||||
}
|
||||
|
||||
typedef void(*store_pixel_t)(struct hle_t* hle, struct RGBA color, uint32_t * addr);
|
||||
|
||||
static void hvqm2_decode(struct hle_t* hle, int is32)
|
||||
{
|
||||
//uint32_t uc_data_ptr = *dmem_u32(hle, TASK_UCODE_DATA);
|
||||
uint32_t data_ptr = *dmem_u32(hle, TASK_DATA_PTR);
|
||||
|
||||
assert((*dmem_u32(hle, TASK_FLAGS) & 0x1) == 0);
|
||||
|
||||
/* Fill HVQM2Arg struct */
|
||||
arg.info = *dram_u32(hle, data_ptr);
|
||||
data_ptr += 4;
|
||||
arg.buf = *dram_u32(hle, data_ptr);
|
||||
data_ptr += 4;
|
||||
arg.buf_width = *dram_u16(hle, data_ptr);
|
||||
data_ptr += 2;
|
||||
arg.chroma_step_h = *dram_u8(hle, data_ptr);
|
||||
data_ptr++;
|
||||
arg.chroma_step_v = *dram_u8(hle, data_ptr);
|
||||
data_ptr++;
|
||||
arg.hmcus = *dram_u16(hle, data_ptr);
|
||||
data_ptr += 2;
|
||||
arg.vmcus = *dram_u16(hle, data_ptr);
|
||||
data_ptr += 2;
|
||||
arg.alpha = *dram_u8(hle, data_ptr);
|
||||
arg.nest = data_ptr + 1;
|
||||
|
||||
assert(arg.chroma_step_h == 2);
|
||||
assert((arg.chroma_step_v == 1) || (arg.chroma_step_v == 2));
|
||||
assert((*hle->sp_status & 0x80) == 0); //SP_STATUS_YIELD
|
||||
|
||||
int length, skip;
|
||||
store_pixel_t store_pixel;
|
||||
|
||||
if (is32)
|
||||
{
|
||||
length = 0x20;
|
||||
skip = arg.buf_width << 2;
|
||||
arg.buf_width <<= 4;
|
||||
store_pixel = &store_rgba8888;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 0x10;
|
||||
skip = arg.buf_width << 1;
|
||||
arg.buf_width <<= 3;
|
||||
store_pixel = &store_rgba5551;
|
||||
}
|
||||
|
||||
if (arg.chroma_step_v == 2)
|
||||
arg.buf_width += arg.buf_width;
|
||||
|
||||
for (int i = arg.vmcus; i != 0; i--)
|
||||
{
|
||||
uint32_t out;
|
||||
int j;
|
||||
|
||||
for (j = arg.hmcus, out = arg.buf; j != 0; j--, out += length)
|
||||
{
|
||||
uint8_t base = 0x80;
|
||||
int16_t Cb[16], Cr[16], Y1[32], Y2[32];
|
||||
int16_t* pCb = Cb;
|
||||
int16_t* pCr = Cr;
|
||||
int16_t* pY1 = Y1;
|
||||
int16_t* pY2 = Y2;
|
||||
|
||||
if (arg.chroma_step_v == 2)
|
||||
{
|
||||
if (process_info(hle, &base, pY1) == 0)
|
||||
continue;
|
||||
if (process_info(hle, &base, pY2) == 0)
|
||||
continue;
|
||||
|
||||
pY1 = &Y1[16];
|
||||
pY2 = &Y2[16];
|
||||
}
|
||||
|
||||
if (process_info(hle, &base, pY1) == 0)
|
||||
continue;
|
||||
if (process_info(hle, &base, pY2) == 0)
|
||||
continue;
|
||||
if (process_info(hle, &base, Cr) == 0)
|
||||
continue;
|
||||
if (process_info(hle, &base, Cb) == 0)
|
||||
continue;
|
||||
|
||||
pY1 = Y1;
|
||||
pY2 = Y2;
|
||||
|
||||
uint32_t out_buf = out;
|
||||
for (int k = 0; k < 4; k++)
|
||||
{
|
||||
for (int m = 0; m < arg.chroma_step_v; m++)
|
||||
{
|
||||
uint32_t addr = out_buf;
|
||||
for (int l = 0; l < 4; l++)
|
||||
{
|
||||
struct RGBA color = YCbCr_to_RGBA(pY1[l], pCb[l >> 1], pCr[l >> 1], arg.alpha);
|
||||
store_pixel(hle, color, &addr);
|
||||
}
|
||||
for (int l = 0; l < 4; l++)
|
||||
{
|
||||
struct RGBA color = YCbCr_to_RGBA(pY2[l], pCb[(l + 4) >> 1], pCr[(l + 4) >> 1], arg.alpha);
|
||||
store_pixel(hle, color, &addr);
|
||||
}
|
||||
out_buf += skip;
|
||||
pY1 += 4;
|
||||
pY2 += 4;
|
||||
}
|
||||
pCr += 4;
|
||||
pCb += 4;
|
||||
}
|
||||
}
|
||||
arg.buf += arg.buf_width;
|
||||
}
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void hvqm2_decode_sp1_task(struct hle_t* hle)
|
||||
{
|
||||
hvqm2_decode(hle, 0);
|
||||
}
|
||||
|
||||
void hvqm2_decode_sp2_task(struct hle_t* hle)
|
||||
{
|
||||
hvqm2_decode(hle, 1);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - jpeg.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2012 Bobby Smiles *
|
||||
* Copyright (C) 2009 Richard Goedeken *
|
||||
* Copyright (C) 2002 Hacktarux *
|
||||
@ -141,6 +141,7 @@ static const float IDCT_K[10] = {
|
||||
void jpeg_decode_PS0(struct hle_t* hle)
|
||||
{
|
||||
jpeg_decode_std(hle, "PS0", RescaleYSubBlock, RescaleUVSubBlock, EmitYUVTileLine);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
@ -150,6 +151,7 @@ void jpeg_decode_PS0(struct hle_t* hle)
|
||||
void jpeg_decode_PS(struct hle_t* hle)
|
||||
{
|
||||
jpeg_decode_std(hle, "PS", NULL, NULL, EmitRGBATileLine);
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
@ -190,6 +192,7 @@ void jpeg_decode_OB(struct hle_t* hle)
|
||||
|
||||
address += (2 * 6 * SUBBLOCK_SIZE);
|
||||
}
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
|
||||
@ -472,7 +475,7 @@ static void ReorderSubBlock(int16_t *dst, const int16_t *src, const unsigned int
|
||||
unsigned int i;
|
||||
|
||||
/* source and destination sublocks cannot overlap */
|
||||
assert(abs(dst - src) > SUBBLOCK_SIZE);
|
||||
assert(labs(dst - src) > SUBBLOCK_SIZE);
|
||||
|
||||
for (i = 0; i < SUBBLOCK_SIZE; ++i)
|
||||
dst[i] = src[table[i]];
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - memory.h *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -25,13 +25,11 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <retro_inline.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "hle_internal.h"
|
||||
|
||||
#ifdef MSB_FIRST
|
||||
#ifdef M64P_BIG_ENDIAN
|
||||
#define S 0
|
||||
#define S16 0
|
||||
#define S8 0
|
||||
@ -60,47 +58,128 @@ enum {
|
||||
TASK_YIELD_DATA_SIZE = 0xffc
|
||||
};
|
||||
|
||||
static INLINE unsigned int align(unsigned int x, unsigned amount)
|
||||
static inline unsigned int align(unsigned int x, unsigned amount)
|
||||
{
|
||||
--amount;
|
||||
return (x + amount) & ~amount;
|
||||
--amount;
|
||||
return (x + amount) & ~amount;
|
||||
}
|
||||
|
||||
#define u8(buffer, address) ((uint8_t*)((buffer) + ((address) ^ S8)))
|
||||
#define u16(buffer, address) ((uint16_t*)((buffer) + ((address) ^ S16)))
|
||||
#define u32(buffer, address) ((uint32_t*)((buffer) + (address)))
|
||||
static inline uint8_t* u8(const unsigned char* buffer, unsigned address)
|
||||
{
|
||||
return (uint8_t*)(buffer + (address ^ S8));
|
||||
}
|
||||
|
||||
static inline uint16_t* u16(const unsigned char* buffer, unsigned address)
|
||||
{
|
||||
assert((address & 1) == 0);
|
||||
return (uint16_t*)(buffer + (address ^ S16));
|
||||
}
|
||||
|
||||
static inline uint32_t* u32(const unsigned char* buffer, unsigned address)
|
||||
{
|
||||
assert((address & 3) == 0);
|
||||
return (uint32_t*)(buffer + address);
|
||||
}
|
||||
|
||||
void load_u8 (uint8_t* dst, const unsigned char* buffer, unsigned address, size_t count);
|
||||
void load_u16(uint16_t* dst, const unsigned char* buffer, unsigned address, size_t count);
|
||||
|
||||
#define load_u32(dst, buffer, address, count) memcpy((dst), u32((buffer), (address)), (count) * sizeof(uint32_t))
|
||||
|
||||
void load_u32(uint32_t* dst, const unsigned char* buffer, unsigned address, size_t count);
|
||||
void store_u8 (unsigned char* buffer, unsigned address, const uint8_t* src, size_t count);
|
||||
void store_u16(unsigned char* buffer, unsigned address, const uint16_t* src, size_t count);
|
||||
void store_u32(unsigned char* buffer, unsigned address, const uint32_t* src, size_t count);
|
||||
|
||||
#define store_u32(buffer, address, src, count) memcpy(u32((buffer), (address)), (src), (count) * sizeof(uint32_t))
|
||||
|
||||
/* convenient functions for DMEM access */
|
||||
#define dmem_u8(hle, address) (u8((hle)->dmem, (address) & 0xFFF)
|
||||
#define dmem_u16(hle, address) (u16((hle)->dmem, (address) & 0xfff);
|
||||
#define dmem_u32(hle, address) (u32((hle)->dmem, (address) & 0xfff))
|
||||
#define dmem_load_u8(hle, dst, address, count) load_u8((dst), (hle)->dmem, (address) & 0xfff, (count))
|
||||
#define dmem_load_u16(hle, dst, address, count) load_u16((dst), (hle)->dmem, (address) & 0xfff, (count))
|
||||
#define dmem_load_u32(hle, dst, address, count) load_u32((dst), (hle)->dmem, (address) & 0xfff, (count))
|
||||
#define dmem_store_u16(hle, src, address, count) store_u16((hle)->dmem, (address) & 0xfff, (src), (count))
|
||||
#define dmem_store_u32(hle, src, address, count) store_u32((hle)->dmem, (address) & 0xfff, (src), (count))
|
||||
static inline uint8_t* dmem_u8(struct hle_t* hle, uint16_t address)
|
||||
{
|
||||
return u8(hle->dmem, address & 0xfff);
|
||||
}
|
||||
|
||||
static inline uint16_t* dmem_u16(struct hle_t* hle, uint16_t address)
|
||||
{
|
||||
return u16(hle->dmem, address & 0xfff);
|
||||
}
|
||||
|
||||
static inline uint32_t* dmem_u32(struct hle_t* hle, uint16_t address)
|
||||
{
|
||||
return u32(hle->dmem, address & 0xfff);
|
||||
}
|
||||
|
||||
static inline void dmem_load_u8(struct hle_t* hle, uint8_t* dst, uint16_t address, size_t count)
|
||||
{
|
||||
load_u8(dst, hle->dmem, address & 0xfff, count);
|
||||
}
|
||||
|
||||
static inline void dmem_load_u16(struct hle_t* hle, uint16_t* dst, uint16_t address, size_t count)
|
||||
{
|
||||
load_u16(dst, hle->dmem, address & 0xfff, count);
|
||||
}
|
||||
|
||||
static inline void dmem_load_u32(struct hle_t* hle, uint32_t* dst, uint16_t address, size_t count)
|
||||
{
|
||||
load_u32(dst, hle->dmem, address & 0xfff, count);
|
||||
}
|
||||
|
||||
static inline void dmem_store_u8(struct hle_t* hle, const uint8_t* src, uint16_t address, size_t count)
|
||||
{
|
||||
store_u8(hle->dmem, address & 0xfff, src, count);
|
||||
}
|
||||
|
||||
static inline void dmem_store_u16(struct hle_t* hle, const uint16_t* src, uint16_t address, size_t count)
|
||||
{
|
||||
store_u16(hle->dmem, address & 0xfff, src, count);
|
||||
}
|
||||
|
||||
static inline void dmem_store_u32(struct hle_t* hle, const uint32_t* src, uint16_t address, size_t count)
|
||||
{
|
||||
store_u32(hle->dmem, address & 0xfff, src, count);
|
||||
}
|
||||
|
||||
/* convenient functions DRAM access */
|
||||
#define dram_u8(hle, address) (u8((hle)->dram, (address) & 0xffffff))
|
||||
#define dram_u16(hle, address) (u16((hle)->dram, (address) & 0xffffff))
|
||||
#define dram_u32(hle, address) (u32((hle)->dram, (address) & 0xffffff))
|
||||
static inline uint8_t* dram_u8(struct hle_t* hle, uint32_t address)
|
||||
{
|
||||
return u8(hle->dram, address & 0xffffff);
|
||||
}
|
||||
|
||||
#define dram_load_u8(hle, dst, address, count) load_u8((dst), (hle)->dram, (address) & 0xffffff, (count))
|
||||
#define dram_load_u16(hle, dst, address, count) load_u16((dst), (hle)->dram, (address) & 0xffffff, (count))
|
||||
#define dram_load_u32(hle, dst, address, count) load_u32((dst), (hle)->dram, (address) & 0xffffff, (count))
|
||||
#define dram_store_u8(hle, src, address, count) store_u8((hle)->dram, (address) & 0xffffff, (src), (count))
|
||||
#define dram_store_u16(hle, src, address, count) store_u16((hle)->dram, (address) & 0xffffff, (src), (count))
|
||||
#define dram_store_u32(hle, src, address, count) store_u32((hle)->dram, (address) & 0xffffff, (src), (count))
|
||||
static inline uint16_t* dram_u16(struct hle_t* hle, uint32_t address)
|
||||
{
|
||||
return u16(hle->dram, address & 0xffffff);
|
||||
}
|
||||
|
||||
static inline uint32_t* dram_u32(struct hle_t* hle, uint32_t address)
|
||||
{
|
||||
return u32(hle->dram, address & 0xffffff);
|
||||
}
|
||||
|
||||
static inline void dram_load_u8(struct hle_t* hle, uint8_t* dst, uint32_t address, size_t count)
|
||||
{
|
||||
load_u8(dst, hle->dram, address & 0xffffff, count);
|
||||
}
|
||||
|
||||
static inline void dram_load_u16(struct hle_t* hle, uint16_t* dst, uint32_t address, size_t count)
|
||||
{
|
||||
load_u16(dst, hle->dram, address & 0xffffff, count);
|
||||
}
|
||||
|
||||
static inline void dram_load_u32(struct hle_t* hle, uint32_t* dst, uint32_t address, size_t count)
|
||||
{
|
||||
load_u32(dst, hle->dram, address & 0xffffff, count);
|
||||
}
|
||||
|
||||
static inline void dram_store_u8(struct hle_t* hle, const uint8_t* src, uint32_t address, size_t count)
|
||||
{
|
||||
store_u8(hle->dram, address & 0xffffff, src, count);
|
||||
}
|
||||
|
||||
static inline void dram_store_u16(struct hle_t* hle, const uint16_t* src, uint32_t address, size_t count)
|
||||
{
|
||||
store_u16(hle->dram, address & 0xffffff, src, count);
|
||||
}
|
||||
|
||||
static inline void dram_store_u32(struct hle_t* hle, const uint32_t* src, uint32_t address, size_t count)
|
||||
{
|
||||
store_u32(hle->dram, address & 0xffffff, src, count);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - mp3.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* Copyright (C) 2009 Richard Goedeken *
|
||||
* Copyright (C) 2002 Hacktarux *
|
||||
@ -21,8 +21,8 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "arithmetics.h"
|
||||
#include "hle_internal.h"
|
||||
@ -205,6 +205,7 @@ static void MP3AB0(int32_t* v)
|
||||
|
||||
void mp3_task(struct hle_t* hle, unsigned int index, uint32_t address)
|
||||
{
|
||||
uint32_t inPtr, outPtr;
|
||||
uint32_t t6;/* = 0x08A0; - I think these are temporary storage buffers */
|
||||
uint32_t t5;/* = 0x0AC0; */
|
||||
uint32_t t4;/* = (w1 & 0x1E); */
|
||||
@ -226,32 +227,29 @@ void mp3_task(struct hle_t* hle, unsigned int index, uint32_t address)
|
||||
/* This must be a header byte or whatnot */
|
||||
readPtr += 8;
|
||||
|
||||
for (cnt = 0; cnt < 0x480; cnt += 0x180)
|
||||
{
|
||||
uint32_t inPtr, outPtr;
|
||||
|
||||
/* DMA: 0xCF0 <- RDRAM[s5] : 0x180 */
|
||||
memcpy(hle->mp3_buffer + 0xCF0, hle->dram + readPtr, 0x180);
|
||||
inPtr = 0xCF0; /* s7 */
|
||||
outPtr = 0xE70; /* s3 */
|
||||
/* --------------- Inner Loop Start -------------------- */
|
||||
for (cnt2 = 0; cnt2 < 0x180; cnt2 += 0x40) {
|
||||
t6 &= 0xFFE0;
|
||||
t5 &= 0xFFE0;
|
||||
t6 |= t4;
|
||||
t5 |= t4;
|
||||
InnerLoop(hle, outPtr, inPtr, t6, t5, t4);
|
||||
t4 = (t4 - 2) & 0x1E;
|
||||
tmp = t6;
|
||||
t6 = t5;
|
||||
t5 = tmp;
|
||||
inPtr += 0x40;
|
||||
outPtr += 0x40;
|
||||
}
|
||||
/* --------------- Inner Loop End -------------------- */
|
||||
memcpy(hle->dram + writePtr, hle->mp3_buffer + 0xe70, 0x180);
|
||||
writePtr += 0x180;
|
||||
readPtr += 0x180;
|
||||
for (cnt = 0; cnt < 0x480; cnt += 0x180) {
|
||||
/* DMA: 0xCF0 <- RDRAM[s5] : 0x180 */
|
||||
memcpy(hle->mp3_buffer + 0xCF0, hle->dram + readPtr, 0x180);
|
||||
inPtr = 0xCF0; /* s7 */
|
||||
outPtr = 0xE70; /* s3 */
|
||||
/* --------------- Inner Loop Start -------------------- */
|
||||
for (cnt2 = 0; cnt2 < 0x180; cnt2 += 0x40) {
|
||||
t6 &= 0xFFE0;
|
||||
t5 &= 0xFFE0;
|
||||
t6 |= t4;
|
||||
t5 |= t4;
|
||||
InnerLoop(hle, outPtr, inPtr, t6, t5, t4);
|
||||
t4 = (t4 - 2) & 0x1E;
|
||||
tmp = t6;
|
||||
t6 = t5;
|
||||
t5 = tmp;
|
||||
inPtr += 0x40;
|
||||
outPtr += 0x40;
|
||||
}
|
||||
/* --------------- Inner Loop End -------------------- */
|
||||
memcpy(hle->dram + writePtr, hle->mp3_buffer + 0xe70, 0x180);
|
||||
writePtr += 0x180;
|
||||
readPtr += 0x180;
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,6 +280,7 @@ static void InnerLoop(struct hle_t* hle,
|
||||
int tmp;
|
||||
int32_t hi0;
|
||||
int32_t hi1;
|
||||
int32_t vt;
|
||||
int32_t v[32];
|
||||
|
||||
v[0] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x00 ^ S16));
|
||||
@ -662,25 +661,24 @@ static void InnerLoop(struct hle_t* hle,
|
||||
|
||||
hi0 = (int)hi0 >> 0x10;
|
||||
hi1 = (int)hi1 >> 0x10;
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
/* v0 */
|
||||
int32_t vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x40)^S16)) * hi0);
|
||||
*(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x40)^S16)) = clamp_s16(vt);
|
||||
for (i = 0; i < 8; i++) {
|
||||
/* v0 */
|
||||
vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x40)^S16)) * hi0);
|
||||
*(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x40)^S16)) = clamp_s16(vt);
|
||||
|
||||
/* v17 */
|
||||
vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x30)^S16)) * hi0);
|
||||
*(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x30)^S16)) = clamp_s16(vt);
|
||||
/* v17 */
|
||||
vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x30)^S16)) * hi0);
|
||||
*(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x30)^S16)) = clamp_s16(vt);
|
||||
|
||||
/* v2 */
|
||||
vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x1E)^S16)) * hi1);
|
||||
*(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x1E)^S16)) = clamp_s16(vt);
|
||||
/* v2 */
|
||||
vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x1E)^S16)) * hi1);
|
||||
*(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x1E)^S16)) = clamp_s16(vt);
|
||||
|
||||
/* v4 */
|
||||
vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0xE)^S16)) * hi1);
|
||||
*(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0xE)^S16)) = clamp_s16(vt);
|
||||
/* v4 */
|
||||
vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0xE)^S16)) * hi1);
|
||||
*(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0xE)^S16)) = clamp_s16(vt);
|
||||
|
||||
tmp += 2;
|
||||
tmp += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - musyx.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2013 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -19,15 +19,14 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <boolean.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include "arithmetics.h"
|
||||
#include "audio.h"
|
||||
#include "common.h"
|
||||
#include "hle_external.h"
|
||||
#include "hle_internal.h"
|
||||
#include "memory.h"
|
||||
@ -175,6 +174,7 @@ static void mix_sfx_with_main_subframes_v1(musyx_t *musyx, const int16_t *subfra
|
||||
static void mix_sfx_with_main_subframes_v2(musyx_t *musyx, const int16_t *subframe,
|
||||
const uint16_t* gains);
|
||||
|
||||
static void mix_samples(int16_t *y, int16_t x, int16_t hgain);
|
||||
static void mix_subframes(int16_t *y, const int16_t *x, int16_t hgain);
|
||||
static void mix_fir4(int16_t *y, const int16_t *x, int16_t hgain, const int16_t *hcoeffs);
|
||||
|
||||
@ -256,6 +256,8 @@ void musyx_v1_task(struct hle_t* hle)
|
||||
dram_store_u16(hle, (uint16_t *)musyx.cc0, state_ptr + STATE_CC0, SUBFRAME_SIZE);
|
||||
dram_store_u16(hle, (uint16_t *)musyx.subframe_740_last4, state_ptr + STATE_740_LAST4_V1,
|
||||
4);
|
||||
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
@ -333,6 +335,8 @@ void musyx_v2_task(struct hle_t* hle)
|
||||
|
||||
sfd_ptr += SFD2_VOICES + MAX_VOICES * VOICE_SIZE;
|
||||
}
|
||||
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
|
||||
@ -375,31 +379,27 @@ static void update_base_vol(struct hle_t* hle, int32_t *base_vol,
|
||||
base_vol[0], base_vol[1], base_vol[2], base_vol[3]);
|
||||
|
||||
/* optim: skip voices contributions entirely if voice_mask is empty */
|
||||
if (voice_mask != 0)
|
||||
{
|
||||
for (i = 0, mask = 1; i < MAX_VOICES;
|
||||
++i, mask <<= 1, last_sample_ptr += 8)
|
||||
{
|
||||
if ((voice_mask & mask) == 0)
|
||||
continue;
|
||||
if (voice_mask != 0) {
|
||||
for (i = 0, mask = 1; i < MAX_VOICES;
|
||||
++i, mask <<= 1, last_sample_ptr += 8) {
|
||||
if ((voice_mask & mask) == 0)
|
||||
continue;
|
||||
|
||||
for (k = 0; k < 4; ++k)
|
||||
base_vol[k] += (int16_t)*dram_u16(hle, last_sample_ptr + k * 2);
|
||||
}
|
||||
for (k = 0; k < 4; ++k)
|
||||
base_vol[k] += (int16_t)*dram_u16(hle, last_sample_ptr + k * 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* optim: skip contributions entirely if mask_15 is empty */
|
||||
if (mask_15 != 0)
|
||||
{
|
||||
for(i = 0, mask = 1; i < 4;
|
||||
++i, mask <<= 1, ptr_24 += 8)
|
||||
{
|
||||
if ((mask_15 & mask) == 0)
|
||||
continue;
|
||||
if (mask_15 != 0) {
|
||||
for(i = 0, mask = 1; i < 4;
|
||||
++i, mask <<= 1, ptr_24 += 8) {
|
||||
if ((mask_15 & mask) == 0)
|
||||
continue;
|
||||
|
||||
for(k = 0; k < 4; ++k)
|
||||
base_vol[k] += (int16_t)*dram_u16(hle, ptr_24 + k * 2);
|
||||
}
|
||||
for(k = 0; k < 4; ++k)
|
||||
base_vol[k] += (int16_t)*dram_u16(hle, ptr_24 + k * 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* apply 3% decay */
|
||||
@ -448,10 +448,10 @@ static void init_subframes_v2(musyx_t *musyx)
|
||||
subframes[2] = musyx->cc0;
|
||||
subframes[3] = musyx->e50;
|
||||
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i)
|
||||
{
|
||||
for(k = 0; k < 4; ++k)
|
||||
*(subframes[k]++) = values[k];
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i) {
|
||||
|
||||
for(k = 0; k < 4; ++k)
|
||||
*(subframes[k]++) = values[k];
|
||||
}
|
||||
}
|
||||
|
||||
@ -460,45 +460,40 @@ static uint32_t voice_stage(struct hle_t* hle, musyx_t *musyx,
|
||||
uint32_t voice_ptr, uint32_t last_sample_ptr)
|
||||
{
|
||||
uint32_t output_ptr;
|
||||
int i = 0;
|
||||
|
||||
/* voice stage can be skipped if first voice has no samples */
|
||||
if (*dram_u16(hle, voice_ptr + VOICE_CATSRC_0 + CATSRC_SIZE1) == 0)
|
||||
{
|
||||
HleVerboseMessage(hle->user_defined, "Skipping Voice stage");
|
||||
output_ptr = *dram_u32(hle, voice_ptr + VOICE_INTERLEAVED_PTR);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned i = 0;
|
||||
if (*dram_u16(hle, voice_ptr + VOICE_CATSRC_0 + CATSRC_SIZE1) == 0) {
|
||||
HleVerboseMessage(hle->user_defined, "Skipping Voice stage");
|
||||
output_ptr = *dram_u32(hle, voice_ptr + VOICE_INTERLEAVED_PTR);
|
||||
} else {
|
||||
/* otherwise process voices until a non null output_ptr is encountered */
|
||||
for (;;) {
|
||||
/* load voice samples (PCM16 or APDCM) */
|
||||
int16_t samples[SAMPLE_BUFFER_SIZE];
|
||||
unsigned segbase;
|
||||
unsigned offset;
|
||||
|
||||
/* otherwise process voices until a non null output_ptr is encountered */
|
||||
for (;;)
|
||||
{
|
||||
/* load voice samples (PCM16 or APDCM) */
|
||||
int16_t samples[SAMPLE_BUFFER_SIZE];
|
||||
unsigned segbase;
|
||||
unsigned offset;
|
||||
HleVerboseMessage(hle->user_defined, "Processing Voice #%d", i);
|
||||
|
||||
HleVerboseMessage(hle->user_defined, "Processing Voice #%d", i);
|
||||
if (*dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES) == 0)
|
||||
load_samples_PCM16(hle, voice_ptr, samples, &segbase, &offset);
|
||||
else
|
||||
load_samples_ADPCM(hle, voice_ptr, samples, &segbase, &offset);
|
||||
|
||||
if (*dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES) == 0)
|
||||
load_samples_PCM16(hle, voice_ptr, samples, &segbase, &offset);
|
||||
else
|
||||
load_samples_ADPCM(hle, voice_ptr, samples, &segbase, &offset);
|
||||
/* mix them with each internal subframes */
|
||||
mix_voice_samples(hle, musyx, voice_ptr, samples, segbase, offset,
|
||||
last_sample_ptr + i * 8);
|
||||
|
||||
/* mix them with each internal subframes */
|
||||
mix_voice_samples(hle, musyx, voice_ptr, samples, segbase, offset,
|
||||
last_sample_ptr + i * 8);
|
||||
/* check break condition */
|
||||
output_ptr = *dram_u32(hle, voice_ptr + VOICE_INTERLEAVED_PTR);
|
||||
if (output_ptr != 0)
|
||||
break;
|
||||
|
||||
/* check break condition */
|
||||
output_ptr = *dram_u32(hle, voice_ptr + VOICE_INTERLEAVED_PTR);
|
||||
if (output_ptr != 0)
|
||||
break;
|
||||
|
||||
/* next voice */
|
||||
++i;
|
||||
voice_ptr += VOICE_SIZE;
|
||||
}
|
||||
/* next voice */
|
||||
++i;
|
||||
voice_ptr += VOICE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
return output_ptr;
|
||||
@ -506,25 +501,27 @@ static uint32_t voice_stage(struct hle_t* hle, musyx_t *musyx,
|
||||
|
||||
static void dma_cat8(struct hle_t* hle, uint8_t *dst, uint32_t catsrc_ptr)
|
||||
{
|
||||
uint32_t ptr1 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR1);
|
||||
uint32_t ptr2 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR2);
|
||||
uint16_t size1 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE1);
|
||||
uint16_t size2 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE2);
|
||||
uint32_t ptr1 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR1);
|
||||
uint32_t ptr2 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR2);
|
||||
uint16_t size1 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE1);
|
||||
uint16_t size2 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE2);
|
||||
|
||||
size_t count1 = size1;
|
||||
size_t count2 = size2;
|
||||
size_t count1 = size1;
|
||||
size_t count2 = size2;
|
||||
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"dma_cat: %08x %08x %04x %04x",
|
||||
ptr1,
|
||||
ptr2,
|
||||
size1,
|
||||
size2);
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"dma_cat: %08x %08x %04x %04x",
|
||||
ptr1,
|
||||
ptr2,
|
||||
size1,
|
||||
size2);
|
||||
|
||||
dram_load_u8(hle, dst, ptr1, count1);
|
||||
dram_load_u8(hle, dst, ptr1, count1);
|
||||
|
||||
if (size2 != 0)
|
||||
dram_load_u8(hle, dst + count1, ptr2, count2);
|
||||
if (size2 == 0)
|
||||
return;
|
||||
|
||||
dram_load_u8(hle, dst + count1, ptr2, count2);
|
||||
}
|
||||
|
||||
static void dma_cat16(struct hle_t* hle, uint16_t *dst, uint32_t catsrc_ptr)
|
||||
@ -546,63 +543,65 @@ static void dma_cat16(struct hle_t* hle, uint16_t *dst, uint32_t catsrc_ptr)
|
||||
|
||||
dram_load_u16(hle, dst, ptr1, count1);
|
||||
|
||||
if (size2 != 0)
|
||||
dram_load_u16(hle, dst + count1, ptr2, count2);
|
||||
if (size2 == 0)
|
||||
return;
|
||||
|
||||
dram_load_u16(hle, dst + count1, ptr2, count2);
|
||||
}
|
||||
|
||||
static void load_samples_PCM16(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples,
|
||||
unsigned *segbase, unsigned *offset)
|
||||
{
|
||||
uint8_t u8_3e = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES);
|
||||
uint16_t u16_40 = *dram_u16(hle, voice_ptr + VOICE_U16_40);
|
||||
uint16_t u16_42 = *dram_u16(hle, voice_ptr + VOICE_U16_42);
|
||||
|
||||
unsigned count = align(u16_40 + u8_3e, 4);
|
||||
uint8_t u8_3e = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES);
|
||||
uint16_t u16_40 = *dram_u16(hle, voice_ptr + VOICE_U16_40);
|
||||
uint16_t u16_42 = *dram_u16(hle, voice_ptr + VOICE_U16_42);
|
||||
|
||||
HleVerboseMessage(hle->user_defined, "Format: PCM16");
|
||||
unsigned count = align(u16_40 + u8_3e, 4);
|
||||
|
||||
*segbase = SAMPLE_BUFFER_SIZE - count;
|
||||
*offset = u8_3e;
|
||||
HleVerboseMessage(hle->user_defined, "Format: PCM16");
|
||||
|
||||
dma_cat16(hle, (uint16_t *)samples + *segbase, voice_ptr + VOICE_CATSRC_0);
|
||||
*segbase = SAMPLE_BUFFER_SIZE - count;
|
||||
*offset = u8_3e;
|
||||
|
||||
if (u16_42 != 0)
|
||||
dma_cat16(hle, (uint16_t *)samples, voice_ptr + VOICE_CATSRC_1);
|
||||
dma_cat16(hle, (uint16_t *)samples + *segbase, voice_ptr + VOICE_CATSRC_0);
|
||||
|
||||
if (u16_42 != 0)
|
||||
dma_cat16(hle, (uint16_t *)samples, voice_ptr + VOICE_CATSRC_1);
|
||||
}
|
||||
|
||||
static void load_samples_ADPCM(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples,
|
||||
unsigned *segbase, unsigned *offset)
|
||||
{
|
||||
/* decompressed samples cannot exceed 0x400 bytes;
|
||||
* ADPCM has a compression ratio of 5/16 */
|
||||
uint8_t buffer[SAMPLE_BUFFER_SIZE * 2 * 5 / 16];
|
||||
int16_t adpcm_table[128];
|
||||
/* decompressed samples cannot exceed 0x400 bytes;
|
||||
* ADPCM has a compression ratio of 5/16 */
|
||||
uint8_t buffer[SAMPLE_BUFFER_SIZE * 2 * 5 / 16];
|
||||
int16_t adpcm_table[128];
|
||||
|
||||
uint8_t u8_3c = *dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES );
|
||||
uint8_t u8_3d = *dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES + 1);
|
||||
uint8_t u8_3e = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES );
|
||||
uint8_t u8_3f = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES + 1);
|
||||
uint32_t adpcm_table_ptr = *dram_u32(hle, voice_ptr + VOICE_ADPCM_TABLE_PTR);
|
||||
unsigned count;
|
||||
uint8_t u8_3c = *dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES );
|
||||
uint8_t u8_3d = *dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES + 1);
|
||||
uint8_t u8_3e = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES );
|
||||
uint8_t u8_3f = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES + 1);
|
||||
uint32_t adpcm_table_ptr = *dram_u32(hle, voice_ptr + VOICE_ADPCM_TABLE_PTR);
|
||||
unsigned count;
|
||||
|
||||
HleVerboseMessage(hle->user_defined, "Format: ADPCM");
|
||||
HleVerboseMessage(hle->user_defined, "Format: ADPCM");
|
||||
|
||||
HleVerboseMessage(hle->user_defined, "Loading ADPCM table: %08x", adpcm_table_ptr);
|
||||
dram_load_u16(hle, (uint16_t *)adpcm_table, adpcm_table_ptr, 128);
|
||||
HleVerboseMessage(hle->user_defined, "Loading ADPCM table: %08x", adpcm_table_ptr);
|
||||
dram_load_u16(hle, (uint16_t *)adpcm_table, adpcm_table_ptr, 128);
|
||||
|
||||
count = u8_3c << 5;
|
||||
count = u8_3c << 5;
|
||||
|
||||
*segbase = SAMPLE_BUFFER_SIZE - count;
|
||||
*offset = u8_3e & 0x1f;
|
||||
*segbase = SAMPLE_BUFFER_SIZE - count;
|
||||
*offset = u8_3e & 0x1f;
|
||||
|
||||
dma_cat8(hle, buffer, voice_ptr + VOICE_CATSRC_0);
|
||||
adpcm_decode_frames(hle, samples + *segbase, buffer, adpcm_table, u8_3c, u8_3e);
|
||||
dma_cat8(hle, buffer, voice_ptr + VOICE_CATSRC_0);
|
||||
adpcm_decode_frames(hle, samples + *segbase, buffer, adpcm_table, u8_3c, u8_3e);
|
||||
|
||||
if (u8_3d == 0)
|
||||
return;
|
||||
|
||||
dma_cat8(hle, buffer, voice_ptr + VOICE_CATSRC_1);
|
||||
adpcm_decode_frames(hle, samples, buffer, adpcm_table, u8_3d, u8_3f);
|
||||
if (u8_3d != 0) {
|
||||
dma_cat8(hle, buffer, voice_ptr + VOICE_CATSRC_1);
|
||||
adpcm_decode_frames(hle, samples, buffer, adpcm_table, u8_3d, u8_3f);
|
||||
}
|
||||
}
|
||||
|
||||
static void adpcm_decode_frames(struct hle_t* hle,
|
||||
@ -610,381 +609,380 @@ static void adpcm_decode_frames(struct hle_t* hle,
|
||||
const int16_t *table, uint8_t count,
|
||||
uint8_t skip_samples)
|
||||
{
|
||||
unsigned i;
|
||||
int16_t frame[32];
|
||||
const uint8_t *nibbles = src + 8;
|
||||
bool jump_gap = false;
|
||||
int16_t frame[32];
|
||||
const uint8_t *nibbles = src + 8;
|
||||
unsigned i;
|
||||
bool jump_gap = false;
|
||||
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"ADPCM decode: count=%d, skip=%d",
|
||||
count, skip_samples);
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"ADPCM decode: count=%d, skip=%d",
|
||||
count, skip_samples);
|
||||
|
||||
if (skip_samples >= 32)
|
||||
{
|
||||
jump_gap = true;
|
||||
nibbles += 16;
|
||||
src += 4;
|
||||
}
|
||||
if (skip_samples >= 32) {
|
||||
jump_gap = true;
|
||||
nibbles += 16;
|
||||
src += 4;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
uint8_t c2 = nibbles[0];
|
||||
const int16_t *book = (c2 & 0xf0) + table;
|
||||
unsigned int rshift = (c2 & 0x0f);
|
||||
for (i = 0; i < count; ++i) {
|
||||
uint8_t c2 = nibbles[0];
|
||||
|
||||
adpcm_predict_frame(frame, src, nibbles, rshift);
|
||||
const int16_t *book = (c2 & 0xf0) + table;
|
||||
unsigned int rshift = (c2 & 0x0f);
|
||||
|
||||
memcpy(dst, frame, 2 * sizeof(frame[0]));
|
||||
adpcm_compute_residuals(dst + 2, frame + 2, book, dst , 6);
|
||||
adpcm_compute_residuals(dst + 8, frame + 8, book, dst + 6, 8);
|
||||
adpcm_compute_residuals(dst + 16, frame + 16, book, dst + 14, 8);
|
||||
adpcm_compute_residuals(dst + 24, frame + 24, book, dst + 22, 8);
|
||||
adpcm_predict_frame(frame, src, nibbles, rshift);
|
||||
|
||||
if (jump_gap)
|
||||
{
|
||||
nibbles += 8;
|
||||
src += 32;
|
||||
}
|
||||
memcpy(dst, frame, 2 * sizeof(frame[0]));
|
||||
adpcm_compute_residuals(dst + 2, frame + 2, book, dst , 6);
|
||||
adpcm_compute_residuals(dst + 8, frame + 8, book, dst + 6, 8);
|
||||
adpcm_compute_residuals(dst + 16, frame + 16, book, dst + 14, 8);
|
||||
adpcm_compute_residuals(dst + 24, frame + 24, book, dst + 22, 8);
|
||||
|
||||
jump_gap = !jump_gap;
|
||||
nibbles += 16;
|
||||
src += 4;
|
||||
dst += 32;
|
||||
}
|
||||
if (jump_gap) {
|
||||
nibbles += 8;
|
||||
src += 32;
|
||||
}
|
||||
|
||||
jump_gap = !jump_gap;
|
||||
nibbles += 16;
|
||||
src += 4;
|
||||
dst += 32;
|
||||
}
|
||||
}
|
||||
|
||||
static void adpcm_predict_frame(int16_t *dst, const uint8_t *src,
|
||||
const uint8_t *nibbles, unsigned int rshift)
|
||||
const uint8_t *nibbles,
|
||||
unsigned int rshift)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int i;
|
||||
|
||||
*(dst++) = (src[0] << 8) | src[1];
|
||||
*(dst++) = (src[2] << 8) | src[3];
|
||||
*(dst++) = (src[0] << 8) | src[1];
|
||||
*(dst++) = (src[2] << 8) | src[3];
|
||||
|
||||
for (i = 1; i < 16; ++i)
|
||||
{
|
||||
uint8_t byte = nibbles[i];
|
||||
for (i = 1; i < 16; ++i) {
|
||||
uint8_t byte = nibbles[i];
|
||||
|
||||
*(dst++) = adpcm_predict_sample(byte, 0xf0, 8, rshift);
|
||||
*(dst++) = adpcm_predict_sample(byte, 0x0f, 12, rshift);
|
||||
}
|
||||
*(dst++) = adpcm_predict_sample(byte, 0xf0, 8, rshift);
|
||||
*(dst++) = adpcm_predict_sample(byte, 0x0f, 12, rshift);
|
||||
}
|
||||
}
|
||||
|
||||
static void mix_voice_samples(struct hle_t* hle, musyx_t *musyx,
|
||||
uint32_t voice_ptr, const int16_t *samples,
|
||||
unsigned segbase, unsigned offset, uint32_t last_sample_ptr)
|
||||
uint32_t voice_ptr, const int16_t *samples,
|
||||
unsigned segbase, unsigned offset, uint32_t last_sample_ptr)
|
||||
{
|
||||
int i, k;
|
||||
int32_t v4_env[4];
|
||||
int32_t v4_env_step[4];
|
||||
int16_t *v4_dst[4];
|
||||
int16_t v4[4];
|
||||
int i, k;
|
||||
|
||||
/* parse VOICE structure */
|
||||
const uint16_t pitch_q16 = *dram_u16(hle, voice_ptr + VOICE_PITCH_Q16);
|
||||
const uint16_t pitch_shift = *dram_u16(hle, voice_ptr + VOICE_PITCH_SHIFT); /* Q4.12 */
|
||||
/* parse VOICE structure */
|
||||
const uint16_t pitch_q16 = *dram_u16(hle, voice_ptr + VOICE_PITCH_Q16);
|
||||
const uint16_t pitch_shift = *dram_u16(hle, voice_ptr + VOICE_PITCH_SHIFT); /* Q4.12 */
|
||||
|
||||
const uint16_t end_point = *dram_u16(hle, voice_ptr + VOICE_END_POINT);
|
||||
const uint16_t restart_point = *dram_u16(hle, voice_ptr + VOICE_RESTART_POINT);
|
||||
const uint16_t end_point = *dram_u16(hle, voice_ptr + VOICE_END_POINT);
|
||||
const uint16_t restart_point = *dram_u16(hle, voice_ptr + VOICE_RESTART_POINT);
|
||||
|
||||
const uint16_t u16_4e = *dram_u16(hle, voice_ptr + VOICE_U16_4E);
|
||||
const uint16_t u16_4e = *dram_u16(hle, voice_ptr + VOICE_U16_4E);
|
||||
|
||||
/* init values and pointers */
|
||||
const int16_t *sample = samples + segbase + offset + u16_4e;
|
||||
const int16_t *const sample_end = samples + segbase + end_point;
|
||||
const int16_t *const sample_restart = samples + (restart_point & 0x7fff) +
|
||||
(((restart_point & 0x8000) != 0) ? 0x000 : segbase);
|
||||
/* init values and pointers */
|
||||
const int16_t *sample = samples + segbase + offset + u16_4e;
|
||||
const int16_t *const sample_end = samples + segbase + end_point;
|
||||
const int16_t *const sample_restart = samples + (restart_point & 0x7fff) +
|
||||
(((restart_point & 0x8000) != 0) ? 0x000 : segbase);
|
||||
|
||||
uint32_t pitch_accu = pitch_q16;
|
||||
uint32_t pitch_step = pitch_shift << 4;
|
||||
|
||||
dram_load_u32(hle, (uint32_t *)v4_env, voice_ptr + VOICE_ENV_BEGIN, 4);
|
||||
dram_load_u32(hle, (uint32_t *)v4_env_step, voice_ptr + VOICE_ENV_STEP, 4);
|
||||
uint32_t pitch_accu = pitch_q16;
|
||||
uint32_t pitch_step = pitch_shift << 4;
|
||||
|
||||
v4_dst[0] = musyx->left;
|
||||
v4_dst[1] = musyx->right;
|
||||
v4_dst[2] = musyx->cc0;
|
||||
v4_dst[3] = musyx->e50;
|
||||
int32_t v4_env[4];
|
||||
int32_t v4_env_step[4];
|
||||
int16_t *v4_dst[4];
|
||||
int16_t v4[4];
|
||||
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"Voice debug: segbase=%d"
|
||||
"\tu16_4e=%04x\n"
|
||||
"\tpitch: frac0=%04x shift=%04x\n"
|
||||
"\tend_point=%04x restart_point=%04x\n"
|
||||
"\tenv = %08x %08x %08x %08x\n"
|
||||
"\tenv_step = %08x %08x %08x %08x\n",
|
||||
segbase,
|
||||
u16_4e,
|
||||
pitch_q16, pitch_shift,
|
||||
end_point, restart_point,
|
||||
v4_env[0], v4_env[1], v4_env[2], v4_env[3],
|
||||
v4_env_step[0], v4_env_step[1], v4_env_step[2], v4_env_step[3]);
|
||||
dram_load_u32(hle, (uint32_t *)v4_env, voice_ptr + VOICE_ENV_BEGIN, 4);
|
||||
dram_load_u32(hle, (uint32_t *)v4_env_step, voice_ptr + VOICE_ENV_STEP, 4);
|
||||
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i)
|
||||
{
|
||||
int dist;
|
||||
int16_t v;
|
||||
/* update sample and lut pointers and then pitch_accu */
|
||||
const int16_t *lut = (RESAMPLE_LUT + ((pitch_accu & 0xfc00) >> 8));
|
||||
v4_dst[0] = musyx->left;
|
||||
v4_dst[1] = musyx->right;
|
||||
v4_dst[2] = musyx->cc0;
|
||||
v4_dst[3] = musyx->e50;
|
||||
|
||||
sample += (pitch_accu >> 16);
|
||||
pitch_accu &= 0xffff;
|
||||
pitch_accu += pitch_step;
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"Voice debug: segbase=%d"
|
||||
"\tu16_4e=%04x\n"
|
||||
"\tpitch: frac0=%04x shift=%04x\n"
|
||||
"\tend_point=%04x restart_point=%04x\n"
|
||||
"\tenv = %08x %08x %08x %08x\n"
|
||||
"\tenv_step = %08x %08x %08x %08x\n",
|
||||
segbase,
|
||||
u16_4e,
|
||||
pitch_q16, pitch_shift,
|
||||
end_point, restart_point,
|
||||
v4_env[0], v4_env[1], v4_env[2], v4_env[3],
|
||||
v4_env_step[0], v4_env_step[1], v4_env_step[2], v4_env_step[3]);
|
||||
|
||||
/* handle end/restart points */
|
||||
dist = sample - sample_end;
|
||||
if (dist >= 0)
|
||||
sample = sample_restart + dist;
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i) {
|
||||
/* update sample and lut pointers and then pitch_accu */
|
||||
const int16_t *lut = (RESAMPLE_LUT + ((pitch_accu & 0xfc00) >> 8));
|
||||
int dist;
|
||||
int16_t v;
|
||||
|
||||
/* apply resample filter */
|
||||
v = clamp_s16(dot4(sample, lut));
|
||||
sample += (pitch_accu >> 16);
|
||||
pitch_accu &= 0xffff;
|
||||
pitch_accu += pitch_step;
|
||||
|
||||
for (k = 0; k < 4; ++k)
|
||||
{
|
||||
/* envmix */
|
||||
int32_t accu = (v * (v4_env[k] >> 16)) >> 15;
|
||||
v4[k] = clamp_s16(accu);
|
||||
*(v4_dst[k]) = clamp_s16(accu + *(v4_dst[k]));
|
||||
/* handle end/restart points */
|
||||
dist = sample - sample_end;
|
||||
if (dist >= 0)
|
||||
sample = sample_restart + dist;
|
||||
|
||||
/* update envelopes and dst pointers */
|
||||
++(v4_dst[k]);
|
||||
v4_env[k] += v4_env_step[k];
|
||||
}
|
||||
}
|
||||
/* apply resample filter */
|
||||
v = clamp_s16(dot4(sample, lut));
|
||||
|
||||
/* save last resampled sample */
|
||||
dram_store_u16(hle, (uint16_t *)v4, last_sample_ptr, 4);
|
||||
for (k = 0; k < 4; ++k) {
|
||||
/* envmix */
|
||||
int32_t accu = (v * (v4_env[k] >> 16)) >> 15;
|
||||
v4[k] = clamp_s16(accu);
|
||||
*(v4_dst[k]) = clamp_s16(accu + *(v4_dst[k]));
|
||||
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"last_sample = %04x %04x %04x %04x",
|
||||
v4[0], v4[1], v4[2], v4[3]);
|
||||
/* update envelopes and dst pointers */
|
||||
++(v4_dst[k]);
|
||||
v4_env[k] += v4_env_step[k];
|
||||
}
|
||||
}
|
||||
|
||||
/* save last resampled sample */
|
||||
dram_store_u16(hle, (uint16_t *)v4, last_sample_ptr, 4);
|
||||
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"last_sample = %04x %04x %04x %04x",
|
||||
v4[0], v4[1], v4[2], v4[3]);
|
||||
}
|
||||
|
||||
|
||||
static void sfx_stage(struct hle_t* hle, mix_sfx_with_main_subframes_t mix_sfx_with_main_subframes,
|
||||
musyx_t *musyx, uint32_t sfx_ptr, uint16_t idx)
|
||||
{
|
||||
uint32_t cbuffer_ptr;
|
||||
uint32_t cbuffer_length;
|
||||
uint16_t tap_count;
|
||||
int16_t fir4_hgain;
|
||||
uint16_t sfx_gains[2];
|
||||
unsigned int i;
|
||||
unsigned int i;
|
||||
|
||||
int16_t buffer[SUBFRAME_SIZE + 4];
|
||||
uint32_t tap_delays[8];
|
||||
int16_t tap_gains[8];
|
||||
int16_t fir4_hcoeffs[4];
|
||||
int16_t buffer[SUBFRAME_SIZE + 4];
|
||||
int16_t *subframe = buffer + 4;
|
||||
|
||||
int16_t delayed[SUBFRAME_SIZE];
|
||||
uint32_t tap_delays[8];
|
||||
int16_t tap_gains[8];
|
||||
int16_t fir4_hcoeffs[4];
|
||||
|
||||
int16_t *subframe = buffer + 4;
|
||||
const uint32_t pos = idx * SUBFRAME_SIZE;
|
||||
int16_t delayed[SUBFRAME_SIZE];
|
||||
int dpos, dlength;
|
||||
|
||||
HleVerboseMessage(hle->user_defined, "SFX: %08x, idx=%d", sfx_ptr, idx);
|
||||
const uint32_t pos = idx * SUBFRAME_SIZE;
|
||||
|
||||
if (sfx_ptr == 0)
|
||||
return;
|
||||
uint32_t cbuffer_ptr;
|
||||
uint32_t cbuffer_length;
|
||||
uint16_t tap_count;
|
||||
int16_t fir4_hgain;
|
||||
uint16_t sfx_gains[2];
|
||||
|
||||
/* load sfx parameters */
|
||||
cbuffer_ptr = *dram_u32(hle, sfx_ptr + SFX_CBUFFER_PTR);
|
||||
cbuffer_length = *dram_u32(hle, sfx_ptr + SFX_CBUFFER_LENGTH);
|
||||
HleVerboseMessage(hle->user_defined, "SFX: %08x, idx=%d", sfx_ptr, idx);
|
||||
|
||||
tap_count = *dram_u16(hle, sfx_ptr + SFX_TAP_COUNT);
|
||||
if (sfx_ptr == 0)
|
||||
return;
|
||||
|
||||
dram_load_u32(hle, tap_delays, sfx_ptr + SFX_TAP_DELAYS, 8);
|
||||
dram_load_u16(hle, (uint16_t *)tap_gains, sfx_ptr + SFX_TAP_GAINS, 8);
|
||||
/* load sfx parameters */
|
||||
cbuffer_ptr = *dram_u32(hle, sfx_ptr + SFX_CBUFFER_PTR);
|
||||
cbuffer_length = *dram_u32(hle, sfx_ptr + SFX_CBUFFER_LENGTH);
|
||||
|
||||
fir4_hgain = *dram_u16(hle, sfx_ptr + SFX_FIR4_HGAIN);
|
||||
dram_load_u16(hle, (uint16_t *)fir4_hcoeffs, sfx_ptr + SFX_FIR4_HCOEFFS, 4);
|
||||
tap_count = *dram_u16(hle, sfx_ptr + SFX_TAP_COUNT);
|
||||
|
||||
sfx_gains[0] = *dram_u16(hle, sfx_ptr + SFX_U16_3C);
|
||||
sfx_gains[1] = *dram_u16(hle, sfx_ptr + SFX_U16_3E);
|
||||
dram_load_u32(hle, tap_delays, sfx_ptr + SFX_TAP_DELAYS, 8);
|
||||
dram_load_u16(hle, (uint16_t *)tap_gains, sfx_ptr + SFX_TAP_GAINS, 8);
|
||||
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"cbuffer: ptr=%08x length=%x", cbuffer_ptr,
|
||||
cbuffer_length);
|
||||
fir4_hgain = *dram_u16(hle, sfx_ptr + SFX_FIR4_HGAIN);
|
||||
dram_load_u16(hle, (uint16_t *)fir4_hcoeffs, sfx_ptr + SFX_FIR4_HCOEFFS, 4);
|
||||
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"fir4: hgain=%04x hcoeff=%04x %04x %04x %04x",
|
||||
fir4_hgain,
|
||||
fir4_hcoeffs[0], fir4_hcoeffs[1], fir4_hcoeffs[2], fir4_hcoeffs[3]);
|
||||
sfx_gains[0] = *dram_u16(hle, sfx_ptr + SFX_U16_3C);
|
||||
sfx_gains[1] = *dram_u16(hle, sfx_ptr + SFX_U16_3E);
|
||||
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"tap count=%d\n"
|
||||
"delays: %08x %08x %08x %08x %08x %08x %08x %08x\n"
|
||||
"gains: %04x %04x %04x %04x %04x %04x %04x %04x",
|
||||
tap_count,
|
||||
tap_delays[0], tap_delays[1], tap_delays[2], tap_delays[3],
|
||||
tap_delays[4], tap_delays[5], tap_delays[6], tap_delays[7],
|
||||
tap_gains[0], tap_gains[1], tap_gains[2], tap_gains[3],
|
||||
tap_gains[4], tap_gains[5], tap_gains[6], tap_gains[7]);
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"cbuffer: ptr=%08x length=%x", cbuffer_ptr,
|
||||
cbuffer_length);
|
||||
|
||||
HleVerboseMessage(hle->user_defined, "sfx_gains=%04x %04x", sfx_gains[0], sfx_gains[1]);
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"fir4: hgain=%04x hcoeff=%04x %04x %04x %04x",
|
||||
fir4_hgain,
|
||||
fir4_hcoeffs[0], fir4_hcoeffs[1], fir4_hcoeffs[2], fir4_hcoeffs[3]);
|
||||
|
||||
/* mix up to 8 delayed subframes */
|
||||
memset(subframe, 0, SUBFRAME_SIZE * sizeof(subframe[0]));
|
||||
for (i = 0; i < tap_count; ++i)
|
||||
{
|
||||
int dlength;
|
||||
int dpos = pos - tap_delays[i];
|
||||
if (dpos <= 0)
|
||||
dpos += cbuffer_length;
|
||||
dlength = SUBFRAME_SIZE;
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"tap count=%d\n"
|
||||
"delays: %08x %08x %08x %08x %08x %08x %08x %08x\n"
|
||||
"gains: %04x %04x %04x %04x %04x %04x %04x %04x",
|
||||
tap_count,
|
||||
tap_delays[0], tap_delays[1], tap_delays[2], tap_delays[3],
|
||||
tap_delays[4], tap_delays[5], tap_delays[6], tap_delays[7],
|
||||
tap_gains[0], tap_gains[1], tap_gains[2], tap_gains[3],
|
||||
tap_gains[4], tap_gains[5], tap_gains[6], tap_gains[7]);
|
||||
|
||||
if ((uint32_t)(dpos + SUBFRAME_SIZE) > cbuffer_length) {
|
||||
dlength = cbuffer_length - dpos;
|
||||
dram_load_u16(hle, (uint16_t *)delayed + dlength, cbuffer_ptr, SUBFRAME_SIZE - dlength);
|
||||
}
|
||||
HleVerboseMessage(hle->user_defined, "sfx_gains=%04x %04x", sfx_gains[0], sfx_gains[1]);
|
||||
|
||||
dram_load_u16(hle, (uint16_t *)delayed, cbuffer_ptr + dpos * 2, dlength);
|
||||
/* mix up to 8 delayed subframes */
|
||||
memset(subframe, 0, SUBFRAME_SIZE * sizeof(subframe[0]));
|
||||
for (i = 0; i < tap_count; ++i) {
|
||||
|
||||
mix_subframes(subframe, delayed, tap_gains[i]);
|
||||
}
|
||||
dpos = pos - tap_delays[i];
|
||||
if (dpos <= 0)
|
||||
dpos += cbuffer_length;
|
||||
dlength = SUBFRAME_SIZE;
|
||||
|
||||
/* add resulting subframe to main subframes */
|
||||
mix_sfx_with_main_subframes(musyx, subframe, sfx_gains);
|
||||
if ((uint32_t)(dpos + SUBFRAME_SIZE) > cbuffer_length) {
|
||||
dlength = cbuffer_length - dpos;
|
||||
dram_load_u16(hle, (uint16_t *)delayed + dlength, cbuffer_ptr, SUBFRAME_SIZE - dlength);
|
||||
}
|
||||
|
||||
/* apply FIR4 filter and writeback filtered result */
|
||||
memcpy(buffer, musyx->subframe_740_last4, 4 * sizeof(int16_t));
|
||||
memcpy(musyx->subframe_740_last4, subframe + SUBFRAME_SIZE - 4, 4 * sizeof(int16_t));
|
||||
mix_fir4(musyx->e50, buffer + 1, fir4_hgain, fir4_hcoeffs);
|
||||
dram_store_u16(hle, (uint16_t *)musyx->e50, cbuffer_ptr + pos * 2, SUBFRAME_SIZE);
|
||||
dram_load_u16(hle, (uint16_t *)delayed, cbuffer_ptr + dpos * 2, dlength);
|
||||
|
||||
mix_subframes(subframe, delayed, tap_gains[i]);
|
||||
}
|
||||
|
||||
/* add resulting subframe to main subframes */
|
||||
mix_sfx_with_main_subframes(musyx, subframe, sfx_gains);
|
||||
|
||||
/* apply FIR4 filter and writeback filtered result */
|
||||
memcpy(buffer, musyx->subframe_740_last4, 4 * sizeof(int16_t));
|
||||
memcpy(musyx->subframe_740_last4, subframe + SUBFRAME_SIZE - 4, 4 * sizeof(int16_t));
|
||||
mix_fir4(musyx->e50, buffer + 1, fir4_hgain, fir4_hcoeffs);
|
||||
dram_store_u16(hle, (uint16_t *)musyx->e50, cbuffer_ptr + pos * 2, SUBFRAME_SIZE);
|
||||
}
|
||||
|
||||
static void mix_sfx_with_main_subframes_v1(musyx_t *musyx, const int16_t *subframe,
|
||||
const uint16_t* UNUSED(gains))
|
||||
{
|
||||
unsigned i;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i)
|
||||
{
|
||||
int16_t v = subframe[i];
|
||||
musyx->left[i] = clamp_s16(musyx->left[i] + v);
|
||||
musyx->right[i] = clamp_s16(musyx->right[i] + v);
|
||||
}
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i) {
|
||||
int16_t v = subframe[i];
|
||||
musyx->left[i] = clamp_s16(musyx->left[i] + v);
|
||||
musyx->right[i] = clamp_s16(musyx->right[i] + v);
|
||||
}
|
||||
}
|
||||
|
||||
static void mix_sfx_with_main_subframes_v2(musyx_t *musyx, const int16_t *subframe,
|
||||
const uint16_t* gains)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i)
|
||||
{
|
||||
int16_t v = subframe[i];
|
||||
int16_t v1 = (int32_t)(v * gains[0]) >> 16;
|
||||
int16_t v2 = (int32_t)(v * gains[1]) >> 16;
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i) {
|
||||
int16_t v = subframe[i];
|
||||
int16_t v1 = (int32_t)(v * gains[0]) >> 16;
|
||||
int16_t v2 = (int32_t)(v * gains[1]) >> 16;
|
||||
|
||||
musyx->left[i] = clamp_s16(musyx->left[i] + v1);
|
||||
musyx->right[i] = clamp_s16(musyx->right[i] + v1);
|
||||
musyx->cc0[i] = clamp_s16(musyx->cc0[i] + v2);
|
||||
}
|
||||
musyx->left[i] = clamp_s16(musyx->left[i] + v1);
|
||||
musyx->right[i] = clamp_s16(musyx->right[i] + v1);
|
||||
musyx->cc0[i] = clamp_s16(musyx->cc0[i] + v2);
|
||||
}
|
||||
}
|
||||
|
||||
#define mix_samples(y, x, hgain) (clamp_s16(*(y) + (((x) * (hgain) + 0x4000) >> 15)))
|
||||
static void mix_samples(int16_t *y, int16_t x, int16_t hgain)
|
||||
{
|
||||
*y = clamp_s16(*y + ((x * hgain + 0x4000) >> 15));
|
||||
}
|
||||
|
||||
static void mix_subframes(int16_t *y, const int16_t *x, int16_t hgain)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i)
|
||||
y[i] = mix_samples(&y[i], x[i], hgain);
|
||||
mix_samples(&y[i], x[i], hgain);
|
||||
}
|
||||
|
||||
static void mix_fir4(int16_t *y, const int16_t *x, int16_t hgain, const int16_t *hcoeffs)
|
||||
{
|
||||
unsigned int i;
|
||||
int32_t h[4];
|
||||
unsigned int i;
|
||||
int32_t h[4];
|
||||
|
||||
h[0] = (hgain * hcoeffs[0]) >> 15;
|
||||
h[1] = (hgain * hcoeffs[1]) >> 15;
|
||||
h[2] = (hgain * hcoeffs[2]) >> 15;
|
||||
h[3] = (hgain * hcoeffs[3]) >> 15;
|
||||
h[0] = (hgain * hcoeffs[0]) >> 15;
|
||||
h[1] = (hgain * hcoeffs[1]) >> 15;
|
||||
h[2] = (hgain * hcoeffs[2]) >> 15;
|
||||
h[3] = (hgain * hcoeffs[3]) >> 15;
|
||||
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i)
|
||||
{
|
||||
int32_t v = (h[0] * x[i] + h[1] * x[i + 1] + h[2] * x[i + 2] + h[3] * x[i + 3]) >> 15;
|
||||
y[i] = clamp_s16(y[i] + v);
|
||||
}
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i) {
|
||||
int32_t v = (h[0] * x[i] + h[1] * x[i + 1] + h[2] * x[i + 2] + h[3] * x[i + 3]) >> 15;
|
||||
y[i] = clamp_s16(y[i] + v);
|
||||
}
|
||||
}
|
||||
|
||||
static void interleave_stage_v1(struct hle_t* hle, musyx_t *musyx, uint32_t output_ptr)
|
||||
{
|
||||
size_t i;
|
||||
int16_t base_left = clamp_s16(musyx->base_vol[0]);
|
||||
int16_t base_right = clamp_s16(musyx->base_vol[1]);
|
||||
int16_t *left = musyx->left;
|
||||
int16_t *right = musyx->right;
|
||||
uint32_t *dst = dram_u32(hle, output_ptr);
|
||||
size_t i;
|
||||
|
||||
#ifndef NDEBUG
|
||||
HleVerboseMessage(hle->user_defined, "interleave: %08x", output_ptr);
|
||||
#endif
|
||||
int16_t base_left;
|
||||
int16_t base_right;
|
||||
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i)
|
||||
{
|
||||
uint16_t l = clamp_s16(*(left++) + base_left);
|
||||
uint16_t r = clamp_s16(*(right++) + base_right);
|
||||
int16_t *left;
|
||||
int16_t *right;
|
||||
uint32_t *dst;
|
||||
|
||||
*(dst++) = (l << 16) | r;
|
||||
}
|
||||
HleVerboseMessage(hle->user_defined, "interleave: %08x", output_ptr);
|
||||
|
||||
base_left = clamp_s16(musyx->base_vol[0]);
|
||||
base_right = clamp_s16(musyx->base_vol[1]);
|
||||
|
||||
left = musyx->left;
|
||||
right = musyx->right;
|
||||
dst = dram_u32(hle, output_ptr);
|
||||
|
||||
for (i = 0; i < SUBFRAME_SIZE; ++i) {
|
||||
uint16_t l = clamp_s16(*(left++) + base_left);
|
||||
uint16_t r = clamp_s16(*(right++) + base_right);
|
||||
|
||||
*(dst++) = (l << 16) | r;
|
||||
}
|
||||
}
|
||||
|
||||
static void interleave_stage_v2(struct hle_t* hle, musyx_t *musyx,
|
||||
uint16_t mask_16, uint32_t ptr_18,
|
||||
uint32_t ptr_1c, uint32_t output_ptr)
|
||||
uint16_t mask_16, uint32_t ptr_18,
|
||||
uint32_t ptr_1c, uint32_t output_ptr)
|
||||
{
|
||||
unsigned i, k;
|
||||
int16_t subframe[SUBFRAME_SIZE];
|
||||
uint32_t *dst;
|
||||
uint16_t mask;
|
||||
unsigned i, k;
|
||||
int16_t subframe[SUBFRAME_SIZE];
|
||||
uint32_t *dst;
|
||||
uint16_t mask;
|
||||
|
||||
#ifndef NDEBUG
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"mask_16=%04x ptr_18=%08x ptr_1c=%08x output_ptr=%08x",
|
||||
mask_16, ptr_18, ptr_1c, output_ptr);
|
||||
#endif
|
||||
HleVerboseMessage(hle->user_defined,
|
||||
"mask_16=%04x ptr_18=%08x ptr_1c=%08x output_ptr=%08x",
|
||||
mask_16, ptr_18, ptr_1c, output_ptr);
|
||||
|
||||
/* compute L_total, R_total and update subframe @ptr_1c */
|
||||
memset(subframe, 0, SUBFRAME_SIZE*sizeof(subframe[0]));
|
||||
/* compute L_total, R_total and update subframe @ptr_1c */
|
||||
memset(subframe, 0, SUBFRAME_SIZE*sizeof(subframe[0]));
|
||||
|
||||
for(i = 0; i < SUBFRAME_SIZE; ++i)
|
||||
{
|
||||
int16_t v = *dram_u16(hle, ptr_1c + i*2);
|
||||
musyx->left[i] = v;
|
||||
musyx->right[i] = clamp_s16(-v);
|
||||
}
|
||||
for(i = 0; i < SUBFRAME_SIZE; ++i) {
|
||||
int16_t v = *dram_u16(hle, ptr_1c + i*2);
|
||||
musyx->left[i] = v;
|
||||
musyx->right[i] = clamp_s16(-v);
|
||||
}
|
||||
|
||||
for (k = 0, mask = 1; k < 8; ++k, mask <<= 1, ptr_18 += 8)
|
||||
{
|
||||
int16_t hgain;
|
||||
uint32_t address;
|
||||
for (k = 0, mask = 1; k < 8; ++k, mask <<= 1, ptr_18 += 8) {
|
||||
int16_t hgain;
|
||||
uint32_t address;
|
||||
|
||||
if ((mask_16 & mask) == 0)
|
||||
continue;
|
||||
if ((mask_16 & mask) == 0)
|
||||
continue;
|
||||
|
||||
address = *dram_u32(hle, ptr_18);
|
||||
hgain = *dram_u16(hle, ptr_18 + 4);
|
||||
address = *dram_u32(hle, ptr_18);
|
||||
hgain = *dram_u16(hle, ptr_18 + 4);
|
||||
|
||||
for(i = 0; i < SUBFRAME_SIZE; ++i, address += 2)
|
||||
{
|
||||
musyx->left[i] = mix_samples(&musyx->left[i], *dram_u16(hle, address), hgain);
|
||||
musyx->right[i] = mix_samples(&musyx->right[i], *dram_u16(hle, address + 2*SUBFRAME_SIZE), hgain);
|
||||
subframe[i] = mix_samples(&subframe[i], *dram_u16(hle, address + 4*SUBFRAME_SIZE), hgain);
|
||||
}
|
||||
}
|
||||
for(i = 0; i < SUBFRAME_SIZE; ++i, address += 2) {
|
||||
mix_samples(&musyx->left[i], *dram_u16(hle, address), hgain);
|
||||
mix_samples(&musyx->right[i], *dram_u16(hle, address + 2*SUBFRAME_SIZE), hgain);
|
||||
mix_samples(&subframe[i], *dram_u16(hle, address + 4*SUBFRAME_SIZE), hgain);
|
||||
}
|
||||
}
|
||||
|
||||
/* interleave L_total and R_total */
|
||||
dst = dram_u32(hle, output_ptr);
|
||||
/* interleave L_total and R_total */
|
||||
dst = dram_u32(hle, output_ptr);
|
||||
for(i = 0; i < SUBFRAME_SIZE; ++i) {
|
||||
uint16_t l = musyx->left[i];
|
||||
uint16_t r = musyx->right[i];
|
||||
*(dst++) = (l << 16) | r;
|
||||
}
|
||||
|
||||
for(i = 0; i < SUBFRAME_SIZE; ++i)
|
||||
{
|
||||
uint16_t l = musyx->left[i];
|
||||
uint16_t r = musyx->right[i];
|
||||
*(dst++) = (l << 16) | r;
|
||||
}
|
||||
|
||||
/* writeback subframe @ptr_1c */
|
||||
dram_store_u16(hle, (uint16_t*)subframe, ptr_1c, SUBFRAME_SIZE);
|
||||
/* writeback subframe @ptr_1c */
|
||||
dram_store_u16(hle, (uint16_t*)subframe, ptr_1c, SUBFRAME_SIZE);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - re2.c *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2016 Gilles Siberlin *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -27,8 +27,10 @@
|
||||
#include "hle_internal.h"
|
||||
#include "memory.h"
|
||||
|
||||
#define SATURATE8(x) ((unsigned int) x <= 255 ? x : (x < 0 ? 0: 255))
|
||||
|
||||
/**************************************************************************
|
||||
* Resident evil 2 ucode
|
||||
* Resident evil 2 ucodes
|
||||
**************************************************************************/
|
||||
void resize_bilinear_task(struct hle_t* hle)
|
||||
{
|
||||
@ -94,4 +96,129 @@ void resize_bilinear_task(struct hle_t* hle)
|
||||
}
|
||||
y += y_ratio;
|
||||
}
|
||||
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
static uint32_t YCbCr_to_RGBA(uint8_t Y, uint8_t Cb, uint8_t Cr)
|
||||
{
|
||||
int r, g, b;
|
||||
|
||||
r = (int)(((double)Y * 0.582199097) + (0.701004028 * (double)(Cr - 128)));
|
||||
g = (int)(((double)Y * 0.582199097) - (0.357070923 * (double)(Cr - 128)) - (0.172073364 * (double)(Cb - 128)));
|
||||
b = (int)(((double)Y * 0.582199097) + (0.886001587 * (double)(Cb - 128)));
|
||||
|
||||
r = SATURATE8(r);
|
||||
g = SATURATE8(g);
|
||||
b = SATURATE8(b);
|
||||
|
||||
return (r << 24) | (g << 16) | (b << 8) | 0;
|
||||
}
|
||||
|
||||
void decode_video_frame_task(struct hle_t* hle)
|
||||
{
|
||||
int data_ptr = *dmem_u32(hle, TASK_UCODE_DATA);
|
||||
|
||||
int pLuminance = *dram_u32(hle, data_ptr);
|
||||
int pCb = *dram_u32(hle, data_ptr + 4);
|
||||
int pCr = *dram_u32(hle, data_ptr + 8);
|
||||
int pDestination = *dram_u32(hle, data_ptr + 12);
|
||||
int nMovieWidth = *dram_u32(hle, data_ptr + 16);
|
||||
int nMovieHeight = *dram_u32(hle, data_ptr + 20);
|
||||
#if 0 /* unused, but keep it for documentation purpose */
|
||||
int nRowsPerDMEM = *dram_u32(hle, data_ptr + 24);
|
||||
int nDMEMPerFrame = *dram_u32(hle, data_ptr + 28);
|
||||
int nLengthSkipCount = *dram_u32(hle, data_ptr + 32);
|
||||
#endif
|
||||
int nScreenDMAIncrement = *dram_u32(hle, data_ptr + 36);
|
||||
|
||||
int i, j;
|
||||
uint8_t Y, Cb, Cr;
|
||||
uint32_t pixel;
|
||||
int pY_1st_row, pY_2nd_row, pDest_1st_row, pDest_2nd_row;
|
||||
|
||||
for (i = 0; i < nMovieHeight; i += 2)
|
||||
{
|
||||
pY_1st_row = pLuminance;
|
||||
pY_2nd_row = pLuminance + nMovieWidth;
|
||||
pDest_1st_row = pDestination;
|
||||
pDest_2nd_row = pDestination + (nScreenDMAIncrement >> 1);
|
||||
|
||||
for (j = 0; j < nMovieWidth; j += 2)
|
||||
{
|
||||
dram_load_u8(hle, (uint8_t*)&Cb, pCb++, 1);
|
||||
dram_load_u8(hle, (uint8_t*)&Cr, pCr++, 1);
|
||||
|
||||
/*1st row*/
|
||||
dram_load_u8(hle, (uint8_t*)&Y, pY_1st_row++, 1);
|
||||
pixel = YCbCr_to_RGBA(Y, Cb, Cr);
|
||||
dram_store_u32(hle, &pixel, pDest_1st_row, 1);
|
||||
pDest_1st_row += 4;
|
||||
|
||||
dram_load_u8(hle, (uint8_t*)&Y, pY_1st_row++, 1);
|
||||
pixel = YCbCr_to_RGBA(Y, Cb, Cr);
|
||||
dram_store_u32(hle, &pixel, pDest_1st_row, 1);
|
||||
pDest_1st_row += 4;
|
||||
|
||||
/*2nd row*/
|
||||
dram_load_u8(hle, (uint8_t*)&Y, pY_2nd_row++, 1);
|
||||
pixel = YCbCr_to_RGBA(Y, Cb, Cr);
|
||||
dram_store_u32(hle, &pixel, pDest_2nd_row, 1);
|
||||
pDest_2nd_row += 4;
|
||||
|
||||
dram_load_u8(hle, (uint8_t*)&Y, pY_2nd_row++, 1);
|
||||
pixel = YCbCr_to_RGBA(Y, Cb, Cr);
|
||||
dram_store_u32(hle, &pixel, pDest_2nd_row, 1);
|
||||
pDest_2nd_row += 4;
|
||||
}
|
||||
|
||||
pLuminance += (nMovieWidth << 1);
|
||||
pDestination += nScreenDMAIncrement;
|
||||
}
|
||||
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
||||
void fill_video_double_buffer_task(struct hle_t* hle)
|
||||
{
|
||||
int data_ptr = *dmem_u32(hle, TASK_UCODE_DATA);
|
||||
|
||||
int pSrc = *dram_u32(hle, data_ptr);
|
||||
int pDest = *dram_u32(hle, data_ptr + 0x4);
|
||||
int width = *dram_u32(hle, data_ptr + 0x8) >> 1;
|
||||
int height = *dram_u32(hle, data_ptr + 0x10) << 1;
|
||||
int stride = *dram_u32(hle, data_ptr + 0x1c) >> 1;
|
||||
|
||||
assert((*dram_u32(hle, data_ptr + 0x28) >> 16) == 0x8000);
|
||||
|
||||
#if 0 /* unused, but keep it for documentation purpose */
|
||||
int arg3 = *dram_u32(hle, data_ptr + 0xc);
|
||||
int arg5 = *dram_u32(hle, data_ptr + 0x14);
|
||||
int arg6 = *dram_u32(hle, data_ptr + 0x18);
|
||||
#endif
|
||||
|
||||
int i, j;
|
||||
int r, g, b;
|
||||
uint32_t pixel, pixel1, pixel2;
|
||||
|
||||
for(i = 0; i < height; i++)
|
||||
{
|
||||
for(j = 0; j < width; j=j+4)
|
||||
{
|
||||
pixel1 = *dram_u32(hle, pSrc+j);
|
||||
pixel2 = *dram_u32(hle, pDest+j);
|
||||
|
||||
r = (((pixel1 >> 24) & 0xff) + ((pixel2 >> 24) & 0xff)) >> 1;
|
||||
g = (((pixel1 >> 16) & 0xff) + ((pixel2 >> 16) & 0xff)) >> 1;
|
||||
b = (((pixel1 >> 8) & 0xff) + ((pixel2 >> 8) & 0xff)) >> 1;
|
||||
|
||||
pixel = (r << 24) | (g << 16) | (b << 8) | 0;
|
||||
|
||||
dram_store_u32(hle, &pixel, pDest+j, 1);
|
||||
}
|
||||
pSrc += stride;
|
||||
pDest += stride;
|
||||
}
|
||||
|
||||
rsp_break(hle, SP_STATUS_TASKDONE);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Mupen64plus-rsp-hle - ucodes.h *
|
||||
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
|
||||
* Mupen64Plus homepage: https://mupen64plus.org/ *
|
||||
* Copyright (C) 2014 Bobby Smiles *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
@ -24,8 +24,23 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CACHED_UCODES_MAX_SIZE 16
|
||||
|
||||
struct hle_t;
|
||||
|
||||
typedef void(*ucode_func_t)(struct hle_t* hle);
|
||||
|
||||
struct ucode_info_t {
|
||||
uint32_t uc_start;
|
||||
uint32_t uc_dstart;
|
||||
uint16_t uc_dsize;
|
||||
ucode_func_t uc_pfunc;
|
||||
};
|
||||
|
||||
struct cached_ucodes_t {
|
||||
struct ucode_info_t infos[CACHED_UCODES_MAX_SIZE];
|
||||
int count;
|
||||
};
|
||||
|
||||
/* cic_x105 ucode */
|
||||
void cicx105_ucode(struct hle_t* hle);
|
||||
@ -126,7 +141,8 @@ void alist_process_nead_oot (struct hle_t* hle);
|
||||
void alist_process_nead_mm (struct hle_t* hle);
|
||||
void alist_process_nead_mmb (struct hle_t* hle);
|
||||
void alist_process_nead_ac (struct hle_t* hle);
|
||||
|
||||
void alist_process_nead_mats(struct hle_t* hle);
|
||||
void alist_process_nead_efz (struct hle_t* hle);
|
||||
|
||||
/* mp3 ucode */
|
||||
void mp3_task(struct hle_t* hle, unsigned int index, uint32_t address);
|
||||
@ -144,6 +160,12 @@ void jpeg_decode_OB(struct hle_t* hle);
|
||||
|
||||
/* Resident evil 2 ucode */
|
||||
void resize_bilinear_task(struct hle_t* hle);
|
||||
void decode_video_frame_task(struct hle_t* hle);
|
||||
void fill_video_double_buffer_task(struct hle_t* hle);
|
||||
|
||||
/* hvqm2 ucode */
|
||||
void hvqm2_decode_sp1_task(struct hle_t* hle);
|
||||
void hvqm2_decode_sp2_task(struct hle_t* hle);
|
||||
|
||||
#endif
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user