📄 nsf.c
字号:
/*** 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: nsf.c,v 1.4 2006/09/26 00:52:17 dgp85 Exp $*/#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 32static 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){ //nsf_setcontext(nsf); /* future expansion =) */ /* one frame of NES processing */ nsf_setup_routine(nsf->play_addr, 0, 0); nes6502_execute((int) NES_FRAME_CYCLES);}/* Deallocate memory */static 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); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -