summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/nosefart/nes6502.c222
-rw-r--r--contrib/nosefart/nes6502.h25
-rw-r--r--contrib/nosefart/nes_apu.c102
-rw-r--r--contrib/nosefart/nes_apu.h12
-rw-r--r--contrib/nosefart/nsf.c769
-rw-r--r--contrib/nosefart/nsf.h92
-rw-r--r--contrib/nosefart/types.h31
-rw-r--r--contrib/nosefart/version.h11
8 files changed, 989 insertions, 275 deletions
diff --git a/contrib/nosefart/nes6502.c b/contrib/nosefart/nes6502.c
index f1ca80af4..d84bae912 100644
--- a/contrib/nosefart/nes6502.c
+++ b/contrib/nosefart/nes6502.c
@@ -1117,8 +1117,17 @@
*/
/* register push/pull */
-#define PUSH(value) stack_page[S--] = (uint8) (value)
-#define PULL() stack_page[++S]
+#ifdef NES6502_MEM_ACCESS_CTRL
+
+# define PUSH(value) stack_push((S--),(value))
+# define PULL() stack_pull((++S))
+
+#else
+
+# define PUSH(value) stack_page[S--] = (uint8) (value)
+# define PULL() stack_page[++S]
+
+#endif /* #ifdef NES6502_MEM_ACCESS_CTRL */
/* Sets the Z and N flags based on given data, taken from precomputed table */
#define SET_NZ_FLAGS(value) P &= ~(N_FLAG | Z_FLAG); \
@@ -1166,50 +1175,194 @@ static uint8 *nes6502_banks[NES6502_NUMBANKS];
static uint8 *ram = NULL;
static uint8 *stack_page = NULL;
+/* access flag for memory
+ * $$$ ben : I add this for the playing time calculation.
+ * Only if compiled with NES6502_MEM_ACCESS.
+ */
+#ifdef NES6502_MEM_ACCESS_CTRL
+
+uint8 *acc_nes6502_banks[NES6502_NUMBANKS];
+static uint8 *acc_ram = NULL;
+static uint8 *acc_stack_page = NULL;
+uint8 nes6502_mem_access = 0;
+
+/* $$$ ben :
+ * Set memory access check flags, and store ORed frame global check
+ * for music time calculation.
+ */
+static void chk_mem_access(uint8 * access, int flags)
+{
+ uint8 oldchk = * access;
+ if ((oldchk&flags) != flags) {
+ nes6502_mem_access |= flags;
+ *access = oldchk|flags;
+ }
+}
+
+INLINE void stack_push(uint8 s, uint8 v)
+{
+ chk_mem_access(acc_stack_page+s, NES6502_WRITE_ACCESS);
+ stack_page[s] = v;
+}
+
+INLINE uint8 stack_pull(uint8 s)
+{
+ chk_mem_access(acc_stack_page+s, NES6502_READ_ACCESS);
+ return stack_page[s];
+}
+
+INLINE uint8 zp_read(register uint32 addr)
+{
+ chk_mem_access(acc_ram+addr, NES6502_READ_ACCESS);
+ return ram[addr];
+}
+
+INLINE void zp_write(register uint32 addr, uint8 v)
+{
+ chk_mem_access(acc_ram+addr, NES6502_WRITE_ACCESS);
+ ram[addr] = v;
+}
+
+#define ZP_READ(addr) zp_read((addr))
+#define ZP_WRITE(addr, value) zp_write((addr),(value))
+
+#define bank_readbyte(address) _bank_readbyte((address), NES6502_READ_ACCESS)
+#define bank_readbyte_pc(address) _bank_readbyte((address), NES6502_EXE_ACCESS)
+
+#else
+# define chk_mem_access(access, flags)
/*
** Zero-page helper macros
*/
-
#define ZP_READ(addr) ram[(addr)]
#define ZP_WRITE(addr, value) ram[(addr)] = (uint8) (value)
+#define bank_readbyte(address) _bank_readbyte((address))
+#define bank_readbyte_pc(address) _bank_readbyte((address))
+
+#endif /* #ifdef NES6502_MEM_ACCESS_CTRL */
-INLINE uint8 bank_readbyte(register uint32 address)
+#ifdef NES6502_MEM_ACCESS_CTRL
+int max_access[NES6502_NUMBANKS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+INLINE uint8 _bank_readbyte(register uint32 address, const uint8 flags)
+#else
+INLINE uint8 _bank_readbyte(register uint32 address)
+#endif
{
ASSERT(nes6502_banks[address >> NES6502_BANKSHIFT]);
+
+#ifdef NES6502_MEM_ACCESS_CTRL
+ //printf("chk_mem_access(acc_nes6502_banks[%d] + %d, %d)\n", address>>NES6502_BANKSHIFT, address & NES6502_BANKMASK, flags);
+
+ if((address & NES6502_BANKMASK) > max_access[address>>NES6502_BANKSHIFT])
+ {
+ max_access[address>>NES6502_BANKSHIFT] = address & NES6502_BANKMASK;
+ //printf("max_access[%d] increased to %d\n", address>>NES6502_BANKSHIFT, max_access[address>>NES6502_BANKSHIFT]);
+ }
+#endif
+ chk_mem_access(acc_nes6502_banks[address>>NES6502_BANKSHIFT]
+ + (address & NES6502_BANKMASK),
+ flags);
+
return nes6502_banks[address >> NES6502_BANKSHIFT][address & NES6502_BANKMASK];
}
+
INLINE void bank_writebyte(register uint32 address, register uint8 value)
{
ASSERT(nes6502_banks[address >> NES6502_BANKSHIFT]);
+
+#ifdef NES6502_MEM_ACCESS_CTRL
+ //printf("chk_mem_access(acc_nes6502_banks[%d] + %d, %d)\n", address>>NES6502_BANKSHIFT, address & NES6502_BANKMASK, NES6502_WRITE_ACCESS);
+
+ if((address & NES6502_BANKMASK) > max_access[address>>NES6502_BANKSHIFT])
+ {
+ max_access[address>>NES6502_BANKSHIFT] = address & NES6502_BANKMASK;
+ //printf("max_access[%d] increased to %d\n", address>>NES6502_BANKSHIFT, max_access[address>>NES6502_BANKSHIFT]);
+ }
+#endif
+
+ chk_mem_access(acc_nes6502_banks[address>>NES6502_BANKSHIFT]
+ + (address & NES6502_BANKMASK),
+ NES6502_WRITE_ACCESS);
+
nes6502_banks[address >> NES6502_BANKSHIFT][address & NES6502_BANKMASK] = value;
}
+/* Read a 16bit word */
+#define READ_SNES_16(bank,offset) \
+(\
+ (offset) [ (uint8 *) (bank) ] |\
+ ((unsigned int)( ((offset)+1) [ (uint8 *) (bank) ] ) << 8)\
+)
+
INLINE uint32 zp_address(register uint8 address)
{
- uint8 *x = ram + address;
- return (x[1] << 8) | x[0];
+ chk_mem_access(acc_ram+address, NES6502_READ_ACCESS);
+ chk_mem_access(acc_ram+address+1, NES6502_READ_ACCESS);
+
+#if defined (HOST_LITTLE_ENDIAN) && defined(HOST_UNALIGN_WORD)
+ /* TODO: this fails if src address is $xFFF */
+ /* TODO: this fails if host architecture doesn't support byte alignment */
+ /* $$$ ben : DONE */
+ return (uint32) (*(uint16 *)(ram + address));
+#elif defined(TARGET_CPU_PPC)
+ return __lhbrx(ram, address);
+#else
+ return READ_SNES_16(ram,address);
+/* uint32 x = (uint32) *(uint16 *)(ram + address); */
+/* return (x << 8) | (x >> 8); */
+//#endif /* TARGET_CPU_PPC */
+#endif /* HOST_LITTLE_ENDIAN */
}
INLINE uint32 bank_readaddress(register uint32 address)
{
- uint8 *x = nes6502_banks[address >> NES6502_BANKSHIFT] + (address & NES6502_BANKMASK);
- return (x[1] << 8) | x[0];
+
+#ifdef NES6502_MEM_ACCESS_CTRL
+ {
+ const unsigned int offset = address & NES6502_BANKMASK;
+ uint8 * addr = acc_nes6502_banks[address >> NES6502_BANKSHIFT];
+ chk_mem_access(addr+offset+0, NES6502_READ_ACCESS);
+ chk_mem_access(addr+offset+1, NES6502_READ_ACCESS);
+ }
+#endif
+
+#if defined (HOST_LITTLE_ENDIAN) && defined(HOST_UNALIGN_WORD)
+ /* TODO: this fails if src address is $xFFF */
+ /* TODO: this fails if host architecture doesn't support byte alignment */
+ /* $$$ ben : DONE */
+ return (uint32) (*(uint16 *)(nes6502_banks[address >> NES6502_BANKSHIFT] + (address & NES6502_BANKMASK)));
+#elif defined(TARGET_CPU_PPC)
+ return __lhbrx(nes6502_banks[address >> NES6502_BANKSHIFT], address & NES6502_BANKMASK);
+#else
+ {
+ const unsigned int offset = address & NES6502_BANKMASK;
+ return READ_SNES_16(nes6502_banks[address >> NES6502_BANKSHIFT], offset);
+ }
+/* uint32 x = (uint32) *(uint16 *)(nes6502_banks[address >> NES6502_BANKSHIFT] + (address & NES6502_BANKMASK)); */
+/* return (x << 8) | (x >> 8); */
+//#endif /* TARGET_CPU_PPC */
+#endif /* HOST_LITTLE_ENDIAN */
}
+
/* read a byte of 6502 memory */
static uint8 mem_read(uint32 address)
{
/* TODO: following cases are N2A03-specific */
/* RAM */
- if (address < 0x800)
- return ram[address];
+ if (address < 0x800) {
+ chk_mem_access(acc_ram + address, NES6502_READ_ACCESS);
+ return ram[address];
+ }
/* always paged memory */
// else if (address >= 0x6000)
- else if (address >= 0x8000)
- return bank_readbyte(address);
+ else if (address >= 0x8000) {
+ return bank_readbyte(address);
+ }
/* check memory range handlers */
else
{
@@ -1230,6 +1383,7 @@ static void mem_write(uint32 address, uint8 value)
/* RAM */
if (address < 0x800)
{
+ chk_mem_access(acc_ram + address, NES6502_WRITE_ACCESS);
ram[address] = value;
return;
}
@@ -1258,11 +1412,19 @@ void nes6502_setcontext(nes6502_context *cpu)
ASSERT(cpu);
/* Set the page pointers */
- for (loop = 0; loop < NES6502_NUMBANKS; loop++)
+ for (loop = 0; loop < NES6502_NUMBANKS; loop++) {
nes6502_banks[loop] = cpu->mem_page[loop];
+#ifdef NES6502_MEM_ACCESS_CTRL
+ acc_nes6502_banks[loop] = cpu->acc_mem_page[loop];
+#endif
+ }
ram = nes6502_banks[0]; /* quicker zero-page/RAM references */
stack_page = ram + STACK_OFFSET;
+#ifdef NES6502_MEM_ACCESS_CTRL
+ acc_ram = acc_nes6502_banks[0]; /* quicker zero-page/RAM references */
+ acc_stack_page = acc_ram + STACK_OFFSET;
+#endif
pmem_read = cpu->read_handler;
pmem_write = cpu->write_handler;
@@ -1283,8 +1445,12 @@ void nes6502_getcontext(nes6502_context *cpu)
int loop;
/* Set the page pointers */
- for (loop = 0; loop < NES6502_NUMBANKS; loop++)
+ for (loop = 0; loop < NES6502_NUMBANKS; loop++) {
cpu->mem_page[loop] = nes6502_banks[loop];
+#ifdef NES6502_MEM_ACCESS_CTRL
+ cpu->acc_mem_page[loop] = acc_nes6502_banks[loop];
+#endif
+ }
cpu->read_handler = pmem_read;
cpu->write_handler = pmem_write;
@@ -1333,7 +1499,14 @@ int nes6502_execute(int remaining_cycles)
GET_GLOBAL_REGS();
+#ifdef NES6502_MEM_ACCESS_CTRL
+ /* reset global memory access for this execute loop. */
+ nes6502_mem_access = 0;
+#endif
+
/* Continue until we run out of cycles */
+
+
while (remaining_cycles > 0)
{
instruction_cycles = 0;
@@ -1373,9 +1546,10 @@ int nes6502_execute(int remaining_cycles)
/* Fetch instruction */
//nes6502_disasm(PC, P, A, X, Y, S);
- opcode = bank_readbyte(PC++);
+ opcode = bank_readbyte_pc(PC++);
/* Execute instruction */
+
switch (opcode)
{
case 0x00: /* BRK */
@@ -2363,16 +2537,20 @@ void nes6502_setdma(int cycles)
dma_cycles += cycles;
}
+#ifdef NES6502_MEM_ACCESS_CTRL
+void nes6502_chk_mem_access(uint8 * access, int flags)
+{
+ chk_mem_access(access, flags);
+}
+#endif
+
/*
** $Log: nes6502.c,v $
-** Revision 1.2 2003/01/09 19:50:03 jkeil
-** NSF audio files were crashing on SPARC.
-**
-** - Define the correct HOST_ENDIAN for SPARC
-** - remove unaligned memory accesses
+** Revision 1.2 2003/05/01 22:34:19 benjihan
+** New NSF plugin
**
-** Revision 1.1 2003/01/08 07:04:35 tmmm
-** initial import of Nosefart sources
+** Revision 1.1 2003/04/08 20:53:00 ben
+** Adding more files...
**
** Revision 1.6 2000/07/04 04:50:07 matt
** minor change to includes
diff --git a/contrib/nosefart/nes6502.h b/contrib/nosefart/nes6502.h
index 9b9be5811..8713304ec 100644
--- a/contrib/nosefart/nes6502.h
+++ b/contrib/nosefart/nes6502.h
@@ -23,6 +23,9 @@
** $Id: nes6502.h,v 1.2 2003/12/05 15:55:01 f1rmb Exp $
*/
+/* straitm */
+#include "types.h"
+
/* NOTE: 16-bit addresses avoided like the plague: use 32-bit values
** wherever humanly possible
*/
@@ -47,6 +50,17 @@
#define NES6502_BANKMASK ((0x10000 / NES6502_NUMBANKS) - 1)
+/* Add memory access control flags. This is a ram shadow memory that holds
+ * for each memory bytes access flags for read, write and execute access.
+ * The nes6502_mem_access holds all new access (all mode all location). It is
+ * used to determine if the player has loop in playing time calculation.
+ */
+#ifdef NES6502_MEM_ACCESS_CTRL
+extern uint8 nes6502_mem_access;
+# define NES6502_READ_ACCESS 1
+# define NES6502_WRITE_ACCESS 2
+# define NES6502_EXE_ACCESS 4
+#endif /* #ifdef NES6502_MEM_ACCESS_CTRL */
/* P (flag) register bitmasks */
#define N_FLAG 0x80
@@ -87,7 +101,10 @@ typedef struct
typedef struct
{
- uint8 *mem_page[NES6502_NUMBANKS]; /* memory page pointers */
+ uint8 * mem_page[NES6502_NUMBANKS]; /* memory page pointers */
+#ifdef NES6502_MEM_ACCESS_CTRL
+ uint8 * acc_mem_page[NES6502_NUMBANKS]; /* memory access page pointer */
+#endif
nes6502_memread *read_handler;
nes6502_memwrite *write_handler;
int dma_cycles;
@@ -110,6 +127,12 @@ extern uint8 nes6502_getbyte(uint32 address);
extern uint32 nes6502_getcycles(boolean reset_flag);
extern void nes6502_setdma(int cycles);
+#ifdef NES6502_MEM_ACCESS_CTRL
+extern void nes6502_chk_mem_access(uint8 * access, int flags);
+#else
+#define nes6502_chk_mem_access(access,flags)
+#endif
+
/* Context get/set */
extern void nes6502_setcontext(nes6502_context *cpu);
extern void nes6502_getcontext(nes6502_context *cpu);
diff --git a/contrib/nosefart/nes_apu.c b/contrib/nosefart/nes_apu.c
index 4a2ed8f4d..474e2ca1a 100644
--- a/contrib/nosefart/nes_apu.c
+++ b/contrib/nosefart/nes_apu.c
@@ -57,6 +57,11 @@ static int8 noise_long_lut[APU_NOISE_32K];
static int8 noise_short_lut[APU_NOISE_93];
#endif /* !REALTIME_NOISE */
+/* $$$ ben : last error */
+#define SET_APU_ERROR(APU,X) \
+if (APU) (APU)->errstr = "apu: " X; else
+
+#define APU_MIX_ENABLE(BIT) (apu->mix_enable&(1<<(BIT)))
/* vblank length table used for rectangles, triangle, noise */
static const uint8 vbl_length[32] =
@@ -102,27 +107,32 @@ const int dmc_clocks[16] =
/* ratios of pos/neg pulse for rectangle waves */
static const int duty_lut[4] = { 2, 4, 8, 12 };
-#if 0 /* unused */
-static void apu_setcontext(apu_t *src_apu)
+
+void apu_setcontext(apu_t *src_apu)
{
apu = src_apu;
+ /* $$$ ben reset eoor string here. */
+ SET_APU_ERROR(apu,"no error");
}
-#endif
/*
** Simple queue routines
*/
#define APU_QEMPTY() (apu->q_head == apu->q_tail)
-static void apu_enqueue(apudata_t *d)
+static int apu_enqueue(apudata_t *d)
{
ASSERT(apu);
apu->queue[apu->q_head] = *d;
apu->q_head = (apu->q_head + 1) & APUQUEUE_MASK;
- if (APU_QEMPTY())
+ if (APU_QEMPTY()) {
log_printf("apu: queue overflow\n");
+ SET_APU_ERROR(apu,"queue overflow");
+ return -1;
+ }
+ return 0;
}
static apudata_t *apu_dequeue(void)
@@ -131,19 +141,32 @@ static apudata_t *apu_dequeue(void)
ASSERT(apu);
- if (APU_QEMPTY())
- log_printf("apu: queue empty\n");
-
+ if (APU_QEMPTY()) {
+ log_printf("apu: queue empty\n");
+ SET_APU_ERROR(apu,"queue empty");
+ /* $$$ ben : should return 0 ??? */
+ }
loc = apu->q_tail;
apu->q_tail = (apu->q_tail + 1) & APUQUEUE_MASK;
return &apu->queue[loc];
}
-void apu_setchan(int chan, boolean enabled)
+int apu_setchan(int chan, boolean enabled)
{
- ASSERT(apu);
- apu->mix_enable[chan] = enabled;
+ const unsigned int max = 6;
+ int old;
+
+ ASSERT(apu);
+ if ((unsigned int)chan >= max) {
+ SET_APU_ERROR(apu,"channel out of range");
+ return -1;
+ }
+ old = (apu->mix_enable>>chan) & 1;
+ if (enabled != (boolean)-1) {
+ apu->mix_enable = (apu->mix_enable & ~(1<<chan)) | ((!!enabled)<<chan);
+ }
+ return old;
}
/* emulation of the 15-bit shift register the
@@ -977,13 +1000,13 @@ void apu_process(void *buffer, int num_samples)
elapsed_cycles += APU_FROM_FIXED(apu->cycle_rate);
accum = 0;
- if (apu->mix_enable[0]) accum += apu_rectangle(&apu->rectangle[0]);
- if (apu->mix_enable[1]) accum += apu_rectangle(&apu->rectangle[1]);
- if (apu->mix_enable[2]) accum += apu_triangle(&apu->triangle);
- if (apu->mix_enable[3]) accum += apu_noise(&apu->noise);
- if (apu->mix_enable[4]) accum += apu_dmc(&apu->dmc);
+ if (APU_MIX_ENABLE(0)) accum += apu_rectangle(&apu->rectangle[0]);
+ if (APU_MIX_ENABLE(1)) accum += apu_rectangle(&apu->rectangle[1]);
+ if (APU_MIX_ENABLE(2)) accum += apu_triangle(&apu->triangle);
+ if (APU_MIX_ENABLE(3)) accum += apu_noise(&apu->noise);
+ if (APU_MIX_ENABLE(4)) accum += apu_dmc(&apu->dmc);
- if (apu->ext && apu->mix_enable[5]) accum += apu->ext->process();
+ if (apu->ext && APU_MIX_ENABLE(5)) accum += apu->ext->process();
/* do any filtering */
if (APU_FILTER_NONE != apu->filter_type)
@@ -1012,11 +1035,12 @@ void apu_process(void *buffer, int num_samples)
/* signed 16-bit output, unsigned 8-bit */
if (16 == apu->sample_bits) {
- *((int16 *) buffer) = (int16) accum;
- buffer = (int16 *) buffer + 1;
- } else {
- *((uint8 *) buffer) = (accum >> 8) ^ 0x80;
- buffer = (int8 *) buffer + 1;
+ *(int16 *)(buffer) = (int16) accum;
+ buffer += sizeof(int16);
+ }
+ else {
+ *(uint8 *)(buffer) = (accum >> 8) ^ 0x80;
+ buffer += sizeof(uint8);
}
}
@@ -1025,10 +1049,18 @@ void apu_process(void *buffer, int num_samples)
}
/* set the filter type */
-void apu_setfilter(int filter_type)
+/* $$$ ben :
+ * Add a get feature (filter_type == -1) and returns old filter type
+ */
+int apu_setfilter(int filter_type)
{
+ int old;
ASSERT(apu);
- apu->filter_type = filter_type;
+ old = apu->filter_type;
+ if (filter_type != -1) {
+ apu->filter_type = filter_type;
+ }
+ return old;
}
void apu_reset(void)
@@ -1057,7 +1089,7 @@ void apu_reset(void)
apu->ext->reset();
}
-static void apu_build_luts(int num_samples)
+void apu_build_luts(int num_samples)
{
int i;
@@ -1090,12 +1122,15 @@ static void apu_setactive(apu_t *active)
apu_t *apu_create(int sample_rate, int refresh_rate, int sample_bits, boolean stereo)
{
apu_t *temp_apu;
- int channel;
+/* int channel; */
temp_apu = malloc(sizeof(apu_t));
if (NULL == temp_apu)
return NULL;
+ /* $$$ ben : safety net, in case we forgot to init something */
+ memset(temp_apu,0,sizeof(apu_t));
+ SET_APU_ERROR(temp_apu,"no error");
temp_apu->sample_rate = sample_rate;
temp_apu->refresh_rate = refresh_rate;
temp_apu->sample_bits = sample_bits;
@@ -1114,8 +1149,9 @@ apu_t *apu_create(int sample_rate, int refresh_rate, int sample_bits, boolean st
apu_setactive(temp_apu);
apu_reset();
- for (channel = 0; channel < 6; channel++)
- apu_setchan(channel, TRUE);
+ temp_apu->mix_enable = 0x3F;
+/* for (channel = 0; channel < 6; channel++) */
+/* apu_setchan(channel, TRUE); */
apu_setfilter(APU_FILTER_LOWPASS);
@@ -1137,15 +1173,23 @@ void apu_destroy(apu_t *src_apu)
}
}
-void apu_setext(apu_t *src_apu, apuext_t *ext)
+int apu_setext(apu_t *src_apu, apuext_t *ext)
{
ASSERT(src_apu);
+ /* $$$ ben : seem cleaner like this */
+ if (src_apu->ext) {
+ src_apu->ext->shutdown();
+ }
+
src_apu->ext = ext;
/* initialize it */
if (src_apu->ext)
src_apu->ext->init();
+
+ /* $$$ ben : May be one day extension int () will return error code */
+ return 0;
}
/* this exists for external mixing routines */
diff --git a/contrib/nosefart/nes_apu.h b/contrib/nosefart/nes_apu.h
index 09b5842d9..9efbb560e 100644
--- a/contrib/nosefart/nes_apu.h
+++ b/contrib/nosefart/nes_apu.h
@@ -249,7 +249,7 @@ typedef struct apu_s
void *buffer; /* pointer to output buffer */
int num_samples;
- boolean mix_enable[6];
+ int mix_enable; /* $$$ben : should improve emulation */
int filter_type;
int32 cycle_rate;
@@ -260,6 +260,9 @@ typedef struct apu_s
void (*process)(void *buffer, int num_samples);
+ /* $$$ ben : last error string */
+ const char * errstr;
+
/* external sound chip */
apuext_t *ext;
} apu_t;
@@ -272,11 +275,11 @@ extern "C" {
/* Function prototypes */
extern apu_t *apu_create(int sample_rate, int refresh_rate, int sample_bits, boolean stereo);
extern void apu_destroy(apu_t *apu);
-extern void apu_setext(apu_t *apu, apuext_t *ext);
-extern void apu_setfilter(int filter_type);
+extern int apu_setext(apu_t *apu, apuext_t *ext);
+extern int apu_setfilter(int filter_type);
extern void apu_process(void *buffer, int num_samples);
extern void apu_reset(void);
-extern void apu_setchan(int chan, boolean enabled);
+extern int apu_setchan(int chan, boolean enabled);
extern int32 apu_getcyclerate(void);
extern apu_t *apu_getcontext(void);
@@ -286,6 +289,7 @@ extern void apu_write(uint32 address, uint8 value);
/* for visualization */
extern void apu_getpcmdata(void **data, int *num_samples, int *sample_bits);
+extern void apu_setcontext(apu_t *src_apu);
#ifdef __cplusplus
}
diff --git a/contrib/nosefart/nsf.c b/contrib/nosefart/nsf.c
index 6bbc96423..69f77546b 100644
--- a/contrib/nosefart/nsf.c
+++ b/contrib/nosefart/nsf.c
@@ -23,6 +23,7 @@
** $Id: nsf.c,v 1.4 2006/09/26 00:52:17 dgp85 Exp $
*/
+
#include <stdio.h>
#include <string.h>
#include "types.h"
@@ -50,25 +51,34 @@ static void nsf_setcontext(nsf_t *nsf)
static uint8 read_mirrored_ram(uint32 address)
{
- return cur_nsf->cpu->mem_page[0][address & 0x7FF];
+ nes6502_chk_mem_access(&cur_nsf->cpu->acc_mem_page[0][address & 0x7FF],
+ NES6502_READ_ACCESS);
+ return cur_nsf->cpu->mem_page[0][address & 0x7FF];
}
static void write_mirrored_ram(uint32 address, uint8 value)
{
- cur_nsf->cpu->mem_page[0][address & 0x7FF] = value;
+ nes6502_chk_mem_access(&cur_nsf->cpu->acc_mem_page[0][address & 0x7FF],
+ NES6502_WRITE_ACCESS);
+ cur_nsf->cpu->mem_page[0][address & 0x7FF] = value;
}
/* can be used for both banked and non-bankswitched NSFs */
static void nsf_bankswitch(uint32 address, uint8 value)
{
int cpu_page;
+ int roffset;
uint8 *offset;
cpu_page = address & 0x0F;
- offset = (cur_nsf->data - (cur_nsf->load_addr & 0x0FFF)) + (value << 12);
+ roffset = -(cur_nsf->load_addr & 0x0FFF) + ((int)value << 12);
+ offset = cur_nsf->data + roffset;
nes6502_getcontext(cur_nsf->cpu);
cur_nsf->cpu->mem_page[cpu_page] = offset;
+#ifdef NES6502_MEM_ACCESS_CTRL
+ cur_nsf->cpu->acc_mem_page[cpu_page] = offset + cur_nsf->length;
+#endif
nes6502_setcontext(cur_nsf->cpu);
}
@@ -248,6 +258,18 @@ static void nsf_inittune(nsf_t *nsf)
memset(nsf->cpu->mem_page[6], 0, 0x1000);
memset(nsf->cpu->mem_page[7], 0, 0x1000);
+#ifdef NES6502_MEM_ACCESS_CTRL
+ memset(nsf->cpu->acc_mem_page[0], 0, 0x800);
+ memset(nsf->cpu->acc_mem_page[6], 0, 0x1000);
+ memset(nsf->cpu->acc_mem_page[7], 0, 0x1000);
+ memset(nsf->data+nsf->length, 0, nsf->length);
+#endif
+ nsf->cur_frame = 0;
+/* nsf->last_access_frame = 0; */
+ nsf->cur_frame_end = !nsf->song_frames
+ ? 0
+ : nsf->song_frames[nsf->current_song];
+
if (nsf->bankswitched)
{
/* the first hack of the NSF spec! */
@@ -289,36 +311,81 @@ static void nsf_inittune(nsf_t *nsf)
void nsf_frame(nsf_t *nsf)
{
- //nsf_setcontext(nsf); /* future expansion =) */
+ // This is how Matthew Conte left it
+ //nsf_setcontext(nsf); /* future expansion =) */
+
+ // This was suggested by Arne Morten Kvarving, who says:
+/* Also, I fixed a bug that prevented Nosefart to play multiple tunes at
+ one time (actually it was just a few missing setcontext calls in the
+ playback routine, it had a nice TODO commented beside it. You had to set
+ the cpu and apu contexts not just the nsf context).
+
+ it will affect any player that tries to use nosefart to play more than one
+ tune at a time.
+*/
+ nsf_setcontext(nsf);
+ apu_setcontext(nsf->apu);
+ nes6502_setcontext(nsf->cpu);
/* one frame of NES processing */
nsf_setup_routine(nsf->play_addr, 0, 0);
nes6502_execute((int) NES_FRAME_CYCLES);
+
+ ++nsf->cur_frame;
+#if defined(NES6502_MEM_ACCESS_CTRL) && 0
+ if (nes6502_mem_access) {
+ uint32 sec =
+ (nsf->last_access_frame + nsf->playback_rate - 1) / nsf->playback_rate;
+ nsf->last_access_frame = nsf->cur_frame;
+ fprintf(stderr,"nsf : memory access [%x] at frame #%u [%u:%02u]\n",
+ nes6502_mem_access,
+ nsf->last_access_frame,
+ sec/60, sec%60);
+ }
+#endif
+
}
/* Deallocate memory */
-static void nes_shutdown(nsf_t *nsf)
+void nes_shutdown(nsf_t *nsf)
{
int i;
ASSERT(nsf);
-
+
if (nsf->cpu)
{
if (nsf->cpu->mem_page[0])
- free(nsf->cpu->mem_page[0]);
- for (i = 5; i <= 7; i++)
- {
- if (nsf->cpu->mem_page[i])
- free(nsf->cpu->mem_page[i]);
+ {
+ free(nsf->cpu->mem_page[0]);/*tracks 1 and 2 of lifeforce hang here.*/
+ }
+ for (i = 5; i <= 7; i++) {
+ if (nsf->cpu->mem_page[i])
+ {
+ free(nsf->cpu->mem_page[i]);
+ }
}
+
+#ifdef NES6502_MEM_ACCESS_CTRL
+ if (nsf->cpu->acc_mem_page[0])
+ {
+ free(nsf->cpu->acc_mem_page[0]);
+ }
+ for (i = 5; i <= 7; i++) {
+ if (nsf->cpu->acc_mem_page[i])
+ {
+ free(nsf->cpu->acc_mem_page[i]);
+ }
+ }
+#endif
free(nsf->cpu);
}
}
-void nsf_init(void)
+int nsf_init(void)
{
nes6502_init();
+ return 0;
}
/* Initialize NES CPU, hardware, etc. */
@@ -344,18 +411,27 @@ static int nsf_cpuinit(nsf_t *nsf)
return -1;
}
+#ifdef NES6502_MEM_ACCESS_CTRL
+ nsf->cpu->acc_mem_page[0] = malloc(0x800);
+ if (NULL == nsf->cpu->acc_mem_page[0])
+ return -1;
+ /* allocate some space for the NSF "player" MMC5 EXRAM, and WRAM */
+ for (i = 5; i <= 7; i++)
+ {
+ nsf->cpu->acc_mem_page[i] = malloc(0x1000);
+ if (NULL == nsf->cpu->acc_mem_page[i])
+ return -1;
+ }
+#endif
+
nsf->cpu->read_handler = nsf_readhandler;
nsf->cpu->write_handler = nsf_writehandler;
return 0;
}
-static void nsf_setup(nsf_t *nsf)
+static unsigned int nsf_playback_rate(nsf_t *nsf)
{
- int i;
-
- nsf->current_song = nsf->start_song;
-
if (nsf->pal_ntsc_bits & NSF_DEDICATED_PAL)
{
if (nsf->pal_speed)
@@ -370,9 +446,17 @@ static void nsf_setup(nsf_t *nsf)
else
nsf->playback_rate = 60; /* 60 Hz */
}
+ return 0;
+}
- nsf->bankswitched = FALSE;
+static void nsf_setup(nsf_t *nsf)
+{
+ int i;
+
+ nsf->current_song = nsf->start_song;
+ nsf_playback_rate(nsf);
+ nsf->bankswitched = FALSE;
for (i = 0; i < 8; i++)
{
if (nsf->bankswitch_info[i])
@@ -389,212 +473,551 @@ static void nsf_setup(nsf_t *nsf)
#define SWAP_16(x) (((uint16) x >> 8) | (((uint16) x & 0xFF) << 8))
#endif /* !HOST_LITTLE_ENDIAN */
-/* Load a ROM image into memory */
-nsf_t *nsf_load(char *filename, void *source, int length)
+/* $$$ ben : find extension. Should be OK with DOS, but not with some
+ * OS like RiscOS ... */
+static char * find_ext(char *fn)
{
- FILE *fp = NULL;
- char *new_fn = NULL;
- nsf_t *temp_nsf;
+ char * a, * b, * c;
+ a = strrchr(fn,'.');
+ b = strrchr(fn,'/');
+ c = strrchr(fn,'\\');
+ if (a <= b || a <= c) {
+ a = 0;
+ }
+ return a;
+}
- if (NULL == filename && NULL == source)
- return NULL;
-
- if (NULL == source)
- {
- fp = fopen(filename, "rb");
+/* $$$ ben : FILE loader */
+struct nsf_file_loader_t {
+ struct nsf_loader_t loader;
+ FILE *fp;
+ char * fname;
+ int name_allocated;
+};
- /* Didn't find the file? Maybe the .NSF extension was omitted */
- if (NULL == fp)
- {
- new_fn = malloc(strlen(filename) + 5);
- if (NULL == new_fn)
- return NULL;
- strcpy(new_fn, filename);
+static int nfs_open_file(struct nsf_loader_t *loader)
+{
+ struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+
+ floader->name_allocated = 0;
+ floader->fp = 0;
+ if (!floader->fname) {
+ return -1;
+ }
+ floader->fp = fopen(floader->fname,"rb");
+ if (!floader->fp) {
+ char * fname, * ext;
+ ext = find_ext(floader->fname);
+ if (ext) {
+ /* There was an extension, so we do not change it */
+ return -1;
+ }
+ fname = malloc(strlen(floader->fname) + 5);
+ if (!fname) {
+ return -1;
+ }
+ /* try with .nsf extension. */
+ strcpy(fname, floader->fname);
+ strcat(fname, ".nsf");
+ floader->fp = fopen(fname,"rb");
+ if (!floader->fp) {
+ free(fname);
+ return -1;
+ }
+ floader->fname = fname;
+ floader->name_allocated = 1;
+ }
+ return 0;
+}
- if (NULL == strrchr(new_fn, '.'))
- strcat(new_fn, ".nsf");
+static void nfs_close_file(struct nsf_loader_t *loader)
+{
+ struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+ if (floader->fp) {
+ fclose(floader->fp);
+ floader->fp = 0;
+ }
+ if (floader->fname && floader->name_allocated) {
+ free(floader->fname);
+ floader->fname = 0;
+ floader->name_allocated = 0;
+ }
+}
- fp = fopen(new_fn, "rb");
+static int nfs_read_file(struct nsf_loader_t *loader, void *data, int n)
+{
+ struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+ int r = fread(data, 1, n, floader->fp);
+ if (r >= 0) {
+ r = n-r;
+ }
+ return r;
+}
- if (NULL == fp)
- {
- log_printf("could not find file '%s'\n", new_fn);
- free(new_fn);
- return NULL;
- }
- }
- }
+static int nfs_length_file(struct nsf_loader_t *loader)
+{
+ struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+ long save, pos;
+ save = ftell(floader->fp);
+ fseek(floader->fp, 0, SEEK_END);
+ pos = ftell(floader->fp);
+ fseek(floader->fp, save, SEEK_SET);
+ return pos;
+}
- temp_nsf = malloc(sizeof(nsf_t));
- if (NULL == temp_nsf) {
- fclose(fp);
- free(new_fn);
- return NULL;
- }
+static int nfs_skip_file(struct nsf_loader_t *loader, int n)
+{
+ struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+ int r;
+ r = fseek(floader->fp, n, SEEK_CUR);
+ return r;
+}
- /* Read in the header */
- if (NULL == source)
- fread(temp_nsf, 1, NSF_HEADER_SIZE, fp);
- else
- memcpy(temp_nsf, source, NSF_HEADER_SIZE);
+static const char * nfs_fname_file(struct nsf_loader_t *loader)
+{
+ struct nsf_file_loader_t * floader = (struct nsf_file_loader_t *)loader;
+ return floader->fname ? floader->fname : "<null>";
+}
- if (memcmp(temp_nsf->id, NSF_MAGIC, 5))
- {
- if (NULL == source)
- {
- log_printf("%s is not an NSF format file\n", new_fn);
- fclose(fp);
- free(new_fn);
- }
- nsf_free(&temp_nsf);
- return NULL;
- }
+static struct nsf_file_loader_t nsf_file_loader = {
+ {
+ nfs_open_file,
+ nfs_close_file,
+ nfs_read_file,
+ nfs_length_file,
+ nfs_skip_file,
+ nfs_fname_file
+ },
+ 0,0,0
+};
+
+struct nsf_mem_loader_t {
+ struct nsf_loader_t loader;
+ uint8 *data;
+ unsigned long cur;
+ unsigned long len;
+ char fname[32];
+};
- /* fixup endianness */
- temp_nsf->load_addr = SWAP_16(temp_nsf->load_addr);
- temp_nsf->init_addr = SWAP_16(temp_nsf->init_addr);
- temp_nsf->play_addr = SWAP_16(temp_nsf->play_addr);
- temp_nsf->ntsc_speed = SWAP_16(temp_nsf->ntsc_speed);
- temp_nsf->pal_speed = SWAP_16(temp_nsf->pal_speed);
+static int nfs_open_mem(struct nsf_loader_t *loader)
+{
+ struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+ if (!mloader->data) {
+ return -1;
+ }
+ mloader->cur = 0;
+ sprintf(mloader->fname,"<mem(%p,%u)>",
+ mloader->data, (unsigned int)mloader->len);
+ return 0;
+}
- /* we're now at position 80h */
- if (NULL == source)
- {
- fseek(fp, 0, SEEK_END);
- temp_nsf->length = ftell(fp) - NSF_HEADER_SIZE;
- }
- else
- {
- temp_nsf->length = length - NSF_HEADER_SIZE;
- }
+static void nfs_close_mem(struct nsf_loader_t *loader)
+{
+ struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+ mloader->data = 0;
+ mloader->cur = 0;
+ mloader->len = 0;
+}
- /* Allocate NSF space, and load it up! */
- temp_nsf->data = malloc(temp_nsf->length);
- if (NULL == temp_nsf->data)
- {
- log_printf("error allocating memory for NSF data\n");
- nsf_free(&temp_nsf);
- return NULL;
- }
+static int nfs_read_mem(struct nsf_loader_t *loader, void *data, int n)
+{
+ struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+ int rem;
+ if (n <= 0) {
+ return n;
+ }
+ if (!mloader->data) {
+ return -1;
+ }
+ rem = mloader->len - mloader->cur;
+ if (rem > n) {
+ rem = n;
+ }
+ memcpy(data, mloader->data + mloader->cur, rem);
+ mloader->cur += rem;
+ return n - rem;
+}
- /* seek to end of header, read in data */
- if (NULL == source)
- {
- fseek(fp, NSF_HEADER_SIZE, SEEK_SET);
- fread(temp_nsf->data, temp_nsf->length, 1, fp);
+static int nfs_length_mem(struct nsf_loader_t *loader)
+{
+ struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+ return mloader->len;
+}
- fclose(fp);
+static int nfs_skip_mem(struct nsf_loader_t *loader, int n)
+{
+ struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+ unsigned long goal = mloader->cur + n;
+ mloader->cur = (goal > mloader->len) ? mloader->len : goal;
+ return goal - mloader->cur;
+}
- if (new_fn)
- free(new_fn);
- }
- else
- memcpy(temp_nsf->data, (uint8 *) source + NSF_HEADER_SIZE, length - NSF_HEADER_SIZE);
+static const char * nfs_fname_mem(struct nsf_loader_t *loader)
+{
+ struct nsf_mem_loader_t * mloader = (struct nsf_mem_loader_t *)loader;
+ return mloader->fname;
+}
+
+static struct nsf_mem_loader_t nsf_mem_loader = {
+ { nfs_open_mem, nfs_close_mem, nfs_read_mem, nfs_length_mem, nfs_skip_mem },
+ 0,0,0
+};
+
+nsf_t * nsf_load_extended(struct nsf_loader_t * loader)
+{
+ nsf_t *temp_nsf = 0;
+ int length;
+ char id[6];
+
+ struct {
+ uint8 magic[4]; /* always "NESM" */
+ uint8 type[4]; /* defines extension type */
+ uint8 size[4]; /* extension data size (this struct include) */
+ } nsf_file_ext;
+
+ /* no loader ! */
+ if (!loader) {
+ return NULL;
+ }
+
+ /* Open the "file" */
+ if (loader->open(loader) < 0) {
+ return NULL;
+ }
+
+ /* Get file size, and exit if there is not enough data for NSF header
+ * and more since it does not make sens to have header without data.
+ */
+ length = loader->length(loader);
+ /* For version 2, we do not need file length. just check error later. */
+#if 0
+ if (length <= NSF_HEADER_SIZE) {
+ log_printf("nsf : [%s] not an NSF format file\n",
+ loader->fname);
+ goto error;
+ }
+#endif
+
+ /* Read magic */
+ if (loader->read(loader, id, 5)) {
+ log_printf("nsf : [%s] error reading magic number\n",
+ loader->fname);
+ goto error;
+ }
+
+ /* Check magic */
+ if (memcmp(id, NSF_MAGIC, 5)) {
+ log_printf("nsf : [%s] is not an NSF format file\n",
+ loader->fname);
+ goto error;
+ }
+
+ /* $$$ ben : Now the file should be an NSF, we can start allocating.
+ * first : the nsf struct
+ */
+ temp_nsf = malloc(sizeof(nsf_t));
+
+ if (NULL == temp_nsf) {
+ log_printf("nsf : [%s] error allocating nsf header\n",
+ loader->fname);
+ goto error;
+ }
+ /* $$$ ben : safety net */
+ memset(temp_nsf,0,sizeof(nsf_t));
+ /* Copy magic ID */
+ memcpy(temp_nsf,id,5);
+
+ /* Read header (without MAGIC) */
+ if (loader->read(loader, (int8 *)temp_nsf+5, NSF_HEADER_SIZE - 5)) {
+ log_printf("nsf : [%s] error reading nsf header\n",
+ loader->fname);
+ goto error;
+ }
+
+ /* fixup endianness */
+ temp_nsf->load_addr = SWAP_16(temp_nsf->load_addr);
+ temp_nsf->init_addr = SWAP_16(temp_nsf->init_addr);
+ temp_nsf->play_addr = SWAP_16(temp_nsf->play_addr);
+ temp_nsf->ntsc_speed = SWAP_16(temp_nsf->ntsc_speed);
+ temp_nsf->pal_speed = SWAP_16(temp_nsf->pal_speed);
+
+ /* we're now at position 80h */
+
+
+ /* Here comes the specific codes for spec version 2 */
+
+ temp_nsf->length = 0;
+
+ if (temp_nsf->version > 1) {
+ /* Get specified data size in reserved field (3 bytes). */
+ temp_nsf->length = 0
+ + temp_nsf->reserved[0]
+ + (temp_nsf->reserved[1]<<8)
+ + (temp_nsf->reserved[2]<<16);
+
+ }
+ /* no specified size : try to guess with file length. */
+ if (!temp_nsf->length) {
+ temp_nsf->length = length - NSF_HEADER_SIZE;
+ }
+
+ if (temp_nsf->length <= 0) {
+ log_printf("nsf : [%s] not an NSF format file (missing data)\n",
+ loader->fname);
+ goto error;
+ }
+
+ /* Allocate NSF space, and load it up! */
+ {
+ int len = temp_nsf->length;
+#ifdef NES6502_MEM_ACCESS_CTRL
+ /* $$$ twice memory for access control shadow mem. */
+ len <<= 1;
+#endif
+ temp_nsf->data = malloc(len);
+ }
+ if (NULL == temp_nsf->data) {
+ log_printf("nsf : [%s] error allocating nsf data\n",
+ loader->fname);
+ goto error;
+ }
+
+ /* Read data */
+ if (loader->read(loader, temp_nsf->data, temp_nsf->length)) {
+ log_printf("nsf : [%s] error reading NSF data\n",
+ loader->fname);
+ goto error;
+ }
+
+ /* Here comes the second part of spec > 1 : get extension */
+ while (!loader->read(loader, &nsf_file_ext, sizeof(nsf_file_ext))
+ && !memcmp(nsf_file_ext.magic,id,4)) {
+ /* Got a NESM extension here. Checks for known extension type :
+ * right now, the only extension is "TIME" which give songs length.
+ * in frames.
+ */
+ int size;
+ size = 0
+ + nsf_file_ext.size[0]
+ + (nsf_file_ext.size[1] << 8)
+ + (nsf_file_ext.size[2] << 16)
+ + (nsf_file_ext.size[3] << 24);
+
+ if (size < sizeof(nsf_file_ext)) {
+ log_printf("nsf : [%s] corrupt extension size (%d)\n",
+ loader->fname, size);
+ /* Not a fatal error here. Just skip extension loading. */
+ break;
+ }
+ size -= sizeof(nsf_file_ext);
+
+ if (!temp_nsf->song_frames
+ && !memcmp(nsf_file_ext.type,"TIME", 4)
+ && !(size & 3)
+ && (size >= 2*4)
+ && (size <= 256*4)) {
+
+ uint8 tmp_time[256][4];
+ int tsongs = size >> 2;
+ int i;
+ int songs = temp_nsf->num_songs;
+
+ /* Add 1 for 0 which contains total time for all songs. */
+ ++songs;
+
+ if (loader->read(loader, tmp_time, size)) {
+ log_printf("nsf : [%s] missing extension data\n",
+ loader->fname);
+ /* Not a fatal error here. Just skip extension loading. */
+ break;
+ }
+ /* Alloc song_frames for songs (not tsongs). */
+ temp_nsf->song_frames = malloc(sizeof(*temp_nsf->song_frames) * songs);
+ if (!temp_nsf->song_frames) {
+ log_printf("nsf : [%s] extension alloc failed\n",
+ loader->fname);
+ /* Not a fatal error here. Just skip extension loading. */
+ break;
+ }
+
+ if (tsongs > songs) {
+ tsongs = songs;
+ }
+
+ /* Copy time info. */
+ for (i=0; i<tsongs; ++i) {
+ temp_nsf->song_frames[i] = 0
+ | tmp_time[i][0]
+ | (tmp_time[i][1] << 8)
+ | (tmp_time[i][2] << 16)
+ | (tmp_time[i][2] << 24);
+ }
+ /* Clear missing (safety net). */
+ for (; i<songs; ++i) {
+ temp_nsf->song_frames[i] = 0;
+ }
+ } else if (loader->skip(loader, size)) {
+ log_printf("nsf : [%s] extension skip failed\n",
+ loader->fname);
+ /* Not a fatal error here. Just skip extension loading. */
+ break;
+ }
+ }
+
+
+ /* Close "file" */
+ loader->close(loader);
+ loader = 0;
/* Set up some variables */
nsf_setup(temp_nsf);
-
temp_nsf->apu = NULL; /* just make sure */
- if (nsf_cpuinit(temp_nsf))
- {
- nsf_free(&temp_nsf);
- return NULL;
+ if (nsf_cpuinit(temp_nsf)) {
+ log_printf("nsf : error cpu init\n");
+ goto error;
}
-
return temp_nsf;
-}
-
-/* Free an NSF */
-void nsf_free(nsf_t **nsf)
-{
- if (*nsf)
- {
- if ((*nsf)->apu)
- apu_destroy((*nsf)->apu);
-
- nes_shutdown(*nsf);
- if ((*nsf)->data)
- free((*nsf)->data);
-
- free(*nsf);
+ /* $$$ ben : some people tell that goto are not clean. I am not agree with
+ * them. In most case, it allow to avoid code duplications, which are as
+ * most people know a source of error... Here we are sure of being clean
+ */
+ error:
+ if (loader) {
+ loader->close(loader);
+ }
+ if (temp_nsf) {
+ nsf_free(&temp_nsf);
}
+ return 0;
}
-void nsf_setchan(nsf_t *nsf, int chan, boolean enabled)
+/* Load a ROM image into memory */
+nsf_t *nsf_load(const char *filename, void *source, int length)
{
- if (nsf)
- {
- nsf_setcontext(nsf);
- apu_setchan(chan, enabled);
- }
+ struct nsf_loader_t * loader = 0;
+
+ /* $$$ ben : new loader */
+ if (filename) {
+ nsf_file_loader.fname = (char *)filename;
+ loader = &nsf_file_loader.loader;
+ } else {
+ nsf_mem_loader.data = source;
+ nsf_mem_loader.len = length;
+ nsf_mem_loader.fname[0] = 0;
+ loader = &nsf_mem_loader.loader;
+ }
+ return nsf_load_extended(loader);
}
-void nsf_playtrack(nsf_t *nsf, int track, int sample_rate, int sample_bits, boolean stereo)
+/* Free an NSF */
+void nsf_free(nsf_t **pnsf)
{
- ASSERT(nsf);
+ nsf_t *nsf;
- /* make this NSF the current context */
- nsf_setcontext(nsf);
+ if (!pnsf) {
+ return;
+ }
- /* create the APU */
- if (nsf->apu)
+ nsf = *pnsf;
+ /* $$$ ben : Don't see why passing a pointer to pointer
+ * is not to clear it :) */
+ *pnsf = 0;
+
+ if (nsf) {
+ if (nsf->apu)
apu_destroy(nsf->apu);
- nsf->apu = apu_create(sample_rate, nsf->playback_rate, sample_bits, stereo);
- if (NULL == nsf->apu)
- {
- nsf_free(&nsf);
- return;
- }
+ nes_shutdown(nsf);
+
+ if (nsf->data)
+ free(nsf->data);
- apu_setext(nsf->apu, nsf_getext(nsf));
+ if (nsf->song_frames)
+ free (nsf->song_frames);
- /* go ahead and init all the read/write handlers */
- build_address_handlers(nsf);
+ free(nsf);
+ }
+}
- /* convenience? */
- nsf->process = nsf->apu->process;
+int nsf_setchan(nsf_t *nsf, int chan, boolean enabled)
+{
+ if (!nsf)
+ return -1;
- nes6502_setcontext(nsf->cpu);
+ nsf_setcontext(nsf);
+ return apu_setchan(chan, enabled);
+}
- if (track > nsf->num_songs)
- track = nsf->num_songs;
- else if (track < 1)
- track = 1;
+int nsf_playtrack(nsf_t *nsf, int track, int sample_rate, int sample_bits,
+ boolean stereo)
+{
+ if (!nsf) {
+ return -1;
+ }
+
+ /* make this NSF the current context */
+ nsf_setcontext(nsf);
+
+ /* create the APU */
+ if (nsf->apu) {
+ apu_destroy(nsf->apu);
+ }
+
+ nsf->apu = apu_create(sample_rate, nsf->playback_rate, sample_bits, stereo);
+ if (NULL == nsf->apu)
+ {
+ /* $$$ ben : from my point of view this is not clean. Function should
+ * never destroy object it has not created...
+ */
+ /* nsf_free(&nsf); */
+ return -1;
+ }
- nsf->current_song = track;
+ apu_setext(nsf->apu, nsf_getext(nsf));
+
+ /* go ahead and init all the read/write handlers */
+ build_address_handlers(nsf);
+
+ /* convenience? */
+ nsf->process = nsf->apu->process;
+
+ nes6502_setcontext(nsf->cpu);
+
+ if (track > nsf->num_songs)
+ track = nsf->num_songs;
+ else if (track < 1)
+ track = 1;
+
+ nsf->current_song = track;
- apu_reset();
+ apu_reset();
+
+ nsf_inittune(nsf);
- nsf_inittune(nsf);
+ return nsf->current_song;
}
-void nsf_setfilter(nsf_t *nsf, int filter_type)
+int nsf_setfilter(nsf_t *nsf, int filter_type)
{
- if (nsf)
- {
- nsf_setcontext(nsf);
- apu_setfilter(filter_type);
- }
+ if (!nsf) {
+ return -1;
+ }
+ nsf_setcontext(nsf);
+ return apu_setfilter(filter_type);
}
/*
** $Log: nsf.c,v $
-** Revision 1.4 2006/09/26 00:52:17 dgp85
-** Free the filename string and close the file pointer when returning.
-**
-** Found by Coverity Scan.
-**
-** Revision 1.3 2003/08/25 21:51:43 f1rmb
-** Reduce GCC verbosity (various prototype declaration fixes). ffmpeg, wine and fft*post are untouched (fft: for now).
+** Revision 1.3 2003/05/01 22:34:20 benjihan
+** New NSF plugin
**
-** Revision 1.2 2003/01/09 18:36:40 jkeil
-** memcpy copies too much, corrupts malloc heap
+** Revision 1.2 2003/04/09 14:50:32 ben
+** Clean NSF api.
**
-** Revision 1.1 2003/01/08 07:04:35 tmmm
-** initial import of Nosefart sources
+** Revision 1.1 2003/04/08 20:53:00 ben
+** Adding more files...
**
** Revision 1.14 2000/07/05 14:54:45 matt
** fix for naughty Crystalis rip
diff --git a/contrib/nosefart/nsf.h b/contrib/nosefart/nsf.h
index 79ec1dbb6..79ce55c35 100644
--- a/contrib/nosefart/nsf.h
+++ b/contrib/nosefart/nsf.h
@@ -62,28 +62,29 @@ enum
{
NSF_FILTER_NONE,
NSF_FILTER_LOWPASS,
- NSF_FILTER_WEIGHTED
+ NSF_FILTER_WEIGHTED,
+ NSF_FILTER_MAX, /* $$$ ben : add this one for range chacking */
};
typedef struct nsf_s
{
/* NESM header */
- uint8 id[5]; /* NESM\x1A */
- uint8 version; /* spec version */
- uint8 num_songs; /* total num songs */
- uint8 start_song; /* first song */
- uint16 load_addr; /* loc to load code */
- uint16 init_addr; /* init call address */
- uint16 play_addr; /* play call address */
- uint8 song_name[32]; /* name of song */
- uint8 artist_name[32]; /* artist name */
- uint8 copyright[32]; /* copyright info */
- uint16 ntsc_speed; /* playback speed (if NTSC) */
- uint8 bankswitch_info[8]; /* initial code banking */
- uint16 pal_speed; /* playback speed (if PAL) */
- uint8 pal_ntsc_bits; /* NTSC/PAL determination bits */
- uint8 ext_sound_type; /* type of external sound gen. */
- uint8 reserved[4]; /* reserved */
+ uint8 id[5]; /* NESM\x1A */
+ uint8 version; /* spec version */
+ uint8 num_songs; /* total num songs */
+ uint8 start_song; /* first song */
+ uint16 load_addr; /* loc to load code */
+ uint16 init_addr; /* init call address */
+ uint16 play_addr; /* play call address */
+ uint8 song_name[32]; /* name of song */
+ uint8 artist_name[32]; /* artist name */
+ uint8 copyright[32]; /* copyright info */
+ uint16 ntsc_speed; /* playback speed (if NTSC) */
+ uint8 bankswitch_info[8]; /* initial code banking */
+ uint16 pal_speed; /* playback speed (if PAL) */
+ uint8 pal_ntsc_bits; /* NTSC/PAL determination bits */
+ uint8 ext_sound_type; /* type of external sound gen. */
+ uint8 reserved[4]; /* reserved */
/* things that the NSF player needs */
uint8 *data; /* actual NSF data */
@@ -92,6 +93,14 @@ typedef struct nsf_s
uint8 current_song; /* current song */
boolean bankswitched; /* is bankswitched? */
+ /* $$$ ben : Playing time ... */
+ uint32 cur_frame;
+ uint32 cur_frame_end;
+ uint32 * song_frames;
+
+ /* $$$ ben : Last error string */
+ const char * errstr;
+
/* CPU and APU contexts */
nes6502_context *cpu;
apu_t *apu;
@@ -100,30 +109,55 @@ typedef struct nsf_s
void (*process)(void *buffer, int num_samples);
} XINE_PACKED nsf_t;
+/* $$$ ben : Generic loader struct */
+struct nsf_loader_t {
+ /* Init and open. */
+ int (*open)(struct nsf_loader_t * loader);
+
+ /* Close and shutdown. */
+ void (*close) (struct nsf_loader_t * loader);
+
+ /* This function should return <0 on error, else the number of byte NOT read.
+ * that way a simple 0 test tell us if read was complete.
+ */
+ int (*read) (struct nsf_loader_t * loader, void *data, int n);
+
+ /* Get file length. */
+ int (*length) (struct nsf_loader_t * loader);
+
+ /* Skip n bytes. */
+ int (*skip) (struct nsf_loader_t * loader,int n);
+
+ /* Get filename (for debug). */
+ const char * (*fname) (struct nsf_loader_t * loader);
+
+};
+
/* Function prototypes */
-extern void nsf_init(void);
+extern int nsf_init(void);
-extern nsf_t *nsf_load(char *filename, void *source, int length);
+extern nsf_t * nsf_load_extended(struct nsf_loader_t * loader);
+extern nsf_t *nsf_load(const char *filename, void *source, int length);
extern void nsf_free(nsf_t **nsf_info);
-extern void nsf_playtrack(nsf_t *nsf, int track, int sample_rate, int sample_bits,
- boolean stereo);
+extern int nsf_playtrack(nsf_t *nsf, int track, int sample_rate,
+ int sample_bits, boolean stereo);
extern void nsf_frame(nsf_t *nsf);
-extern void nsf_setchan(nsf_t *nsf, int chan, boolean enabled);
-extern void nsf_setfilter(nsf_t *nsf, int filter_type);
+extern int nsf_setchan(nsf_t *nsf, int chan, boolean enabled);
+extern int nsf_setfilter(nsf_t *nsf, int filter_type);
#endif /* _NSF_H_ */
/*
** $Log: nsf.h,v $
-** Revision 1.3 2007/01/18 21:34:10 dgp85
-** __attribute__(packed) is used on the struct, not on its members.
+** Revision 1.3 2003/05/01 22:34:20 benjihan
+** New NSF plugin
**
-** Revision 1.2 2003/12/05 15:55:01 f1rmb
-** cleanup phase II. use xprintf when it's relevant, use xine_xmalloc when it's relevant too. Small other little fix (can't remember). Change few internal function prototype because it xine_t pointer need to be used if some xine's internal sections. NOTE: libdvd{nav,read} is still too noisy, i will take a look to made it quit, without invasive changes. To be continued...
+** Revision 1.2 2003/04/09 14:50:32 ben
+** Clean NSF api.
**
-** Revision 1.1 2003/01/08 07:04:35 tmmm
-** initial import of Nosefart sources
+** Revision 1.1 2003/04/08 20:53:00 ben
+** Adding more files...
**
** Revision 1.11 2000/07/04 04:59:24 matt
** removed DOS-specific stuff
diff --git a/contrib/nosefart/types.h b/contrib/nosefart/types.h
index 01f196035..c0293066c 100644
--- a/contrib/nosefart/types.h
+++ b/contrib/nosefart/types.h
@@ -31,10 +31,9 @@
#endif
/* Define this if running on little-endian (x86) systems */
-#ifdef WORDS_BIGENDIAN
-#undef HOST_LITTLE_ENDIAN
-#else
-#define HOST_LITTLE_ENDIAN
+
+#ifndef DCPLAYA
+# define HOST_LITTLE_ENDIAN
#endif
#ifdef __GNUC__
@@ -46,13 +45,25 @@
#endif
/* These should be changed depending on the platform */
-typedef char int8;
-typedef short int16;
-typedef int int32;
-typedef unsigned char uint8;
-typedef unsigned short uint16;
-typedef unsigned int uint32;
+
+
+#ifdef __BEOS__ /* added by Eli Dayan (for compiling under BeOS) */
+
+ /* use types in the BeOS Support Kit instead */
+ #include <be/support/SupportDefs.h>
+#elif defined (DCPLAYA) /* $$$ added by ben (for compiling with dcplaya) */
+# include <arch/types.h>
+#else
+ typedef char int8;
+ typedef short int16;
+ typedef int int32;
+
+ typedef unsigned char uint8;
+ typedef unsigned short uint16;
+ typedef unsigned int uint32;
+
+#endif
typedef uint8 boolean;
diff --git a/contrib/nosefart/version.h b/contrib/nosefart/version.h
index d7dcb2e5d..38d69c61c 100644
--- a/contrib/nosefart/version.h
+++ b/contrib/nosefart/version.h
@@ -20,7 +20,7 @@
** version.h
**
** Program name / version definitions
-** $Id: version.h,v 1.2 2003/12/05 15:55:01 f1rmb Exp $
+** $Id: version.h,v 1.1 2003/04/08 20:46:46 ben Exp $
*/
#ifndef _VERSION_H_
@@ -32,17 +32,14 @@
#define APP_STRING "Nofrendo"
#endif /* NSF_PLAYER */
-#define APP_VERSION "1.92"
+#define APP_VERSION "2.3-mls"
#endif /* _VERSION_H_ */
/*
** $Log: version.h,v $
-** Revision 1.2 2003/12/05 15:55:01 f1rmb
-** cleanup phase II. use xprintf when it's relevant, use xine_xmalloc when it's relevant too. Small other little fix (can't remember). Change few internal function prototype because it xine_t pointer need to be used if some xine's internal sections. NOTE: libdvd{nav,read} is still too noisy, i will take a look to made it quit, without invasive changes. To be continued...
-**
-** Revision 1.1 2003/01/08 07:04:36 tmmm
-** initial import of Nosefart sources
+** Revision 1.1 2003/04/08 20:46:46 ben
+** add new input for NES music file.
**
** Revision 1.7 2000/07/04 04:46:55 matt
** updated version number