Exemplo n.º 1
0
void MacroAssembler::lowLevelDebug(const char *s,
								   Register ra,
								   Register rb,
								   Register rc)
{
	Q_ASSERT(ra.code() < 13 && rb.code() < 13 && rc.code() < 13);
	int preserved = 0x1fff | lr.bit();
	stm(db_w, sp, preserved);
	add(fp, sp, Operand(12*4));
	mrs(r4, CPSR);

	Label omitString;
	b(&omitString);

	int sPtr = intptr_t(buffer_ + pcOffset());
	do {
		db(*s);
	} while (*(s++));
	while (pcOffset() & 3)
		db('\0');

	bind(&omitString);
	ldr(r3, MemOperand(sp, rc.code()*4));
	ldr(r2, MemOperand(sp, rb.code()*4));
	ldr(r1, MemOperand(sp, ra.code()*4));
	mov(r0, Operand(sPtr));

	void (*qDebugPtr)(const char *,...) = &qDebug;
	mov(ip, Operand(intptr_t(qDebugPtr)));
	blx(ip);

	msr(CPSR_f, Operand(r4));
	ldm(ia_w, sp, preserved);
}
Exemplo n.º 2
0
/*!
	Adds cycles from cpuRecData.additionalCycles to mCycles and sets
	cpuRecData.additionalCycles to 0. additionalCycles member holds
	postponed cycles in case of interrupt pending or DMA transfer.
 */
void NesCpuTranslator::mFetchAdditionalCycles()
{
	__ ldr(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,additionalCycles)));
	__ add(mCycles, mCycles, Operand(ip), SetCC);
	__ mov(ip, Operand(0));
	__ str(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,additionalCycles)));
}
Exemplo n.º 3
0
void NesCpuTranslator::mHandleInterrupts()
{
	// code at the bottom does following job:
	//
	// if (interrupts & mNmiInterrupt) {
	//     hwInterrupt(NmiVectorAddress);
	//     interrupts &= ~mNmiInterrupt;
	// } else if (interrupts & mIrqInterrupt) {
	//     if (!(mFlags & mIrqDisable)) {
	//         hwInterrupt(IrqVectorAddress);
	//     }
	// }

	// r0 - 6502.PC address of the next instruction

	Label exitInterruptCheck;
	Label irqPending;
	Label doInterrupt;

	__ ldr(r1, MemOperand(mDataBase, offsetof(NesCpuRecData,interrupts)));
	__ mov(r1, r1, SetCC);
	__ b(&exitInterruptCheck, eq);

	__ tst(r1, Operand(NesCpuBase::NmiInterrupt));
	__ b(&irqPending, eq);

	// NMI is handled here
	// clear NMI flag
	__ bic(r1, r1, Operand(NesCpuBase::NmiInterrupt));
	__ str(r1, MemOperand(mDataBase, offsetof(NesCpuRecData,interrupts)));
	__ mov(r1, Operand(NesCpuBase::NmiVectorAddress));
	__ b(&doInterrupt);

// irqPending:
	// IRQ is handled here
	// check if P.I is cleared
	__ bind(&irqPending);
	__ tst(mFlags, Operand(NesCpuBase::IrqDisable));
	__ b(&exitInterruptCheck, ne);
	__ mov(r1, Operand(NesCpuBase::IrqVectorAddress));

// doInterrupt:
	__ bind(&doInterrupt);
	// interrupt occured - handle it
	mHwInterrupt();

// exitInterruptCheck:
	__ bind(&exitInterruptCheck);
}
Exemplo n.º 4
0
void NesSyncCompiler::mLeaveToCpu()
{
	__ add(r3, pc, Operand(Assembler::kInstrSize));
	__ str(r3, MemOperand(m_dataBase, offsetof(NesSyncData,nextPc)));
	// return to cpu emulation
	__ ldm(ia_w, sp, m_regList | pc.bit());
	// nesSyncRecData.nextPc points here now
}
Exemplo n.º 5
0
void NesCpuTranslator::mRestoreInternalFlags()
{
#if !defined(FRAME_POINTER_FOR_GDB)
	__ msr(CPSR_f, Operand(mInternalFlagsCopy));
#else
	__ ldr(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,internalFlagsCopy)));
	__ msr(CPSR_f, Operand(ip));
#endif
}
Exemplo n.º 6
0
void NesCpuTranslator::mSaveInternalFlags()
{
#if !defined(FRAME_POINTER_FOR_GDB)
	__ mrs(mInternalFlagsCopy, CPSR);
#else
	__ mrs(ip, CPSR);
	__ str(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,internalFlagsCopy)));
#endif
}
Exemplo n.º 7
0
void NesSyncCompiler::mCallCFunction(int offsetInData)
{
#if defined(CAN_USE_ARMV7_INSTRUCTIONS)
	__ mov(ip, Operand(*(u32 *)((u8 *)&syncData + offsetInData)));
	__ blx(ip);
#else
	__ ldr(ip, MemOperand(m_dataBase, offsetInData));
	__ blx(ip);
#endif
}
Exemplo n.º 8
0
/*!
	Compiles a code that loads address pointed by a label at the given \a offset
	in 6502 address space to the \a dst. The label can be bound to the
	recompiled code or to mTranslateCaller if translation haven't occured for
	the given \a offset.
 */
inline void NesCpuTranslator::mLoadLabelAddress(Register offset, Register dst)
{
	Q_ASSERT(offset != ip && dst != ip);

	__ mov(dst, Operand(intptr_t(m_codeBuffer)));
	__ mov(ip, Operand(intptr_t(m_labels)));
	__ ldr(ip, MemOperand(ip, offset, LSL, 2));
	// dst = buffer - pos - 1
	__ sub(dst, dst, Operand(ip));
	__ bic(dst, dst, Operand(3));
}
Exemplo n.º 9
0
inline void NesCpuTranslator::mCheckAlert()
{
	// check if write caused an alert
	__ ldr(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,alert)));
	__ add(pc, pc, Operand(ip, LSL, 2));
	// some pad needed - write current 6502.PC
	__ dd(currentPc());
	// save state can happen in alert handler - r0 must contain 6502.PC
	__ mov(r0, Operand(currentPc()));
	// handle an alert if needed
	__ bl(&m_alertHandlerLabel);
	// continue otherwise
}
Exemplo n.º 10
0
void NesCpuTranslator::mHandleEvent()
{
	Label saveState;
	Label loadState;
	Label exit;

	// arguments: r1 = event

	// reg list for saving state
	// r0 contains 6502.PC address
	// r2 contains 6502.P flags
	RegList regList =	r0.bit() | r2.bit() | mA.bit() |
						mX.bit() | mY.bit() | mS.bit();

	// regs member of NesCpuRecData should be placed at the start of the object
	Q_ASSERT(offsetof(NesCpuRecData,regs) == 0);
	__ cmp(r1, Operand(NesCpuBase::SaveStateEvent));
	__ b(&saveState, eq);
	__ cmp(r1, Operand(NesCpuBase::LoadStateEvent));
	__ b(&loadState, eq);

// exitEvent:
	mExitPoint();

// loadState:
	__ bind(&loadState);
	__ ldm(ia, mDataBase, regList);
	mUnpackFlags(r2, r1);
	mSaveInternalFlags();
	mLoadLabelAddress(r0, lr);
	// mCycles is zeroed later
	__ b(&exit);

// saveState:
	__ bind(&saveState);
	mRestoreInternalFlags();
	mPackFlags(r2);
	__ stm(ia, mDataBase, regList);

	// saveState occurs on frame end, ticks() will be used on new frame
	// so set it to zero
	__ mov(ip, Operand(0));
	__ str(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,currentCycles)));

// exit:
	__ bind(&exit);
}
Exemplo n.º 11
0
void NesSyncCompiler::mEntryPoint()
{
	// r0 = additionalCpuCycles - the amount of cycles that CPU emulation
	//      executed minus requested cycles to execute

	__ stm(db_w, sp, m_regList | lr.bit());
#if defined(FRAME_POINTER_FOR_GDB)
	__ add(fp, sp, Operand(3*4)); // point to lr - omit r9,r10,fp
#endif
	// one step before we passed an amount of cycles that CPU should run,
	// but it's almost impossible to clock CPU by exact cycles, it will
	// often run more, and we must track these additional cycles
	// save additional CPU cycles, they will be used later in mClock
	__ mov(m_additionalCpuCycles, r0);
	// jump to the point the frame generation was before
	__ mov(m_dataBase, Operand(reinterpret_cast<int>(&syncData)));
	__ ldr(pc, MemOperand(m_dataBase, offsetof(NesSyncData,nextPc)));
}
Exemplo n.º 12
0
void NesCpuTranslator::mClearAlert()
{
	__ mov(ip, Operand(NesCpuRecData::AlertOff));
	__ str(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,alert)));
}
Exemplo n.º 13
0
void NesCpuTranslator::mSync()
{
	// r0 - 6502.PC address of the next instruction

	__ bind(&m_syncLabel);
	Label exitSync;
	Label handleEvent;
	mSaveInternalFlags();
	// IRQ can be cleared, so we must fetch additional cycles every time
	mFetchAdditionalCycles();
	mHandleInterrupts();

	// IRQ may be pending and P.I can be set, in this case we may not call
	// nesSync if mCycles < 0
	__ mov(mCycles, mCycles, SetCC);
	__ b(&m_checkInterruptsForNextInstruction, mi);

	__ bind(&m_syncWithoutInterruptHandling);

	int preserved = r0.bit() | lr.bit();
#if defined(FRAME_POINTER_FOR_GDB)
	preserved |= fp.bit();
#endif
	__ stm(db_w, sp, preserved);
#if defined(FRAME_POINTER_FOR_GDB)
	__ add(fp, sp, Operand(2*4));
#endif

	Label dontSyncWithApuAndClock;
	__ ldr(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,startCycles)));
	__ add(r0, mCycles, ip, SetCC);
	__ b(&dontSyncWithApuAndClock, eq);
	mCallCFunction(offsetof(NesCpuRecData,apuClock));
	if (nesMapper->hasClock()) {
		__ ldr(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,startCycles)));
		__ add(r0, mCycles, ip, SetCC);
		mCallCFunction(offsetof(NesCpuRecData,mapperClock));
	}
	__ bind(&dontSyncWithApuAndClock);

	__ mov(r0, mCycles);
	mCallCFunction(offsetof(NesCpuRecData,nesSync));
	__ str(r0, MemOperand(mDataBase, offsetof(NesCpuRecData,startCycles)));
	__ rsb(mCycles, r0, Operand(0), SetCC);
	__ ldm(ia_w, sp, preserved);

	// if r0 >= 0 then it means we should handle an event
	__ b(&handleEvent, pl);

	// interrupts handling is a little tricky:
	//   - once interrupt occurs save cycles in the memory
	//   - force mSync to be called next time by setting mCycles to zero
	//   - handle interrupt in the new mSync call
	__ bind(&m_checkInterruptsForNextInstruction);
	mClearAlert();
	__ ldr(ip, MemOperand(mDataBase, offsetof(NesCpuRecData,interrupts)));
	__ mov(ip, ip, SetCC);
	__ b(&exitSync, eq);
	// interrupt is pending here
	__ str(mCycles, MemOperand(mDataBase,
							   offsetof(NesCpuRecData,additionalCycles)));
	__ mov(mCycles, Operand(0));
	__ b(&exitSync);

// handleEvent:
	__ bind(&handleEvent);
	__ rsb(r1, mCycles, Operand(0));
	__ mov(mCycles, Operand(0));
	__ str(mCycles, MemOperand(mDataBase, offsetof(NesCpuRecData,startCycles)));
	mHandleEvent();
	__ b(&m_syncWithoutInterruptHandling);

// exitSync:
	__ bind(&exitSync);
	mRestoreInternalFlags();
	// r0 must be loaded here with 6502.PC address of the next instruction
	__ mov(pc, lr);
}
Exemplo n.º 14
0
/*!
	Loads C function address based on the given \a offset and calls
	this function.
 */
inline void NesCpuTranslator::mCallCFunction(int funcOffset)
{
	__ ldr(ip, MemOperand(mDataBase, funcOffset));
	__ blx(ip);
}
Exemplo n.º 15
0
/*!
	Stores mCycles to currentCycles member of cpuRecData, exact cycles are
	needed for APU writes.
 */
inline void NesCpuTranslator::mStoreCurrentCycles()
{
	__ str(mCycles, MemOperand(mDataBase,
							   offsetof(NesCpuRecData,currentCycles)));
}
Exemplo n.º 16
0
void NesSyncCompiler::mClock(int ppuCycles)
{
	/*
		Here the compiler will generate following function:

		syncData.baseCycleCounter += baseCycles;
		u64 cpuCyclesNow = syncData.baseCycleCounter / nesEmu.clockDividerForCpu();
		syncData.cpuCycleCounter += additionalCpuCycles;
		int newCpuCycles = cpuCyclesNow - syncData.cpuCycleCounter;
		if (newCpuCycles > 0) {
			syncData.cpuCycleCounter += newCpuCycles;
			return newCpuCycles;
		}
		return 0;

		If return value != 0 it will return to the cpu emulation also.
	*/
	int baseCycles = ppuCycles * nesEmu.clockDividerForPpu();
	Q_ASSERT(baseCycles >= 0);
	__ Ldrd(r0, r1, MemOperand(m_dataBase, offsetof(NesSyncData,baseCycleCounter)));
	__ add(r0, r0, Operand(baseCycles), SetCC);
	__ adc(r1, r1, Operand(0));
	__ Strd(r0, r1, MemOperand(m_dataBase, offsetof(NesSyncData,baseCycleCounter)));

	if (nesEmu.clockDividerForCpu() == 12
	#if !defined(CAN_USE_ARMV7_INSTRUCTIONS)
		|| nesEmu.clockDividerForCpu() == 16
	#endif
		) {
		__ mov(r2, Operand(nesEmu.clockDividerForCpu()));
		__ mov(r3, Operand(0));
		u8 *uldiv = reinterpret_cast<u8 *>(&__aeabi_uldivmod);
		__ mov(ip, Operand(reinterpret_cast<u32>(uldiv)));
		__ blx(ip);
#if defined(CAN_USE_ARMV7_INSTRUCTIONS)
	} else if (nesEmu.clockDividerForCpu() == 16) {
		__ bfi(r0, r1, 0, 4);
		__ mov(r0, Operand(r0, ROR, 4));
		__ mov(r1, Operand(r1, LSR, 4));
#endif
	} else {
		UNREACHABLE();
	}

	__ Ldrd(r2, r3, MemOperand(m_dataBase, offsetof(NesSyncData,cpuCycleCounter)));
	__ add(r2, r2, m_additionalCpuCycles, SetCC);
	__ adc(r3, r3, Operand(0));
	__ Strd(r2, r3, MemOperand(m_dataBase, offsetof(NesSyncData,cpuCycleCounter)));

	// clear additionalCpuCycles here because mClock can be executed multiple
	// times in single synchronization step
	__ mov(m_additionalCpuCycles, Operand(0));

	__ sub(r0, r0, r2);
	__ cmp(r0, Operand(0));
	__ mov(r0, Operand(0), LeaveCC, le);

	Label holdCpu;
	__ b(&holdCpu, le);

	__ add(r2, r2, r0, SetCC);
	__ adc(r3, r3, Operand(0));
	__ Strd(r2, r3, MemOperand(m_dataBase, offsetof(NesSyncData,cpuCycleCounter)));

	mLeaveToCpu();

	__ bind(&holdCpu);
}