Esempio n. 1
0
/**
 * Find next interrupt to occur, and store to global variables for decrement
 * in instruction decode loop.
 * Note: Although InterruptHandlers.Cycles and LowestCycleCount are 64 bit
 * variables to get all the cycle counters right (e.g. the DMA sound counter
 * can get very high), PendingInterruptCount is still a 32 bit variable for
 * performance reasons (it's decremented after each CPU instruction).
 * So we have to initialize LowestCycleCount with INT_MAX, not with INT64_MAX!
 * Since there is always a VBL or HBL counter pending which fits fine into the
 * 32 bit variable, we can be sure that we don't run into problems here.
 */
static void CycInt_SetNewInterrupt(void)
{
	Sint64 LowestCycleCount = INT_MAX;
	interrupt_id LowestInterrupt = INTERRUPT_NULL, i;

	LOG_TRACE(TRACE_INT, "int set new in video_cyc=%d active_int=%d pending_count=%d\n",
	          Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount);

	/* Find next interrupt to go off */
	for (i = INTERRUPT_NULL+1; i < MAX_INTERRUPTS; i++)
	{
		/* Is interrupt pending? */
		if (InterruptHandlers[i].bUsed)
		{
			if (InterruptHandlers[i].Cycles < LowestCycleCount)
			{
				LowestCycleCount = InterruptHandlers[i].Cycles;
				LowestInterrupt = i;
			}
		}
	}

	/* Set new counts, active interrupt */
	PendingInterruptCount = InterruptHandlers[LowestInterrupt].Cycles;
	PendingInterruptFunction = InterruptHandlers[LowestInterrupt].pFunction;
	ActiveInterrupt = LowestInterrupt;

	LOG_TRACE(TRACE_INT, "int set new out video_cyc=%d active_int=%d pending_count=%d\n",
	               Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, PendingInterruptCount );
}
Esempio n. 2
0
/**
 * Read a counter on CPU memory write access by taking care of the instruction
 * type (add the needed amount of additional cycles).
 */
int Cycles_GetCounterOnWriteAccess(int nId)
{
	int AddCycles;

	AddCycles = Cycles_GetInternalCycleOnWriteAccess();

	return Cycles_GetCounter(nId) + AddCycles;
}
Esempio n. 3
0
/**
 * Initialize CPU profiling when necessary.  Return true if profiling.
 */
bool Profile_CpuStart(void)
{
	int size;

	Profile_FreeCallinfo(&(cpu_callinfo));
	if (cpu_profile.sort_arr) {
		/* remove previous results */
		free(cpu_profile.sort_arr);
		free(cpu_profile.data);
		cpu_profile.sort_arr = NULL;
		cpu_profile.data = NULL;
		printf("Freed previous CPU profile buffers.\n");
	}
	if (!cpu_profile.enabled) {
		return false;
	}
	/* zero everything */
	memset(&cpu_profile, 0, sizeof(cpu_profile));

	/* Shouldn't change within same debug session */
	size = (STRamEnd + 0x20000 + TosSize) / 2;

	/* Add one entry for catching invalid PC values */
	cpu_profile.data = calloc(size + 1, sizeof(*cpu_profile.data));
	if (!cpu_profile.data) {
		perror("ERROR, new CPU profile buffer alloc failed");
		return false;
	}
	printf("Allocated CPU profile buffer (%d MB).\n",
	       (int)sizeof(*cpu_profile.data)*size/(1024*1024));
	cpu_profile.size = size;

	Profile_AllocCallinfo(&(cpu_callinfo), Symbols_CpuCount(), "CPU");

	/* special hack for EmuTOS */
	etos_switcher = PC_UNDEFINED;
	if (cpu_callinfo.sites && bIsEmuTOS &&
	    (!Symbols_GetCpuAddress(SYMTYPE_TEXT, "_switchto", &etos_switcher) || etos_switcher < TosAddress)) {
		etos_switcher = PC_UNDEFINED;
	}

	cpu_profile.prev_cycles = Cycles_GetCounter(CYCLES_COUNTER_CPU);
	cpu_profile.prev_family = OpcodeFamily;
	cpu_profile.prev_pc = M68000_GetPC() & 0xffffff;

	cpu_profile.loop_start = PC_UNDEFINED;
	cpu_profile.loop_end = PC_UNDEFINED;
	cpu_profile.loop_count = 0;
	Profile_LoopReset();

	cpu_profile.disasm_addr = 0;
	cpu_profile.processed = false;
	cpu_profile.enabled = true;
	return cpu_profile.enabled;
}
Esempio n. 4
0
/**
 * Return cycles passed for an interrupt handler
 */
int CycInt_FindCyclesPassed(interrupt_id Handler, int CycleType)
{
	Sint64 CyclesPassed, CyclesFromLastInterrupt;

	CyclesFromLastInterrupt = InterruptHandlers[ActiveInterrupt].Cycles - PendingInterruptCount;
	CyclesPassed = InterruptHandlers[Handler].Cycles - CyclesFromLastInterrupt;

	LOG_TRACE(TRACE_INT, "int find passed cyc video_cyc=%d handler=%d last_cyc=%lld passed_cyc=%lld\n",
	          Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
	          (long long)CyclesFromLastInterrupt, (long long)CyclesPassed);

	return INT_CONVERT_FROM_INTERNAL ( CyclesPassed , CycleType ) ;
}
Esempio n. 5
0
/**
 * Resume a stopped interrupt from its current cycle count (for MFP timers)
 */
void CycInt_ResumeStoppedInterrupt(interrupt_id Handler)
{
	/* Restart interrupt */
	InterruptHandlers[Handler].bUsed = true;

	/* Update list cycle counts */
	CycInt_UpdateInterrupt();
	/* Set new */
	CycInt_SetNewInterrupt();

	LOG_TRACE(TRACE_INT, "int resume stopped video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
	          Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
	          (long long)InterruptHandlers[Handler].Cycles, PendingInterruptCount);
}
Esempio n. 6
0
/**
 * Adjust all interrupt timings as 'ActiveInterrupt' has occured, and
 * remove from active list.
 */
void CycInt_AcknowledgeInterrupt(void)
{
	/* Update list cycle counts */
	CycInt_UpdateInterrupt();

	/* Disable interrupt entry which has just occured */
	InterruptHandlers[ActiveInterrupt].bUsed = false;

	/* Set new */
	CycInt_SetNewInterrupt();

	LOG_TRACE(TRACE_INT, "int ack video_cyc=%d active_int=%d active_cyc=%d pending_count=%d\n",
	               Cycles_GetCounter(CYCLES_COUNTER_VIDEO), ActiveInterrupt, (int)InterruptHandlers[ActiveInterrupt].Cycles, PendingInterruptCount );
}
Esempio n. 7
0
/**
 * Remove a pending interrupt from our table
 */
void CycInt_RemovePendingInterrupt(interrupt_id Handler)
{
	/* Update list cycle counts, including the handler we want to remove */
	/* to be able to resume it later (for MFP timers) */
	CycInt_UpdateInterrupt();

	/* Stop interrupt after CycInt_UpdateInterrupt, for CycInt_ResumeStoppedInterrupt */
	InterruptHandlers[Handler].bUsed = false;

	/* Set new */
	CycInt_SetNewInterrupt();

	LOG_TRACE(TRACE_INT, "int remove pending video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
	          Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
	          (long long)InterruptHandlers[Handler].Cycles, PendingInterruptCount);
}
Esempio n. 8
0
void CycInt_AddRelativeInterruptNoOffset(int CycleTime, int CycleType, interrupt_id Handler)
{
	/* Update list cycle counts before adding a new one, */
	/* since CycInt_SetNewInterrupt can change the active int / PendingInterruptCount */
	if ( ( ActiveInterrupt > 0 ) && ( PendingInterruptCount > 0 ) )
		CycInt_UpdateInterrupt();

//  nCyclesOver = 0;
	InterruptHandlers[Handler].bUsed = true;
	InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + PendingInterruptCount;

	/* Set new */
	CycInt_SetNewInterrupt();

	LOG_TRACE(TRACE_INT, "int add rel no_off video_cyc=%d handler=%d handler_cyc=%lld pending_count=%d\n",
	               Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler, InterruptHandlers[Handler].Cycles, PendingInterruptCount );
}
Esempio n. 9
0
/**
 * Add interrupt to occur after CycleTime/CycleType + CycleOffset.
 * CycleOffset can be used to add another delay to the resulting
 * number of internal cycles (should be 0 most of the time, except in
 * the MFP emulation to start timers precisely based on the number of
 * cycles of the current instruction).
 * This allows to restart an MFP timer just after it expired.
 */
void CycInt_AddRelativeInterruptWithOffset(int CycleTime, int CycleType, interrupt_id Handler, int CycleOffset)
{
	assert(CycleTime >= 0);

	/* Update list cycle counts with current PendingInterruptCount before adding a new int, */
	/* because CycInt_SetNewInterrupt can change the active int / PendingInterruptCount */
	if ( ActiveInterrupt > 0 )
		CycInt_UpdateInterrupt();

	InterruptHandlers[Handler].bUsed = true;
	InterruptHandlers[Handler].Cycles = INT_CONVERT_TO_INTERNAL((Sint64)CycleTime , CycleType) + CycleOffset;

	/* Set new active int and compute a new value for PendingInterruptCount*/
	CycInt_SetNewInterrupt();

	LOG_TRACE(TRACE_INT, "int add rel offset video_cyc=%d handler=%d handler_cyc=%lld offset_cyc=%d pending_count=%d\n",
	          Cycles_GetCounter(CYCLES_COUNTER_VIDEO), Handler,
	          (long long)InterruptHandlers[Handler].Cycles, CycleOffset, PendingInterruptCount);
}
Esempio n. 10
0
/**
 * Adjust all interrupt timings, MUST call CycInt_SetNewInterrupt after this.
 */
static void CycInt_UpdateInterrupt(void)
{
	Sint64 CycleSubtract;
	int i;

	/* Find out how many cycles we went over (<=0) */
	nCyclesOver = PendingInterruptCount;
	/* Calculate how many cycles have passed, included time we went over */
	CycleSubtract = InterruptHandlers[ActiveInterrupt].Cycles - nCyclesOver;

	/* Adjust table */
	for (i = 0; i < MAX_INTERRUPTS; i++)
	{
		if (InterruptHandlers[i].bUsed)
			InterruptHandlers[i].Cycles -= CycleSubtract;
	}

	LOG_TRACE(TRACE_INT, "int upd video_cyc=%d cycle_over=%d cycle_sub=%lld\n",
	          Cycles_GetCounter(CYCLES_COUNTER_VIDEO), nCyclesOver,
	          (long long)CycleSubtract);
}
Esempio n. 11
0
/**
 * Update CPU cycle and count statistics for PC address.
 *
 * This gets called after instruction has executed and PC
 * has advanced to next instruction.
 */
void Profile_CpuUpdate(void)
{
	counters_t *counters = &(cpu_profile.all);
	Uint32 pc, prev_pc, idx, cycles, misses;
	cpu_profile_item_t *prev;

	prev_pc = cpu_profile.prev_pc;
	/* PC may have extra bits, they need to be masked away as
	 * emulation itself does that too when PC value is used
	 */
	cpu_profile.prev_pc = pc = M68000_GetPC() & 0xffffff;

	if (unlikely(profile_loop.fp)) {
		if (pc < prev_pc) {
			if (pc == cpu_profile.loop_start && prev_pc == cpu_profile.loop_end) {
				cpu_profile.loop_count++;
			} else {
				cpu_profile.loop_start = pc;
				cpu_profile.loop_end = prev_pc;
				cpu_profile.loop_count = 1;
			}
		} else {
			if (pc > cpu_profile.loop_end) {
				log_last_loop();
				cpu_profile.loop_end = 0xffffffff;			
				cpu_profile.loop_count = 0;
			}
		}
	}

	idx = address2index(prev_pc);
	assert(idx <= cpu_profile.size);
	prev = cpu_profile.data + idx;

	if (likely(prev->count < MAX_CPU_PROFILE_VALUE)) {
		prev->count++;
	}

#if USE_CYCLES_COUNTER
	/* Confusingly, with DSP enabled, cycle counter is for this instruction,
	 * without DSP enabled, it's a monotonically increasing counter.
	 */
	if (bDspEnabled) {
		cycles = Cycles_GetCounter(CYCLES_COUNTER_CPU);
	} else {
		Uint32 newcycles = Cycles_GetCounter(CYCLES_COUNTER_CPU);
		cycles = newcycles - cpu_profile.prev_cycles;
		cpu_profile.prev_cycles = newcycles;
	}
#else
	cycles = CurrentInstrCycles + nWaitStateCycles;
#endif
	/* cycles are based on 8Mhz clock, change them to correct one */
	cycles <<= nCpuFreqShift;

	if (likely(prev->cycles < MAX_CPU_PROFILE_VALUE - cycles)) {
		prev->cycles += cycles;
	} else {
		prev->cycles = MAX_CPU_PROFILE_VALUE;
	}

#if ENABLE_WINUAE_CPU
	misses = CpuInstruction.iCacheMisses;
	assert(misses < MAX_MISS);
	cpu_profile.miss_counts[misses]++;
	if (likely(prev->misses < MAX_CPU_PROFILE_VALUE - misses)) {
		prev->misses += misses;
	} else {
		prev->misses = MAX_CPU_PROFILE_VALUE;
	}
#else
	misses = 0;
#endif
	if (cpu_callinfo.sites) {
		collect_calls(prev_pc, counters);
	}
	/* counters are increased after caller info is processed,
	 * otherwise cost for the instruction calling the callee
	 * doesn't get accounted to caller (but callee).
	 */
	counters->misses += misses;
	counters->cycles += cycles;
	counters->count++;

#if DEBUG
	if (unlikely(OpcodeFamily == 0)) {
		Uint32 nextpc;
		fputs("WARNING: instruction opcode family is zero (=i_ILLG) for instruction:\n", stderr);
		Disasm(stderr, prev_pc, &nextpc, 1);
	}
	/* catch too large (and negative) cycles for other than STOP instruction */
	if (unlikely(cycles > 512 && OpcodeFamily != i_STOP)) {
		Uint32 nextpc;
		fprintf(stderr, "WARNING: cycles %d > 512:\n", cycles);
		Disasm(stderr, prev_pc, &nextpc, 1);
	}
	if (unlikely(cycles == 0)) {
		Uint32 nextpc;
		fputs("WARNING: Zero cycles for an opcode:\n", stderr);
		Disasm(stderr, prev_pc, &nextpc, 1);
	}
#endif
}