void cpuJSR(unsigned short npc, unsigned char na) { a = na; x = 0; y = 0; p = 0; s = 255; pc = npc; push(0); push(0); while(pc) cpuParse(getmem(pc++)); }
/* * handle a potentially endless running main program (e.g. started from "init_addr"). * @param npc 0 means that interrupted main program is continued (whereever it was) * @param cycleLimit limit the number of available CPU cycles before the processing stops * @return 0= run to completion; 1= interrupted by cyclelimit or due to number of produced digi samples */ static uint8_t callMain(uint16_t npc, uint8_t na, uint32_t startTime, int32_t cycleLimit) { /* the way how the sequence of main and INT calls is scheduled is obviously incorrect, and the logic below tries to at least workaround (which does not always work) some the flaws. the problems arise when: scenario 1: main program is interrupted by some INT and when it later resumes it expects to continue with the register content it had at the time of the interrupt (why is special handling needed here: any well behaved INT would perform that cleanup anyway.. but various songs do seem to benefit from a respective hack.. see comment in cpuRegSave) scenario 2: supposing a chain of INTs does not expect any interference from MAIN and registers are just passed on from one INT call to the next (see Jevers_Bannys_and_the_Master_Mixers.sid): a main call should then under no circumstances mess with the registers... (caution: doing too much here will harm songs like: Storebror.sid, Wonderland XII part1.sid, Comalight 13 tune4.sid) */ initCycleCount(0, startTime); // use new timestamps for potential digi-samples & cycleLimit check if (npc == 0) cpuRegSave(1, 1); if (npc == 0) { cpuRegRestore(0,0); } else { cpuReset(npc, na); // sid init call: with 'start addr' and 'seleced track' in acc } while (cpuPcIsValid()) { // if a main progs is already producing samples then it is done with the "init" phase and // we interrupt it when we have enough samples for one screen (see Suicide_Express.sid) if (((cycleLimit >0) && (cpuCycles() >=cycleLimit)) || (digiGetOverflowCount() >0)) { cpuRegSave(0,0); if (npc == 0) cpuRegRestore(1, 1); return 1; } vicSimRasterline(); cpuParse(); } if (npc == 0) cpuRegRestore(1, 1); return 0; }
unsigned long processInterrupt(unsigned long intMask, unsigned short npc, unsigned long startTime, signed long cycleLimit) { // known limitation: if the code uses ROM routines (e.g. register restore/return sequence) then we will // be missing those cpu cycles in our calculation.. unsigned long originalDigiCount= sDigiCount; if (isPsidDummyIrqVector()) { // no point in trying to keep the stack consistent for a PSID cpuReset(); } // provide dummy return address - which we use to return from the emulation: // in case of RTI (e.g. progs implementing $fffe/f vector directly) this will be used "as is". // if some program was to return with "RTS" (e.g. legacy PSID) the address would be returned as $0001. push(0); // addr high push(0); // addr low push(p); // processor status (processor would do this in case of interrupt...) // only set pc and keep current stackpointer and registers in case player directly passes // them between calls (see Look_sharp.sid) pc= npc; sProgramMode= intMask; initCycleCount( (intMask == NMI_OFFSET_MASK) ? 7 : 0, startTime); while (pc > 1 && ((cycleLimit <0) || (sCycles <cycleLimit))) cpuParse(); sProgramMode= MAIN_OFFSET_MASK; if (intMask == IRQ_OFFSET_MASK) { sCurrentVolume= getmem(0xd418); } markSampleOrigin(intMask, startTime, originalDigiCount); return sCycles; }
static uint32_t processInterrupt(uint32_t intMask, uint16_t npc, uint32_t startTime, int32_t cycleLimit) { // known limitation: if the code uses ROM routines calculation is flawed (see mock-up ROM routines). uint16_t originalDigiCount= digiGetCount(); uint16_t originalDigiOverflowCount= digiGetOverflowCount(); sidResetVolumeChangeCount(); if (isPsidDummyIrqVector()) { // no point in trying to keep the stack consistent for a PSID cpuRegReset(); } cpuResetToIrq(npc); cpuSetProgramMode(intMask); initCycleCount(intMask, startTime); if (intMask == IRQ_OFFSET_MASK) { vicSyncRasterIRQ(); // FIXME the cpu interrupt flag should also be set before starting the interrupt handler! // but since no one seems to have missed it yet.. } while (cpuPcIsValid() && ((cycleLimit <0) || (cpuCycles() <cycleLimit))) { vicSimRasterline(); cpuParse(); } cpuSetProgramMode(MAIN_OFFSET_MASK); digiTagOrigin(intMask, startTime, originalDigiCount, originalDigiOverflowCount); if (intMask == IRQ_OFFSET_MASK) _volUpdates+= sidGetNumberOfVolumeChanges(); // number of updates to the volume return cpuCycles(); }