void psxRcnt0Wmode(u32 value) { PSXCNT_LOG("IOP Counter[0] writeMode = %lx\n", value); psxCounters[0].mode = value; psxCounters[0].mode|= 0x0400; psxCounters[0].rate = 1; if(value & IOPCNT_ALT_SOURCE) psxCounters[0].rate = PSXPIXEL; if(psxCounters[0].mode & IOPCNT_ENABLE_GATE) { // gated counters are added up as per the h/vblank timers. PSXCNT_LOG("IOP Counter[0] Gate Check set, value = %x\n", value); psxhblankgate |= 1; } else psxhblankgate &= ~1; psxCounters[0].count = 0; psxCounters[0].sCycleT = psxRegs.cycle; psxCounters[0].target &= 0xffff; _rcntSet( 0 ); }
void psxRcnt5Wmode(u32 value) { PSXCNT_LOG("IOP Counter[5] writeMode = %lx\n", value); psxCounters[5].mode = value; psxCounters[5].mode|= 0x0400; switch(value & 0x6000) { case 0x0000: psxCounters[5].rate = 1; break; case 0x2000: psxCounters[5].rate = 8; break; case 0x4000: psxCounters[5].rate = 16; break; case 0x6000: psxCounters[5].rate = 256; break; } // Need to set a rate and target if((psxCounters[5].mode & 0x7) == 0x7 || (psxCounters[5].mode & 0x7) == 0x1) { SysPrintf("Gate set on IOP C5, disabling\n"); psxCounters[5].mode |= IOPCNT_STOPPED; } psxCounters[5].count = 0; psxCounters[5].sCycleT = psxRegs.cycle; psxCounters[5].target &= 0xffffffff; _rcntSet( 5 ); }
static void _psxCheckStartGate( int i ) { if(!(psxCounters[i].mode & IOPCNT_ENABLE_GATE)) return; //Ignore Gate switch((psxCounters[i].mode & 0x6) >> 1) { case 0x0: //GATE_ON_count - stop count on gate start: // get the current count at the time of stoppage: psxCounters[i].count = ( i < 3 ) ? psxRcntRcount16( i ) : psxRcntRcount32( i ); psxCounters[i].mode |= IOPCNT_STOPPED; return; case 0x1: //GATE_ON_ClearStart - count normally with resets after every end gate // do nothing - All counting will be done on a need-to-count basis. return; case 0x2: //GATE_ON_Clear_OFF_Start - start counting on gate start, stop on gate end psxCounters[i].count = 0; psxCounters[i].sCycleT = psxRegs.cycle; psxCounters[i].mode &= ~IOPCNT_STOPPED; break; case 0x3: //GATE_ON_Start - start and count normally on gate end (no restarts or stops or clears) // do nothing! return; } _rcntSet( i ); }
static void _psxCheckEndGate(int i) { if(!(psxCounters[i].mode & IOPCNT_ENABLE_GATE)) return; //Ignore Gate switch((psxCounters[i].mode & 0x6) >> 1) { case 0x0: //GATE_ON_count - reset and start counting case 0x1: //GATE_ON_ClearStart - count normally with resets after every end gate psxCounters[i].count = 0; psxCounters[i].sCycleT = psxRegs.cycle; psxCounters[i].mode &= ~IOPCNT_STOPPED; break; case 0x2: //GATE_ON_Clear_OFF_Start - start counting on gate start, stop on gate end psxCounters[i].count = ( i < 3 ) ? psxRcntRcount16( i ) : psxRcntRcount32( i ); psxCounters[i].mode |= IOPCNT_STOPPED; return; // do not set the counter case 0x3: //GATE_ON_Start - start and count normally (no restarts or stops or clears) if( psxCounters[i].mode & IOPCNT_STOPPED ) { psxCounters[i].count = 0; psxCounters[i].sCycleT = psxRegs.cycle; psxCounters[i].mode &= ~IOPCNT_STOPPED; } break; } _rcntSet( i ); }
void psxRcntWcount32(int index, u32 value) { u32 change; pxAssert( index >= 3 && index < 6 ); PSXCNT_LOG("32bit IOP Counter[%d] writeCount32 = %x", index, value); if(psxCounters[index].rate != PSXHBLANK) { // Re-adjust the sCycleT to match where the counter is currently // (remainder of the rate divided into the time passed will do the trick) change = psxRegs.cycle - psxCounters[index].sCycleT; psxCounters[index].sCycleT = psxRegs.cycle - (change % psxCounters[index].rate); } psxCounters[index].count = value; if ((psxCounters[index].mode & 0x400) || (psxCounters[index].mode & 0x40)) { //IRQ not triggered (one shot) or toggle psxCounters[index].target &= 0xffffffff; } if (value > psxCounters[index].target) {//Count already higher than Target //DevCon.Warning("32bit Count already higher than target"); psxCounters[index].target |= IOPCNT_FUTURE_TARGET; } _rcntSet( index ); }
__fi void psxRcntWmode16( int index, u32 value ) { PSXCNT_LOG( "IOP Counter[%d] writeMode = 0x%04X", index, value ); pxAssume( index >= 0 && index < 3 ); psxCounter& counter = psxCounters[index]; counter.mode = value; counter.mode |= 0x0400; if( index == 2 ) { switch(value & 0x200) { case 0x000: psxCounters[2].rate = 1; break; case 0x200: psxCounters[2].rate = 8; break; jNO_DEFAULT; } if((counter.mode & 0x7) == 0x7 || (counter.mode & 0x7) == 0x1) { counter.mode |= IOPCNT_STOPPED; } } else { // Counters 0 and 1 can select PIXEL or HSYNC as an alternate source: counter.rate = 1; if(value & IOPCNT_ALT_SOURCE) counter.rate = (index==0) ? PSXPIXEL : PSXHBLANK; if(counter.mode & IOPCNT_ENABLE_GATE) { // gated counters are added up as per the h/vblank timers. // (the PIXEL alt source becomes a vsync gate) counter.mode |= IOPCNT_STOPPED; PSXCNT_LOG( "IOP Counter[%d] Gate Check set, value = 0x%04X", index, value ); if( index == 0 ) psxhblankgate |= 1; // fixme: these gate flags should be one var >_< else psxvblankgate |= 1<<1; } else { if( index == 0 ) psxhblankgate &= ~1; else psxvblankgate &= ~(1<<1); } } counter.count = 0; counter.sCycleT = psxRegs.cycle; counter.target &= 0xffff; _rcntSet( index ); }
void psxRcntWtarget16(int index, u32 value) { assert( index < 3 ); PSXCNT_LOG("IOP Counter[%d] writeTarget16 = %lx\n", index, value); psxCounters[index].target = value & 0xffff; // protect the target from an early arrival. // if the target is behind the current count, then set the target overflow // flag, so that the target won't be active until after the next overflow. if(psxCounters[index].target <= psxRcntCycles(index)) psxCounters[index].target |= IOPCNT_FUTURE_TARGET; _rcntSet( index ); }
void psxRcntWtarget16(int index, u32 value) { pxAssert( index < 3 ); //DevCon.Warning("IOP Counter[%d] writeTarget16 = %lx", index, value); psxCounters[index].target = value & 0xffff; // protect the target from an early arrival. // if the target is behind the current count, then set the target overflow // flag, so that the target won't be active until after the next overflow. if(psxCounters[index].target <= psxRcntCycles(index) || ((psxCounters[index].mode & 0x400) == 0 && !(psxCounters[index].mode & 0x40))) psxCounters[index].target |= IOPCNT_FUTURE_TARGET; _rcntSet( index ); }
__fi void psxRcntWmode32( int index, u32 value ) { PSXCNT_LOG( "IOP Counter[%d] writeMode = 0x%04x", index, value ); pxAssume( index >= 3 && index < 6 ); psxCounter& counter = psxCounters[index]; counter.mode = value; counter.mode |= 0x0400; if( index == 3 ) { // Counter 3 has the HBlank as an alternate source. counter.rate = 1; if(value & IOPCNT_ALT_SOURCE) counter.rate = PSXHBLANK; if(counter.mode & IOPCNT_ENABLE_GATE) { PSXCNT_LOG("IOP Counter[3] Gate Check set, value = %x", value); counter.mode |= IOPCNT_STOPPED; psxvblankgate |= 1<<3; } else psxvblankgate &= ~(1<<3); } else { switch(value & 0x6000) { case 0x0000: counter.rate = 1; break; case 0x2000: counter.rate = 8; break; case 0x4000: counter.rate = 16; break; case 0x6000: counter.rate = 256; break; } // Need to set a rate and target if((counter.mode & 0x7) == 0x7 || (counter.mode & 0x7) == 0x1) { Console.WriteLn( "Gate set on IOP Counter %d, disabling", index ); counter.mode |= IOPCNT_STOPPED; } } counter.count = 0; counter.sCycleT = psxRegs.cycle; counter.target &= 0xffffffff; _rcntSet( index ); }
void psxRcntWtarget32(int index, u32 value) { pxAssert( index >= 3 && index < 6); //DevCon.Warning("IOP Counter[%d] writeTarget32 = %lx mode %x", index, value, psxCounters[index].mode); psxCounters[index].target = value; if (!(psxCounters[index].mode & 0x80)) { //Toggle mode psxCounters[index].mode |= 0x0400; // Interrupt flag set low } // protect the target from an early arrival. // if the target is behind the current count, then set the target overflow // flag, so that the target won't be active until after the next overflow. if (psxCounters[index].target <= psxRcntCycles(index) || ((psxCounters[index].mode & 0x400) == 0 && !(psxCounters[index].mode & 0x40))) psxCounters[index].target |= IOPCNT_FUTURE_TARGET; _rcntSet( index ); }
void psxRcntWcount32(int index, u32 value) { u32 change; assert( index >= 3 && index < 6 ); PSXCNT_LOG("IOP Counter[%d] writeCount32 = %x\n", index, value); if(psxCounters[index].rate != PSXHBLANK) { // Re-adjust the sCycleT to match where the counter is currently // (remainder of the rate divided into the time passed will do the trick) change = psxRegs.cycle - psxCounters[index].sCycleT; psxCounters[index].sCycleT = psxRegs.cycle - (change % psxCounters[index].rate); } psxCounters[index].count = value & 0xffffffff; psxCounters[index].target &= 0xffffffff; _rcntSet( index ); }
void psxRcnt3Wmode(u32 value) { PSXCNT_LOG("IOP Counter[3] writeMode = %lx\n", value); psxCounters[3].mode = value; psxCounters[3].rate = 1; psxCounters[3].mode|= 0x0400; if(value & IOPCNT_ALT_SOURCE) psxCounters[3].rate = PSXHBLANK; if(psxCounters[3].mode & IOPCNT_ENABLE_GATE) { PSXCNT_LOG("IOP Counter[3] Gate Check set, value = %x\n", value); psxvblankgate |= 1<<3; } else psxvblankgate &= ~(1<<3); psxCounters[3].count = 0; psxCounters[3].sCycleT = psxRegs.cycle; psxCounters[3].target &= 0xffffffff; _rcntSet( 3 ); }
void psxRcnt2Wmode(u32 value) { PSXCNT_LOG("IOP Counter[0] writeMode = %lx\n", value); psxCounters[2].mode = value; psxCounters[2].mode|= 0x0400; switch(value & 0x200) { case 0x200: psxCounters[2].rate = 8; break; case 0x000: psxCounters[2].rate = 1; break; } if((psxCounters[2].mode & 0x7) == 0x7 || (psxCounters[2].mode & 0x7) == 0x1) { //SysPrintf("Gate set on IOP C2, disabling\n"); psxCounters[2].mode |= IOPCNT_STOPPED; } psxCounters[2].count = 0; psxCounters[2].sCycleT = psxRegs.cycle; psxCounters[2].target &= 0xffff; _rcntSet( 2 ); }
void psxRcntUpdate() { int i; //u32 change = 0; for (i=0; i<=5; i++) { s32 change = psxRegs.cycle - psxCounters[i].sCycleT; // don't count disabled, gated, or hblank counters... // We can't check the ALTSOURCE flag because the PSXCLOCK source *should* // be counted here. if( psxCounters[i].mode & (IOPCNT_STOPPED | IOPCNT_ENABLE_GATE) ) continue; if( psxCounters[i].rate == PSXHBLANK ) continue; if( change <= 0 ) continue; psxCounters[i].count += change / psxCounters[i].rate; if(psxCounters[i].rate != 1) { change -= (change / psxCounters[i].rate) * psxCounters[i].rate; psxCounters[i].sCycleT = psxRegs.cycle - change; } else psxCounters[i].sCycleT = psxRegs.cycle; } // Do target/overflow testing // Optimization Note: This approach is very sound. Please do not try to unroll it // as the size of the Test functions will cause code cache clutter and slowness. for( i=0; i<6; i++ ) { // don't do target/oveflow checks for hblankers. Those // checks are done when the counters are updated. if( psxCounters[i].rate == PSXHBLANK ) continue; if( psxCounters[i].mode & IOPCNT_STOPPED ) continue; _rcntTestTarget( i ); _rcntTestOverflow( i ); // perform second target test because if we overflowed above it's possible we // already shot past our target if it was very near zero. //if( psxCounters[i].count >= psxCounters[i].target ) _rcntTestTarget( i ); } psxNextCounter = 0xffffff; psxNextsCounter = psxRegs.cycle; if(SPU2async) { const s32 difference = psxRegs.cycle - psxCounters[6].sCycleT; s32 c = psxCounters[6].CycleT; if(difference >= psxCounters[6].CycleT) { SPU2async(difference); psxCounters[6].sCycleT = psxRegs.cycle; psxCounters[6].CycleT = psxCounters[6].rate; } else c -= difference; psxNextCounter = c; } if(USBasync) { const s32 difference = psxRegs.cycle - psxCounters[7].sCycleT; s32 c = psxCounters[7].CycleT; if(difference >= psxCounters[7].CycleT) { USBasync(difference); psxCounters[7].sCycleT = psxRegs.cycle; psxCounters[7].CycleT = psxCounters[7].rate; } else c -= difference; if (c < psxNextCounter) psxNextCounter = c; } for (i=0; i<6; i++) _rcntSet( i ); }
__fi void psxRcntWmode32( int index, u32 value ) { PSXCNT_LOG("32bit IOP Counter[%d] writeMode = 0x%04x", index, value ); int irqmode = 0; pxAssume( index >= 3 && index < 6 ); psxCounter& counter = psxCounters[index]; counter.mode = value; counter.mode |= 0x0400; //IRQ enable if (value & (1 << 4)) { irqmode += 1; } if (value & (1 << 5)) { irqmode += 2; } if (value & (1 << 7)) { PSXCNT_LOG("32 Counter %d Toggle IRQ on %s", index, (irqmode & 3) == 1 ? "Target" : ((irqmode & 3) == 2 ? "Overflow" : "Target and Overflow")); } else { PSXCNT_LOG("32 Counter %d Pulsed IRQ on %s", index, (irqmode & 3) == 1 ? "Target" : ((irqmode & 3) == 2 ? "Overflow" : "Target and Overflow")); } if (!(value & (1 << 6))) { PSXCNT_LOG("32 Counter %d One Shot", index); } else { PSXCNT_LOG("32 Counter %d Repeat", index); } if( index == 3 ) { // Counter 3 has the HBlank as an alternate source. counter.rate = 1; if(value & IOPCNT_ALT_SOURCE) counter.rate = PSXHBLANK; if(counter.mode & IOPCNT_ENABLE_GATE) { PSXCNT_LOG("IOP Counter[3] Gate Check set, value = %x", value); counter.mode |= IOPCNT_STOPPED; psxvblankgate |= 1<<3; } else psxvblankgate &= ~(1<<3); } else { switch(value & 0x6000) { case 0x0000: counter.rate = 1; break; case 0x2000: counter.rate = 8; break; case 0x4000: counter.rate = 16; break; case 0x6000: counter.rate = 256; break; } // Need to set a rate and target if((counter.mode & 0x7) == 0x7 || (counter.mode & 0x7) == 0x1) { Console.WriteLn( "Gate set on IOP Counter %d, disabling", index ); counter.mode |= IOPCNT_STOPPED; } } counter.count = 0; counter.sCycleT = psxRegs.cycle; counter.target &= 0xffffffff; _rcntSet( index ); }
void psxRcntUpdate() { int i; //u32 change = 0; g_iopNextEventCycle = psxRegs.cycle + 32; psxNextCounter = 0x7fffffff; psxNextsCounter = psxRegs.cycle; for (i=0; i<=5; i++) { s32 change = psxRegs.cycle - psxCounters[i].sCycleT; // don't count disabled or hblank counters... // We can't check the ALTSOURCE flag because the PSXCLOCK source *should* // be counted here. if( psxCounters[i].mode & IOPCNT_STOPPED ) continue; if ((psxCounters[i].mode & 0x40) && !(psxCounters[i].mode & 0x80)) { //Repeat IRQ mode Pulsed, resets a few cycles after the interrupt, this should do. psxCounters[i].mode |= 0x400; } if( psxCounters[i].rate == PSXHBLANK ) continue; if( change <= 0 ) continue; psxCounters[i].count += change / psxCounters[i].rate; if(psxCounters[i].rate != 1) { change -= (change / psxCounters[i].rate) * psxCounters[i].rate; psxCounters[i].sCycleT = psxRegs.cycle - change; } else psxCounters[i].sCycleT = psxRegs.cycle; } // Do target/overflow testing // Optimization Note: This approach is very sound. Please do not try to unroll it // as the size of the Test functions will cause code cache clutter and slowness. for( i=0; i<6; i++ ) { // don't do target/oveflow checks for hblankers. Those // checks are done when the counters are updated. if( psxCounters[i].rate == PSXHBLANK ) continue; if( psxCounters[i].mode & IOPCNT_STOPPED ) continue; _rcntTestTarget( i ); _rcntTestOverflow( i ); // perform second target test because if we overflowed above it's possible we // already shot past our target if it was very near zero. //if( psxCounters[i].count >= psxCounters[i].target ) _rcntTestTarget( i ); } if(SPU2async) { const s32 difference = psxRegs.cycle - psxCounters[6].sCycleT; s32 c = psxCounters[6].CycleT; if(difference >= psxCounters[6].CycleT) { SPU2async(difference); psxCounters[6].sCycleT = psxRegs.cycle; psxCounters[6].CycleT = psxCounters[6].rate; } else c -= difference; psxNextCounter = c; } if (DEV9async) { DEV9async(1); } if(USBasync) { const s32 difference = psxRegs.cycle - psxCounters[7].sCycleT; s32 c = psxCounters[7].CycleT; if(difference >= psxCounters[7].CycleT) { USBasync(difference); psxCounters[7].sCycleT = psxRegs.cycle; psxCounters[7].CycleT = psxCounters[7].rate; } else c -= difference; if (c < psxNextCounter) psxNextCounter = c; } #ifdef ENABLE_NEW_IOPDMA // New Iop DMA handler WIP { const s32 difference = psxRegs.cycle - psxCounters[8].sCycleT; s32 c = psxCounters[8].CycleT; if(difference >= psxCounters[8].CycleT) { psxCounters[8].sCycleT = psxRegs.cycle; psxCounters[8].CycleT = psxCounters[8].rate; IopDmaUpdate(difference); } else c -= difference; if (c < psxNextCounter) psxNextCounter = c; } #endif for (i=0; i<6; i++) _rcntSet( i ); }