akkera 102
/
apuplay
SPC music playback tools for real snes apu
apu2.cpp
- Committer:
- akkera102
- Date:
- 2017-01-19
- Revision:
- 8:072621697467
- Parent:
- 3:b845c0cf715a
File content as of revision 8:072621697467:
#include "apu.h" #include "apu2.h" #include "cmd.h" // cmd.cpp extern int g_debug; extern int g_verbose; // code loaded at 0xf2 static unsigned char dsploader[16] = { 0xc4, 0xf2, // start: MOV f2, A 0x64, 0xf4, // loop: CMP A, f4 0xd0, 0xfc, // BNE loop 0xfa, 0xf5, 0xf3, // MOV f3 , f5 0xc4, 0xf4, // MOV f4, A 0xbc, // INC A 0x10, 0xf2, // BPL start 0x2f, 0xb7 // BRA 0xb7 ; jump inside rom }; static unsigned char bootcode[77] = { 0x8f, 0x00, 0x00, 0x8f, 0x00, 0x01, 0x8f, 0xff, 0xfc, 0x8f, 0xff, 0xfb, 0x8f, 0x4f, 0xfa, 0x8f, 0x31, 0xf1, 0xcd, 0x53, 0xd8, 0xf4, 0xe4, 0xf4, 0x68, 0x00, 0xd0, 0xfa, 0xe4, 0xf5, 0x68, 0x00, 0xd0, 0xfa, 0xe4, 0xf6, 0x68, 0x00, 0xd0, 0xfa, 0xe4, 0xf7, 0x68, 0x00, 0xd0, 0xfa, 0xe4, 0xfd, 0xe4, 0xfe, 0xe4, 0xff, 0x8f, 0x6c, 0xf2, 0x8f, 0x00, 0xf3, 0x8f, 0x4c, 0xf2, 0x8f, 0x00, 0xf3, 0x8f, 0x7f, 0xf2, 0xcd, 0xf5, 0xbd, 0xe8, 0xff, 0x8d, 0x00, 0xcd, 0x00, 0x7f }; int LoadAPU_embedded(FILE *fp) { 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 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=0; fseek(fp, 0x25, SEEK_SET); fread(&spc_pcl, 1, 1, fp); fread(&spc_pch, 1, 1, fp); fread(&spc_a, 1, 1, fp); fread(&spc_x, 1, 1, fp); fread(&spc_y, 1, 1, fp); fread(&spc_sw, 1, 1, fp); fread(&spc_sp, 1, 1, fp); 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_init(); apu_reset(); apu_initTransfer(0x0002); if(g_verbose) { printf("Restoring dsp registers...\n"); } // 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); // restore the 128 dsp registers one by one with the help of the dsp loader. fseek(fp, OFFSET_DSPDATA, SEEK_SET); for(i=0; i<128; i+=64) { fread(workbuf, 64, 1, fp); 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) { if(g_verbose) { printf("ingored %d\n", i+j); } } else { fprintf(stderr, "timeout 3\n"); return -1; } } cmd_pspin_update(); } } // 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; } // 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(fp, OFFSET_SPCDATA, SEEK_SET); fread(workbuf, 2, 1, fp); bootcode[0x01] = workbuf[0]; bootcode[0x04] = workbuf[1]; // save most spc registers (0xf0 to 0xff) into bootcode to be restored // later fseek(fp, OFFSET_SPCDATA+0xf0, SEEK_SET); fread(workbuf, 0x10, 1, fp); 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(fp, OFFSET_SPCDATA, SEEK_SET); for(j=0; j<256; j+=64) { fread(workbuf, 64, 1, fp); for(i=0; i<0x40; i++) { if(j+i >= 0xf0) { break; } // skip $0000 and $0001 if(j==0 && i<2) { continue; } 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; } cmd_pspin_update(); } if (j+i>=0xf0) { break; } } 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(fp, OFFSET_SPCDATA+0x100, SEEK_SET); bootptr = -1; for(i=0x100; i<=65471; i+=16) { fread(workbuf, 16, 1, fp); 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; } } 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; cmd_pspin_update(); } } 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\n"); 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(fp, OFFSET_SPCRAM, SEEK_SET); fread(workbuf, 64, 1, fp); } else { fseek(fp, OFFSET_SPCDATA + 65472, SEEK_SET); fread(workbuf, 64, 1, fp); } if(apu_writeBytes(workbuf, 64)) { return -1; } 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]); return 0; }