2006-07-13 15:07:28 +00:00
|
|
|
/*
|
|
|
|
** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
|
|
|
|
**
|
|
|
|
**
|
|
|
|
** This program is free software; you can redistribute it and/or
|
|
|
|
** modify it under the terms of version 2 of the GNU Library General
|
|
|
|
** Public License as published by the Free Software Foundation.
|
|
|
|
**
|
|
|
|
** This program is distributed in the hope that it will be useful,
|
|
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
** Library General Public License for more details. To obtain a
|
|
|
|
** copy of the GNU Library General Public License, write to the Free
|
|
|
|
** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
**
|
|
|
|
** Any permitted reproduction of these routines, in whole or in part,
|
|
|
|
** must bear this legend.
|
|
|
|
**
|
|
|
|
**
|
|
|
|
** nsf.c
|
|
|
|
**
|
|
|
|
** NSF loading/saving related functions
|
|
|
|
** $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "types.h"
|
|
|
|
#include "nsf.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "nes6502.h"
|
|
|
|
#include "nes_apu.h"
|
|
|
|
#include "vrcvisnd.h"
|
|
|
|
#include "vrc7_snd.h"
|
|
|
|
#include "mmc5_snd.h"
|
|
|
|
#include "fds_snd.h"
|
|
|
|
|
|
|
|
/* TODO: bleh! should encapsulate in NSF */
|
|
|
|
#define MAX_ADDRESS_HANDLERS 32
|
|
|
|
static nes6502_memread nsf_readhandler[MAX_ADDRESS_HANDLERS];
|
|
|
|
static nes6502_memwrite nsf_writehandler[MAX_ADDRESS_HANDLERS];
|
|
|
|
|
|
|
|
static nsf_t *cur_nsf = NULL;
|
|
|
|
|
|
|
|
static void
|
|
|
|
nsf_setcontext (nsf_t * nsf)
|
|
|
|
{
|
|
|
|
ASSERT (nsf);
|
|
|
|
cur_nsf = nsf;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint8
|
|
|
|
read_mirrored_ram (uint32 address)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* can be used for both banked and non-bankswitched NSFs */
|
|
|
|
static void
|
|
|
|
nsf_bankswitch (uint32 address, uint8 value)
|
|
|
|
{
|
|
|
|
int cpu_page;
|
|
|
|
uint8 *offset;
|
|
|
|
|
|
|
|
cpu_page = address & 0x0F;
|
|
|
|
offset = (cur_nsf->data - (cur_nsf->load_addr & 0x0FFF)) + (value << 12);
|
|
|
|
|
|
|
|
nes6502_getcontext (cur_nsf->cpu);
|
|
|
|
cur_nsf->cpu->mem_page[cpu_page] = offset;
|
|
|
|
nes6502_setcontext (cur_nsf->cpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
static nes6502_memread default_readhandler[] = {
|
|
|
|
{0x0800, 0x1FFF, read_mirrored_ram},
|
|
|
|
{0x4000, 0x4017, apu_read},
|
|
|
|
{-1, -1, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
static nes6502_memwrite default_writehandler[] = {
|
|
|
|
{0x0800, 0x1FFF, write_mirrored_ram},
|
|
|
|
{0x4000, 0x4017, apu_write},
|
|
|
|
{0x5FF6, 0x5FFF, nsf_bankswitch},
|
|
|
|
{-1, -1, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
static uint8
|
|
|
|
invalid_read (uint32 address)
|
|
|
|
{
|
|
|
|
#ifdef NOFRENDO_DEBUG
|
|
|
|
log_printf ("filthy NSF read from $%04X\n", address);
|
|
|
|
#endif /* NOFRENDO_DEBUG */
|
|
|
|
|
|
|
|
return 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
invalid_write (uint32 address, uint8 value)
|
|
|
|
{
|
|
|
|
#ifdef NOFRENDO_DEBUG
|
|
|
|
log_printf ("filthy NSF tried to write $%02X to $%04X\n", value, address);
|
|
|
|
#endif /* NOFRENDO_DEBUG */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set up the address handlers that the CPU uses */
|
|
|
|
static void
|
|
|
|
build_address_handlers (nsf_t * nsf)
|
|
|
|
{
|
|
|
|
int count, num_handlers;
|
|
|
|
|
|
|
|
memset (nsf_readhandler, 0, sizeof (nsf_readhandler));
|
|
|
|
memset (nsf_writehandler, 0, sizeof (nsf_writehandler));
|
|
|
|
|
|
|
|
num_handlers = 0;
|
|
|
|
for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++) {
|
|
|
|
if (NULL == default_readhandler[count].read_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
memcpy (&nsf_readhandler[num_handlers], &default_readhandler[count],
|
|
|
|
sizeof (nes6502_memread));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nsf->apu->ext) {
|
|
|
|
if (NULL != nsf->apu->ext->mem_read) {
|
|
|
|
for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS;
|
|
|
|
count++, num_handlers++) {
|
|
|
|
if (NULL == nsf->apu->ext->mem_read[count].read_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
memcpy (&nsf_readhandler[num_handlers], &nsf->apu->ext->mem_read[count],
|
|
|
|
sizeof (nes6502_memread));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* catch-all for bad reads */
|
|
|
|
nsf_readhandler[num_handlers].min_range = 0x2000; /* min address */
|
|
|
|
nsf_readhandler[num_handlers].max_range = 0x5BFF; /* max address */
|
|
|
|
nsf_readhandler[num_handlers].read_func = invalid_read; /* handler */
|
|
|
|
num_handlers++;
|
|
|
|
nsf_readhandler[num_handlers].min_range = -1;
|
|
|
|
nsf_readhandler[num_handlers].max_range = -1;
|
|
|
|
nsf_readhandler[num_handlers].read_func = NULL;
|
|
|
|
num_handlers++;
|
|
|
|
ASSERT (num_handlers <= MAX_ADDRESS_HANDLERS);
|
|
|
|
|
|
|
|
num_handlers = 0;
|
|
|
|
for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS; count++, num_handlers++) {
|
|
|
|
if (NULL == default_writehandler[count].write_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
memcpy (&nsf_writehandler[num_handlers], &default_writehandler[count],
|
|
|
|
sizeof (nes6502_memwrite));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nsf->apu->ext) {
|
|
|
|
if (NULL != nsf->apu->ext->mem_write) {
|
|
|
|
for (count = 0; num_handlers < MAX_ADDRESS_HANDLERS;
|
|
|
|
count++, num_handlers++) {
|
|
|
|
if (NULL == nsf->apu->ext->mem_write[count].write_func)
|
|
|
|
break;
|
|
|
|
|
|
|
|
memcpy (&nsf_writehandler[num_handlers],
|
|
|
|
&nsf->apu->ext->mem_write[count], sizeof (nes6502_memwrite));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* catch-all for bad writes */
|
|
|
|
nsf_writehandler[num_handlers].min_range = 0x2000; /* min address */
|
|
|
|
nsf_writehandler[num_handlers].max_range = 0x5BFF; /* max address */
|
|
|
|
nsf_writehandler[num_handlers].write_func = invalid_write; /* handler */
|
|
|
|
num_handlers++;
|
|
|
|
/* protect region at $8000-$FFFF */
|
|
|
|
nsf_writehandler[num_handlers].min_range = 0x8000; /* min address */
|
|
|
|
nsf_writehandler[num_handlers].max_range = 0xFFFF; /* max address */
|
|
|
|
nsf_writehandler[num_handlers].write_func = invalid_write; /* handler */
|
|
|
|
num_handlers++;
|
|
|
|
nsf_writehandler[num_handlers].min_range = -1;
|
|
|
|
nsf_writehandler[num_handlers].max_range = -1;
|
|
|
|
nsf_writehandler[num_handlers].write_func = NULL;
|
|
|
|
num_handlers++;
|
|
|
|
ASSERT (num_handlers <= MAX_ADDRESS_HANDLERS);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NSF_ROUTINE_LOC 0x5000
|
|
|
|
|
|
|
|
/* sets up a simple loop that calls the desired routine and spins */
|
|
|
|
static void
|
|
|
|
nsf_setup_routine (uint32 address, uint8 a_reg, uint8 x_reg)
|
|
|
|
{
|
|
|
|
uint8 *mem;
|
|
|
|
|
|
|
|
nes6502_getcontext (cur_nsf->cpu);
|
|
|
|
mem =
|
|
|
|
cur_nsf->cpu->mem_page[NSF_ROUTINE_LOC >> 12] +
|
|
|
|
(NSF_ROUTINE_LOC & 0x0FFF);
|
|
|
|
|
|
|
|
/* our lovely 4-byte 6502 NSF player */
|
|
|
|
mem[0] = 0x20; /* JSR address */
|
|
|
|
mem[1] = address & 0xFF;
|
|
|
|
mem[2] = address >> 8;
|
|
|
|
mem[3] = 0xF2; /* JAM (cpu kill op) */
|
|
|
|
|
|
|
|
cur_nsf->cpu->pc_reg = NSF_ROUTINE_LOC;
|
|
|
|
cur_nsf->cpu->a_reg = a_reg;
|
|
|
|
cur_nsf->cpu->x_reg = x_reg;
|
|
|
|
cur_nsf->cpu->y_reg = 0;
|
|
|
|
cur_nsf->cpu->s_reg = 0xFF;
|
|
|
|
|
|
|
|
nes6502_setcontext (cur_nsf->cpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* retrieve any external soundchip driver */
|
|
|
|
static apuext_t *
|
|
|
|
nsf_getext (nsf_t * nsf)
|
|
|
|
{
|
|
|
|
switch (nsf->ext_sound_type) {
|
|
|
|
case EXT_SOUND_VRCVI:
|
|
|
|
return &vrcvi_ext;
|
|
|
|
|
|
|
|
case EXT_SOUND_VRCVII:
|
|
|
|
return &vrc7_ext;
|
|
|
|
|
|
|
|
case EXT_SOUND_FDS:
|
|
|
|
return &fds_ext;
|
|
|
|
|
|
|
|
case EXT_SOUND_MMC5:
|
|
|
|
return &mmc5_ext;
|
|
|
|
|
|
|
|
case EXT_SOUND_NAMCO106:
|
|
|
|
case EXT_SOUND_SUNSOFT_FME07:
|
|
|
|
case EXT_SOUND_NONE:
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nsf_inittune (nsf_t * nsf)
|
|
|
|
{
|
|
|
|
uint8 bank, x_reg;
|
|
|
|
uint8 start_bank, num_banks;
|
|
|
|
|
|
|
|
memset (nsf->cpu->mem_page[0], 0, 0x800);
|
|
|
|
memset (nsf->cpu->mem_page[6], 0, 0x1000);
|
|
|
|
memset (nsf->cpu->mem_page[7], 0, 0x1000);
|
|
|
|
|
|
|
|
if (nsf->bankswitched) {
|
|
|
|
/* the first hack of the NSF spec! */
|
|
|
|
if (EXT_SOUND_FDS == nsf->ext_sound_type) {
|
|
|
|
nsf_bankswitch (0x5FF6, nsf->bankswitch_info[6]);
|
|
|
|
nsf_bankswitch (0x5FF7, nsf->bankswitch_info[7]);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (bank = 0; bank < 8; bank++)
|
|
|
|
nsf_bankswitch (0x5FF8 + bank, nsf->bankswitch_info[bank]);
|
|
|
|
} else {
|
|
|
|
/* not bankswitched, just page in our standard stuff */
|
|
|
|
ASSERT (nsf->load_addr + nsf->length <= 0x10000);
|
|
|
|
|
|
|
|
/* avoid ripper filth */
|
|
|
|
for (bank = 0; bank < 8; bank++)
|
|
|
|
nsf_bankswitch (0x5FF8 + bank, bank);
|
|
|
|
|
|
|
|
start_bank = nsf->load_addr >> 12;
|
|
|
|
num_banks = ((nsf->load_addr + nsf->length - 1) >> 12) - start_bank + 1;
|
|
|
|
|
|
|
|
for (bank = 0; bank < num_banks; bank++)
|
|
|
|
nsf_bankswitch (0x5FF0 + start_bank + bank, bank);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* determine PAL/NTSC compatibility shite */
|
|
|
|
if (nsf->pal_ntsc_bits & NSF_DEDICATED_PAL)
|
|
|
|
x_reg = 1;
|
|
|
|
else
|
|
|
|
x_reg = 0;
|
|
|
|
|
|
|
|
/* execute 1 frame or so; let init routine run free */
|
|
|
|
nsf_setup_routine (nsf->init_addr, (uint8) (nsf->current_song - 1), x_reg);
|
|
|
|
nes6502_execute ((int) NES_FRAME_CYCLES);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsf_frame (nsf_t * nsf)
|
|
|
|
{
|
|
|
|
/* future expansion =) */
|
|
|
|
/*nsf_setcontext(nsf); */
|
|
|
|
|
|
|
|
/* one frame of NES processing */
|
|
|
|
nsf_setup_routine (nsf->play_addr, 0, 0);
|
|
|
|
nes6502_execute ((int) NES_FRAME_CYCLES);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Deallocate memory */
|
|
|
|
void
|
|
|
|
nes_shutdown (nsf_t * nsf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
void *mem;
|
|
|
|
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
mem = nsf->cpu;
|
|
|
|
free (mem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsf_init (void)
|
|
|
|
{
|
|
|
|
nes6502_init ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize NES CPU, hardware, etc. */
|
|
|
|
static int
|
|
|
|
nsf_cpuinit (nsf_t * nsf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
nsf->cpu = malloc (sizeof (nes6502_context));
|
|
|
|
if (NULL == nsf->cpu)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
memset (nsf->cpu, 0, sizeof (nes6502_context));
|
|
|
|
|
|
|
|
nsf->cpu->mem_page[0] = malloc (0x800);
|
|
|
|
if (NULL == nsf->cpu->mem_page[0])
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* allocate some space for the NSF "player" MMC5 EXRAM, and WRAM */
|
|
|
|
for (i = 5; i <= 7; i++) {
|
|
|
|
nsf->cpu->mem_page[i] = malloc (0x1000);
|
|
|
|
if (NULL == nsf->cpu->mem_page[i])
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsf->cpu->read_handler = nsf_readhandler;
|
|
|
|
nsf->cpu->write_handler = nsf_writehandler;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
nsf_setup (nsf_t * nsf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
nsf->current_song = nsf->start_song;
|
|
|
|
|
|
|
|
if (nsf->pal_ntsc_bits & NSF_DEDICATED_PAL) {
|
|
|
|
if (nsf->pal_speed)
|
|
|
|
nsf->playback_rate = 1000000 / nsf->pal_speed;
|
|
|
|
else
|
|
|
|
nsf->playback_rate = 50; /* 50 Hz */
|
|
|
|
} else {
|
|
|
|
if (nsf->ntsc_speed)
|
|
|
|
nsf->playback_rate = 1000000 / nsf->ntsc_speed;
|
|
|
|
else
|
|
|
|
nsf->playback_rate = 60; /* 60 Hz */
|
|
|
|
}
|
|
|
|
|
|
|
|
nsf->bankswitched = FALSE;
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
|
|
if (nsf->bankswitch_info[i]) {
|
|
|
|
nsf->bankswitched = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HOST_LITTLE_ENDIAN
|
|
|
|
#define SWAP_16(x) (x)
|
|
|
|
#else /* !HOST_LITTLE_ENDIAN */
|
|
|
|
#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)
|
|
|
|
{
|
|
|
|
FILE *fp = NULL;
|
|
|
|
char *new_fn = NULL;
|
|
|
|
nsf_t *temp_nsf;
|
|
|
|
|
|
|
|
if (NULL == filename && NULL == source)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (NULL == source) {
|
|
|
|
fp = fopen (filename, "rb");
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
if (NULL == strrchr (new_fn, '.'))
|
|
|
|
strcat (new_fn, ".nsf");
|
|
|
|
|
|
|
|
fp = fopen (new_fn, "rb");
|
|
|
|
|
|
|
|
if (NULL == fp) {
|
|
|
|
void *t = new_fn;
|
|
|
|
|
|
|
|
log_printf ("could not find file '%s'\n", new_fn);
|
|
|
|
free (t);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
temp_nsf = malloc (sizeof (nsf_t));
|
|
|
|
if (NULL == temp_nsf)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Read in the header */
|
2006-07-18 09:36:46 +00:00
|
|
|
if (NULL == source) {
|
|
|
|
if (fread (temp_nsf, 1, NSF_HEADER_SIZE, fp) != NSF_HEADER_SIZE) {
|
|
|
|
log_printf ("error reading file\n");
|
|
|
|
free (temp_nsf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
} else
|
2006-07-13 15:07:28 +00:00
|
|
|
memcpy (temp_nsf, source, NSF_HEADER_SIZE);
|
|
|
|
|
|
|
|
if (memcmp (temp_nsf->id, NSF_MAGIC, 5)) {
|
|
|
|
if (NULL == source) {
|
|
|
|
void *t = new_fn;
|
|
|
|
|
|
|
|
log_printf ("%s is not an NSF format file\n", new_fn);
|
|
|
|
fclose (fp);
|
|
|
|
free (t);
|
|
|
|
}
|
|
|
|
nsf_free (&temp_nsf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
if (NULL == source) {
|
|
|
|
fseek (fp, 0, SEEK_END);
|
|
|
|
temp_nsf->length = ftell (fp) - NSF_HEADER_SIZE;
|
|
|
|
} else {
|
|
|
|
temp_nsf->length = length - NSF_HEADER_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* seek to end of header, read in data */
|
|
|
|
if (NULL == source) {
|
|
|
|
fseek (fp, NSF_HEADER_SIZE, SEEK_SET);
|
2006-07-19 11:43:50 +00:00
|
|
|
if (fread (temp_nsf->data, temp_nsf->length, 1, fp) != 1)
|
2006-07-18 09:36:46 +00:00
|
|
|
log_printf ("error reading end of header\n");
|
2006-07-13 15:07:28 +00:00
|
|
|
|
|
|
|
fclose (fp);
|
|
|
|
|
|
|
|
|
|
|
|
if (new_fn) {
|
|
|
|
void *t = new_fn;
|
|
|
|
|
|
|
|
free (t);
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
memcpy (temp_nsf->data, (uint8 *) source + NSF_HEADER_SIZE,
|
|
|
|
temp_nsf->length);
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
void *mem = (*nsf)->data;
|
|
|
|
|
|
|
|
free (mem);
|
|
|
|
}
|
|
|
|
|
|
|
|
free (*nsf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsf_setchan (nsf_t * nsf, int chan, boolean enabled)
|
|
|
|
{
|
|
|
|
if (nsf) {
|
|
|
|
nsf_setcontext (nsf);
|
|
|
|
apu_setchan (chan, enabled);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsf_playtrack (nsf_t * nsf, int track, int sample_rate, int sample_bits,
|
|
|
|
boolean stereo)
|
|
|
|
{
|
|
|
|
ASSERT (nsf);
|
|
|
|
|
|
|
|
/* 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) {
|
|
|
|
nsf_free (&nsf);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 ();
|
|
|
|
|
|
|
|
nsf_inittune (nsf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsf_setfilter (nsf_t * nsf, int filter_type)
|
|
|
|
{
|
|
|
|
if (nsf) {
|
|
|
|
nsf_setcontext (nsf);
|
|
|
|
apu_setfilter (filter_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** $Log$
|
2006-07-19 11:43:50 +00:00
|
|
|
** Revision 1.3 2006/07/19 11:43:50 tpm
|
|
|
|
** * gst/nsf/nsf.c: (nsf_load):
|
|
|
|
** Really fix compilation. Apparently it's not enough to
|
|
|
|
** just check the return value for errors, but we need to
|
|
|
|
** check for short reads as well (now if only we handled
|
|
|
|
** them too ...). Fixes #347935.
|
|
|
|
**
|
2006-07-18 09:36:46 +00:00
|
|
|
** Revision 1.2 2006/07/18 09:36:46 wtay
|
|
|
|
** * gst/nsf/nsf.c: (nsf_load):
|
|
|
|
** Fix compilation by not ignoring return values of fread.
|
|
|
|
**
|
2006-07-13 15:07:28 +00:00
|
|
|
** Revision 1.1 2006/07/13 15:07:28 wtay
|
|
|
|
** Based on patches by: Johan Dahlin <johan at gnome dot org>
|
|
|
|
** Ronald Bultje <rbultje at ronald dot bitfreak dot net>
|
|
|
|
** * configure.ac:
|
|
|
|
** * gst/nsf/Makefile.am:
|
|
|
|
** * gst/nsf/dis6502.h:
|
|
|
|
** * gst/nsf/fds_snd.c:
|
|
|
|
** * gst/nsf/fds_snd.h:
|
|
|
|
** * gst/nsf/fmopl.c:
|
|
|
|
** * gst/nsf/fmopl.h:
|
|
|
|
** * gst/nsf/gstnsf.c:
|
|
|
|
** * gst/nsf/gstnsf.h:
|
|
|
|
** * gst/nsf/log.c:
|
|
|
|
** * gst/nsf/log.h:
|
|
|
|
** * gst/nsf/memguard.c:
|
|
|
|
** * gst/nsf/memguard.h:
|
|
|
|
** * gst/nsf/mmc5_snd.c:
|
|
|
|
** * gst/nsf/mmc5_snd.h:
|
|
|
|
** * gst/nsf/nes6502.c:
|
|
|
|
** * gst/nsf/nes6502.h:
|
|
|
|
** * gst/nsf/nes_apu.c:
|
|
|
|
** * gst/nsf/nes_apu.h:
|
|
|
|
** * gst/nsf/nsf.c:
|
|
|
|
** * gst/nsf/nsf.h:
|
|
|
|
** * gst/nsf/osd.h:
|
|
|
|
** * gst/nsf/types.h:
|
|
|
|
** * gst/nsf/vrc7_snd.c:
|
|
|
|
** * gst/nsf/vrc7_snd.h:
|
|
|
|
** * gst/nsf/vrcvisnd.c:
|
|
|
|
** * gst/nsf/vrcvisnd.h:
|
|
|
|
** Added NSF decoder plugin. Fixes 151192.
|
|
|
|
**
|
|
|
|
** Revision 1.14 2000/07/05 14:54:45 matt
|
|
|
|
** fix for naughty Crystalis rip
|
|
|
|
**
|
|
|
|
** Revision 1.13 2000/07/04 04:59:38 matt
|
|
|
|
** removed DOS-specific stuff, fixed bug in address handlers
|
|
|
|
**
|
|
|
|
** Revision 1.12 2000/07/03 02:19:36 matt
|
|
|
|
** dynamic address range handlers, cleaner and faster
|
|
|
|
**
|
|
|
|
** Revision 1.11 2000/06/23 03:27:58 matt
|
|
|
|
** cleaned up external sound inteface
|
|
|
|
**
|
|
|
|
** Revision 1.10 2000/06/20 20:42:47 matt
|
|
|
|
** accuracy changes
|
|
|
|
**
|
|
|
|
** Revision 1.9 2000/06/20 00:05:58 matt
|
|
|
|
** changed to driver-based external sound generation
|
|
|
|
**
|
|
|
|
** Revision 1.8 2000/06/13 03:51:54 matt
|
|
|
|
** update API to take freq/sample data on nsf_playtrack
|
|
|
|
**
|
|
|
|
** Revision 1.7 2000/06/12 03:57:14 matt
|
|
|
|
** more robust checking for winamp plugin
|
|
|
|
**
|
|
|
|
** Revision 1.6 2000/06/12 01:13:00 matt
|
|
|
|
** added CPU/APU as members of the nsf struct
|
|
|
|
**
|
|
|
|
** Revision 1.5 2000/06/11 16:09:21 matt
|
|
|
|
** nsf_free is more robust
|
|
|
|
**
|
|
|
|
** Revision 1.4 2000/06/09 15:12:26 matt
|
|
|
|
** initial revision
|
|
|
|
**
|
|
|
|
*/
|