diff options
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/nosefart/nes6502.c | 222 | ||||
-rw-r--r-- | contrib/nosefart/nes6502.h | 25 | ||||
-rw-r--r-- | contrib/nosefart/nes_apu.c | 102 | ||||
-rw-r--r-- | contrib/nosefart/nes_apu.h | 12 | ||||
-rw-r--r-- | contrib/nosefart/nsf.c | 769 | ||||
-rw-r--r-- | contrib/nosefart/nsf.h | 92 | ||||
-rw-r--r-- | contrib/nosefart/types.h | 31 | ||||
-rw-r--r-- | contrib/nosefart/version.h | 11 |
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 |