Пример #1
0
int LoadAPU_embedded(FILE *fptr)
{
	int i=0, j=0, count=0, val=0;
	
	unsigned char spc_pcl;
	unsigned char spc_pch;
	unsigned char spc_a;
	unsigned char spc_x;
	unsigned char spc_y;
	unsigned char spc_sw;
	unsigned char spc_sp;

//	unsigned char spcdata[65536];
//	unsigned char spcram[64];

	unsigned char dsp_kon=0;
	unsigned char dsp_flg=0;
	unsigned char dsp_esa=0;
	unsigned char dsp_edl=0;
	
	unsigned char workbuf[64];
	
	int echosize, echoregion, bootptr, readcount;
	
	fseek(fptr, 0x25, SEEK_SET);

	fread(&spc_pcl, 1, 1, fptr);
	fread(&spc_pch, 1, 1, fptr);
	fread(&spc_a, 1, 1, fptr);
	fread(&spc_x, 1, 1, fptr);
	fread(&spc_y, 1, 1, fptr);
	fread(&spc_sw, 1, 1, fptr);
	fread(&spc_sp, 1, 1, fptr);

	if (g_debug) {
		printf("PC: %02x%02x\n", spc_pch, spc_pcl);
		printf("A: %02X\n", spc_a);
		printf("X: %02X\n", spc_x);
		printf("Y: %02X\n", spc_y);
		printf("SW: %02X\n", spc_sw);
		printf("SP: %02X\n", spc_sp);
	}

	apu_reset();
	apu_initTransfer(0x0002);

	if (g_verbose) 
		printf("Restoring dsp registers...\n");

	if (g_exit_now || !g_playing) { apu_reset(); return 0; }
	
	/* first, we send a small program called the dsploader which we will
	 * use to restore the DSP registers (with our modified KON and FLG to
	 * keep it silent) */
	if (apu_writeBytes(dsploader, 16)) {
		fprintf(stderr, "Timeout sending dsploader\n");
		return -1;
	}
	apu_endTransfer(0x0002);	
	
	if (g_exit_now || !g_playing) { apu_reset(); return 0; }

	/* restore the 128 dsp registers one by one with the help of the dsp loader. */
	fseek(fptr, OFFSET_DSPDATA, SEEK_SET);
	for (i=0; i<128; i+=64)
	{
		fread(workbuf, 64, 1, fptr);
		for (j=0; j<64; j++)
		{
			/* mute all voices and stop all notes */
			if (i+j == DSP_FLG) {
				dsp_flg = workbuf[j]; // save it for later
				workbuf[j] = DSP_FLG_MUTE|DSP_FLG_ECEN;
			}
			if (i+j == DSP_KON) {
				dsp_kon = workbuf[j]; // save it for later
				workbuf[j] = 0x00;
			}

			// take note of some values while we upload...
			if (i+j == DSP_ESA) { dsp_esa = workbuf[j]; }
			if (i+j == DSP_EDL) { dsp_edl = workbuf[j]; }
			
			apu_write(1, workbuf[j]);
			apu_write(0, i+j);
			if (!apu_waitInport(0, i+j, 500)) {
				if (apu_read(0)==0xaa) {
//				fprintf(stderr, "ingored\n");
				} else {
					fprintf(stderr, "timeout 3\n"); return -1; 
				}
			}

			if (g_exit_now || !g_playing) { apu_reset(); return 0; }
#ifdef PROGRESS_SPINNER
			if (g_progress) {
				pspin_update();
			}
#endif
		}

	}
//	if (g_verbose) 
//		printf("\n");

	/* after receiving 128 registers, the dsp loaded will jump
	 * inside the rom at address $ffc9. Once 0xAA appears in 
	 * port0, the apu is ready for a new transfer. */
	if (!apu_waitInport(0, 0xaa, 500)) {
		fprintf(stderr, "timeout 4\n"); return -1; 
	}

//	fseek(fptr, OFFSET_SPCRAM, SEEK_SET);
//	fread(spcram, 64, 1, fptr);

	/* save a bunch of registers to be restored
	 * later by the "bootcode" */
	bootcode[BOOT_DSP_FLG] = dsp_flg;
	bootcode[BOOT_DSP_KON] = dsp_kon;
	bootcode[BOOT_A] = spc_a;
	bootcode[BOOT_Y] = spc_y;
	bootcode[BOOT_X] = spc_x;
	bootcode[BOOT_SP] = spc_sp - 3; // save new stack pointer

	/* save address $0000 and $0001 to be restored by "bootcode" */
	fseek(fptr, OFFSET_SPCDATA, SEEK_SET);
	fread(workbuf, 2, 1, fptr);
	bootcode[0x01] = workbuf[0];
	bootcode[0x04] = workbuf[1];
	
	/* save most spc registers (0xf0 to 0xff) into bootcode to be restored
	 * later */
	fseek(fptr, OFFSET_SPCDATA+0xf0, SEEK_SET);
	fread(workbuf, 0x10, 1, fptr);
	for (i=0xf0; i<=0xff; i++) {
		switch (i)
		{
			case SPC_PORT0: bootcode[BOOT_SPC_PORT0] = workbuf[i-0xf0]; break;
			case SPC_PORT1: bootcode[BOOT_SPC_PORT1] = workbuf[i-0xf0]; break;
			case SPC_PORT2: bootcode[BOOT_SPC_PORT2] = workbuf[i-0xf0]; break;
			case SPC_PORT3: bootcode[BOOT_SPC_PORT3] = workbuf[i-0xf0]; break;
			case SPC_TIMER0: bootcode[BOOT_SPC_TIMER0] = workbuf[i-0xf0]; break;
			case SPC_TIMER1: bootcode[BOOT_SPC_TIMER1] = workbuf[i-0xf0]; break;
			case SPC_TIMER2: bootcode[BOOT_SPC_TIMER2] = workbuf[i-0xf0]; break;
			case SPC_CONTROL: bootcode[BOOT_SPC_CONTROL] = workbuf[i-0xf0]; break;
			case SPC_REGADD: bootcode[BOOT_SPC_REGADD] = workbuf[i-0xf0]; break;
		}
	}
	


	/* to produce an echo effect, the dsp uses a memory region.
	 * ESA: Esa * 100h becomes the lead-off address of the echo
	 * region. Calculate this address... */
	echoregion = dsp_esa * 256;

	/* echo delay. The bigger the delay is, more memory is needed.
	 * calculate how much memory used... */
	echosize = dsp_edl * 2048;
	if (echosize==0) { echosize = 4; }

	if (g_debug) { 
		printf("debug: echoregion: $%04x, size %d\n", echoregion, echosize);
	}

	apu_initTransfer(0x0002);
	if (g_verbose) 
		printf("Restoring spc700 memory...\n");
	
	if (g_debug) { 
		printf("debug: Sending spc memory from 0x02 to 0xef\n");
	}
	/* send the first part of the memory (0x02 to 0xef)
	 * After 0xef comes spc700 registers (0xf0 to 0xff). Those
	 * are taken care of by the bootcode. 0x00 and 0x01 are
	 * retored by the bootcode too. */
	fseek(fptr, OFFSET_SPCDATA, SEEK_SET);
	for (j=0; j<256; j+=64)
	{
		fread(workbuf, 64, 1, fptr);
		for (i=0; i<0x40; i++) {
			if (j+i>=0xf0) { break; }
			if (j==0 && i<2) { continue; } // skip $0000 and $0001
			apu_write(1, workbuf[i]);
			apu_write(0, j+i-2);
			if (!apu_waitInport(0, j+i-2, 500)) {
				fprintf(stderr, "timeout 5\n"); return -1; 
			}
#ifdef PROGRESS_SPINNER
			if (g_progress) 
				pspin_update();
#endif
			if (g_exit_now || !g_playing) { apu_reset(); return 0; }
		}
		if (j+i>=0xf0) { break; }
	}

//	if (g_verbose) 
//		printf("\n");

	if (apu_newTransfer(0x100)) { apu_reset(); return -1; }
	
	if (g_debug) { 
		printf("debug: Sending spc memory from 0x100 to 0xffc0\n");
	}
	/* upload the external memory region data (0x100 (page 1) to 0xffbf (rom),
	 * and look for an area with the same consecutive value repeated 77 times */	
	fseek(fptr, OFFSET_SPCDATA+0x100, SEEK_SET);
	bootptr = -1;
	for (i=0x100; i <= 65471; i+= 16)
	{		
		fread(workbuf, 16, 1, fptr);
		
		for (j=0; j<16; j++) {
			/* push program counter and status ward on stack */
			if ((i+j) == (0x100 +spc_sp - 0)) {
				workbuf[j] = spc_pch;
			}
			if ((i+j) == (0x100 +spc_sp - 1)) {
				workbuf[j] = spc_pcl;
			}
			if ((i+j) == (0x100 +spc_sp - 2)) {
				workbuf[j] = spc_sw;
			}
			
			if ((i > echoregion + echosize) || (i < echoregion) )
			{
				if (val==workbuf[j]) {
					count++;
					if (count>=77) {
						bootptr = i+j-77;
//						printf("nbptr: %d\n", i+j-77);
					}
				}
				else {
					val = workbuf[j];
					count = 0;
				}
			}
			else
			{
				count = 0;
			}
		}
		
		if (apu_writeBytes(workbuf, 16))
		{
			fprintf(stderr, "Transfer error\n");
			return -1;
		}

		if (i % 256 == 0) {
			readcount += 256;
#ifdef PROGRESS_SPINNER
			if (g_progress) {
				pspin_update();
			}
#endif
		}
		if (g_exit_now || !g_playing) { apu_reset(); return 0; }
	}
	
//	bootptr = 0x2e47;
	if (g_debug) { 	
		printf("debug: area for bootcode: $%04x (%02X)\n", bootptr, val);
	}
	
	/* we did not find an area of 77 consecutive identical byte values. */
	if (bootptr == -1) 
	{
		/* We will have to use the echo region. The region will need to be
		 * at least 77 bytes... */
		if (echosize < 77) {
			fprintf(stderr, "This spc file does not have sufficient ram to be loaded");
			return -1;
		}
		else {
			/* we will use the echo region */
			bootptr = echoregion;
		}
	}

	if (g_debug) { 
		printf("debug: Sending spc memory from 0xffc0 to 0xffff\n");
	}
	/* upload the external memory area overlapping with the rom... I guess
	 * if we write to those address from the SPC it really writes to this
	 * memory area, but if you read you'll probably get the ROM code. Maybe
	 * it's really Read/Write from the DSP point of view... TODO: Check this 
	 *
	 * Maybe also setting SPC_CONTROL msb bit enables this region? It's not
	 * documented my manual...
	 * */
	if (bootcode[BOOT_SPC_CONTROL] & 0x80) {
		fseek(fptr, OFFSET_SPCRAM, SEEK_SET);
		fread(workbuf, 64, 1, fptr);
	}
	else {
		fseek(fptr, OFFSET_SPCDATA + 65472, SEEK_SET);
		fread(workbuf, 64, 1, fptr);
	}
	
	if (apu_writeBytes(workbuf, 64)) {
		return -1;
	}

//	if (g_verbose) 
//		printf("\n");

	if (apu_newTransfer(bootptr)) { apu_reset(); return -1; }

	/* Copy our bootcode into the area we found */
	if (apu_writeBytes(bootcode, 77)) {
		fprintf(stderr, "Bootcode transfer error\n");
		return -1;
	}

	apu_endTransfer(bootptr);
	
	//i = 0;
	if (!apu_waitInport(0, 0x53, 500)) {
		fprintf(stderr, "timeout 7\n");
		return -1;
	}

	if (g_debug) {
		printf("Setting final port values $%02X $%02X $%02X $%02X\n",
				bootcode[BOOT_SPC_PORT0], bootcode[BOOT_SPC_PORT1], 
				bootcode[BOOT_SPC_PORT2], bootcode[BOOT_SPC_PORT3]);
	}

	/* Restore the ports to the value they
	 * had in the .spc  (this is not done by the bootcode because
	 * Port0-3 have 2 different values (The value set internally is
	 * seen externaly and the value seen internally is set externally) */
	apu_write(0, bootcode[BOOT_SPC_PORT0]);
	apu_write(1, bootcode[BOOT_SPC_PORT1]);
	apu_write(2, bootcode[BOOT_SPC_PORT2]);
	apu_write(3, bootcode[BOOT_SPC_PORT3]);

	
	if (g_exit_now || !g_playing) { apu_reset(); return 0; }
	return 0;
}
Пример #2
0
void apu_process(void* buffer, int num_samples)
{
	register int32 accum1;
//	int32 accum2;
//	uint32* buf32;
	int16* buf16;
	int16 prev_sample = 0;
	int16 next_sample;

	int count;

	//next write is the sample on which the next write will occur
	int16 next_write;
	int queue_index;

	if (0 != buffer)
	{
//		buf32 = (uint32*) buffer;
		buf16 = (int16*) buffer;

		//If there was a write during the last section
		if (num_queues)
		{
			queue_index = 0;
			num_queues--;
			next_write = num_samples - (int16) (num_samples * ((float) (queue[queue_index].sample_write) / 524.0f));
		}
		else
			next_write = -1;
		
		count = num_samples;

		while (count--)
		{
			accum1 = 0;

			if (apu.mix_enable & 0x01)
				accum1 += apu_rectangle_0();
			if (apu.mix_enable & 0x02)
				accum1 += apu_rectangle_1();
			if (apu.mix_enable & 0x04)
				accum1 += apu_triangle();
			if (apu.mix_enable & 0x08)
				accum1 += apu_noise();
			if (apu.mix_enable & 0x10)
				accum1 += apu_dmc();

//			if (apu_rectangle_0() + apu_rectangle_1() + apu_triangle() + apu_noise() + apu_dmc();

			next_sample = accum1;

			/* do clipping */
			accum1 += prev_sample;
			accum1 >>= 1;

			prev_sample = next_sample;

			if (accum1 > 0x7FFF)
				accum1 = 0x7FFF;
			else if (accum1 < -0x8000)
				accum1 = -0x8000;

			*buf16++ = accum1;

			while (next_write >= count)
			{
				apu_write(queue[queue_index].reg, queue[queue_index].bydata);
				num_queues--;

				if (num_queues >= 0)
				{
					queue_index++;
					next_write = num_samples - (int16) (num_samples * ((float) (queue[queue_index].sample_write) / 524.0f));
				}
				else
				{
					next_write = -1;
				}
			}
		}
	}
Пример #3
0
int nes_loadstate(int fp)
{
	stateheader_t header;
	blocknes_t *bnes;
	blockcpu_t *cpu;
	blockppu_t *ppu;
	blockapu_t *apu;
	blockvram_t *vram;
	blocksvram_t *svram;
	blocksram_t *sram;
	blockwram_t *wram;
	blockmapper_t *mapper;
	blockdisk_t *disk;
	int i;
	void *block;
	int blocktype;

	//load state header
	if(blocks_loadheader(fp,&header) != 0)
		return(1);

	//enumerate thru all the state blocks
	while((block = blocks_load(fp,&blocktype)) != 0) {
		switch(blocktype) {
			case B_NES:
				bnes = (blocknes_t*)block;
				memcpy(nes->ram,bnes->ram,0x800);
				memcpy(nes->nametables[0],bnes->nametables + 0x000,0x400);
				memcpy(nes->nametables[1],bnes->nametables + 0x400,0x400);
				memcpy(nes->nametables[2],bnes->nametables + 0x800,0x400);
				memcpy(nes->nametables[3],bnes->nametables + 0xC00,0x400);
				memcpy(nes->pal,bnes->palette,32);
				nes->strobe = bnes->strobe;
				for(i=0;i<32;i++)
					pal_write(i,nes->pal[i]);
				break;
			case B_CPU:
				cpu = (blockcpu_t*)block;
				nes->cpu.a = cpu->a;
				nes->cpu.x = cpu->x;
				nes->cpu.y = cpu->y;
				nes->cpu.s = cpu->s;
				nes->cpu.f = cpu->f;
				nes->cpu.pc = cpu->pc;
				nes->cpu.totalcycles = cpu->cycles;
				nes->cpu.needirq = cpu->irq;
				break;
			case B_PPU:
				ppu = (blockppu_t*)block;
				nes->scanline = ppu->scanline;
				nes->ppu.ctrl0 = ppu->control1;
				nes->ppu.ctrl1 = ppu->control2;
				nes->ppu.status = ppu->status;
				nes->ppu.temp = ppu->temp;
				nes->ppu.scroll = ppu->scroll;
				nes->ppu.scrollx = ppu->finex;
				nes->ppu.toggle = ppu->toggle;
				nes->ppu.buf = ppu->buffer;
				nes->ppu.latch = ppu->latch;
				nes->ppu.spraddr = ppu->spraddr;
				memcpy(nes->sprmem,ppu->spriteram,0x100);
				ppucycles = ppu->cycles;
				ppuframes = ppu->frames;
				break;
			case B_APU:
				apu = (blockapu_t*)block;
				for(i=0;i<0x16;i++)
					apu_write(0x4000 + i,apu->regs[i]);
				break;
			case B_VRAM:
				vram = (blockvram_t*)block;
				if(nes->rom->vram) {
					memcpy(nes->rom->vram,vram->data,nes->rom->vramsize);
					for(i=0;i<0x2000;i++)
						ppumem_write(i,ppumem_read(i));
				}
				break;
			case B_SVRAM:
				svram = (blocksvram_t*)block;
				if(nes->rom->svram)
					memcpy(nes->rom->svram,svram->data,nes->rom->svramsize);
				break;
			case B_SRAM:
				sram = (blocksram_t*)block;
				if(nes->rom->sram)
					memcpy(nes->rom->sram,sram->data,nes->rom->sramsize);
				break;
			case B_WRAM:
				wram = (blockwram_t*)block;
				if(nes->rom->wram)
					memcpy(nes->rom->wram,wram->data,nes->rom->wramsize);
				break;
			case B_MAPR:
				mapper = (blockmapper_t*)block;
				if(nes->mapper->state)
					nes->mapper->state(STATE_LOAD,mapper->data);
				break;
			case B_DISK:
				disk = (blockdisk_t*)block;
				for(i=0;i<(nes->rom->disksides * 65500);i++)
					nes->rom->diskdata[i] = nes->rom->orig_diskdata[i] ^ disk->data[i];
				break;
		}
		blocks_free(block);
		log_message("loading block '%s'\n",blockids[blocktype]);
	}

	log_message("loaded state, %ld bytes\n",file_tell(fp));

	return(0);
}