- USB CDC 경로 복원: #define Serial Serial1 우회 제거 - rp2040js/avr8js/codemirror/xterm/monaco-editor vendor화 - /vendor 정적 라우팅 추가 (Express 5 catch-all 우회) - 부팅 완료 타임아웃 감지 (Serial 출력 없어도 8초 후 RAF 전환) - 리셋 시 Simulator 재생성으로 주변 장치 상태 초기화 - 시리얼 패널의 내부 메시지를 console.log로 이동
1250 lines
46 KiB
JavaScript
1250 lines
46 KiB
JavaScript
import { MAX_HARDWARE_IRQ } from './irq.js';
|
|
import { APB_START_ADDRESS, SIO_START_ADDRESS } from './rp2040.js';
|
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
const EXC_RESET = 1;
|
|
const EXC_NMI = 2;
|
|
const EXC_HARDFAULT = 3;
|
|
const EXC_SVCALL = 11;
|
|
const EXC_PENDSV = 14;
|
|
const EXC_SYSTICK = 15;
|
|
const SYSM_APSR = 0;
|
|
const SYSM_IAPSR = 1;
|
|
const SYSM_EAPSR = 2;
|
|
const SYSM_XPSR = 3;
|
|
const SYSM_IPSR = 5;
|
|
const SYSM_EPSR = 6;
|
|
const SYSM_IEPSR = 7;
|
|
export const SYSM_MSP = 8;
|
|
export const SYSM_PSP = 9;
|
|
export const SYSM_PRIMASK = 16;
|
|
export const SYSM_CONTROL = 20;
|
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
// Lowest possible exception priority
|
|
const LOWEST_PRIORITY = 4;
|
|
var ExecutionMode;
|
|
(function (ExecutionMode) {
|
|
ExecutionMode[ExecutionMode["Mode_Thread"] = 0] = "Mode_Thread";
|
|
ExecutionMode[ExecutionMode["Mode_Handler"] = 1] = "Mode_Handler";
|
|
})(ExecutionMode || (ExecutionMode = {}));
|
|
function signExtend8(value) {
|
|
return (value << 24) >> 24;
|
|
}
|
|
function signExtend16(value) {
|
|
return (value << 16) >> 16;
|
|
}
|
|
const spRegister = 13;
|
|
const pcRegister = 15;
|
|
var StackPointerBank;
|
|
(function (StackPointerBank) {
|
|
StackPointerBank[StackPointerBank["SPmain"] = 0] = "SPmain";
|
|
StackPointerBank[StackPointerBank["SPprocess"] = 1] = "SPprocess";
|
|
})(StackPointerBank || (StackPointerBank = {}));
|
|
const LOG_NAME = 'CortexM0Core';
|
|
export class CortexM0Core {
|
|
constructor(rp2040) {
|
|
this.rp2040 = rp2040;
|
|
this.registers = new Uint32Array(16);
|
|
this.bankedSP = 0;
|
|
this.cycles = 0;
|
|
this.eventRegistered = false;
|
|
this.waiting = false;
|
|
// APSR fields
|
|
this.N = false;
|
|
this.C = false;
|
|
this.Z = false;
|
|
this.V = false;
|
|
// How many bytes to rewind the last break instruction
|
|
this.breakRewind = 0;
|
|
// PRIMASK fields
|
|
this.PM = false;
|
|
// CONTROL fields
|
|
this.SPSEL = StackPointerBank.SPmain;
|
|
this.nPRIV = false;
|
|
this.currentMode = ExecutionMode.Mode_Thread;
|
|
this.IPSR = 0;
|
|
this.interruptNMIMask = 0;
|
|
this.pendingInterrupts = 0;
|
|
this.enabledInterrupts = 0;
|
|
this.interruptPriorities = [0xffffffff, 0x0, 0x0, 0x0];
|
|
this.pendingNMI = false;
|
|
this.pendingPendSV = false;
|
|
this.pendingSVCall = false;
|
|
this.pendingSystick = false;
|
|
this.interruptsUpdated = false;
|
|
this.VTOR = 0;
|
|
this.SHPR2 = 0;
|
|
this.SHPR3 = 0;
|
|
/** Hook to listen for function calls - branch-link (BL/BLX) instructions */
|
|
this.blTaken = (core, blx) => {
|
|
void core; // surpress unused variable warnings
|
|
void blx;
|
|
};
|
|
this.SP = 0xfffffffc;
|
|
this.bankedSP = 0xfffffffc;
|
|
}
|
|
get logger() {
|
|
return this.rp2040.logger;
|
|
}
|
|
reset() {
|
|
this.SP = this.rp2040.readUint32(this.VTOR);
|
|
this.PC = this.rp2040.readUint32(this.VTOR + 4) & 0xfffffffe;
|
|
this.cycles = 0;
|
|
}
|
|
get SP() {
|
|
return this.registers[13];
|
|
}
|
|
set SP(value) {
|
|
this.registers[13] = value & ~0x3;
|
|
}
|
|
get LR() {
|
|
return this.registers[14];
|
|
}
|
|
set LR(value) {
|
|
this.registers[14] = value;
|
|
}
|
|
get PC() {
|
|
return this.registers[15];
|
|
}
|
|
set PC(value) {
|
|
this.registers[15] = value;
|
|
}
|
|
get APSR() {
|
|
return ((this.N ? 0x80000000 : 0) |
|
|
(this.Z ? 0x40000000 : 0) |
|
|
(this.C ? 0x20000000 : 0) |
|
|
(this.V ? 0x10000000 : 0));
|
|
}
|
|
set APSR(value) {
|
|
this.N = !!(value & 0x80000000);
|
|
this.Z = !!(value & 0x40000000);
|
|
this.C = !!(value & 0x20000000);
|
|
this.V = !!(value & 0x10000000);
|
|
}
|
|
get xPSR() {
|
|
return this.APSR | this.IPSR | (1 << 24);
|
|
}
|
|
set xPSR(value) {
|
|
this.APSR = value;
|
|
this.IPSR = value & 0x3f;
|
|
}
|
|
checkCondition(cond) {
|
|
// Evaluate base condition.
|
|
let result = false;
|
|
switch (cond >> 1) {
|
|
case 0b000:
|
|
result = this.Z;
|
|
break;
|
|
case 0b001:
|
|
result = this.C;
|
|
break;
|
|
case 0b010:
|
|
result = this.N;
|
|
break;
|
|
case 0b011:
|
|
result = this.V;
|
|
break;
|
|
case 0b100:
|
|
result = this.C && !this.Z;
|
|
break;
|
|
case 0b101:
|
|
result = this.N === this.V;
|
|
break;
|
|
case 0b110:
|
|
result = this.N === this.V && !this.Z;
|
|
break;
|
|
case 0b111:
|
|
result = true;
|
|
break;
|
|
}
|
|
return cond & 0b1 && cond != 0b1111 ? !result : result;
|
|
}
|
|
readUint32(address) {
|
|
return this.rp2040.readUint32(address);
|
|
}
|
|
readUint16(address) {
|
|
return this.rp2040.readUint16(address);
|
|
}
|
|
readUint8(address) {
|
|
return this.rp2040.readUint8(address);
|
|
}
|
|
writeUint32(address, value) {
|
|
this.rp2040.writeUint32(address, value);
|
|
}
|
|
writeUint16(address, value) {
|
|
this.rp2040.writeUint16(address, value);
|
|
}
|
|
writeUint8(address, value) {
|
|
this.rp2040.writeUint8(address, value);
|
|
}
|
|
switchStack(stack) {
|
|
if (this.SPSEL !== stack) {
|
|
const temp = this.SP;
|
|
this.SP = this.bankedSP;
|
|
this.bankedSP = temp;
|
|
this.SPSEL = stack;
|
|
}
|
|
}
|
|
get SPprocess() {
|
|
return this.SPSEL === StackPointerBank.SPprocess ? this.SP : this.bankedSP;
|
|
}
|
|
set SPprocess(value) {
|
|
if (this.SPSEL === StackPointerBank.SPprocess) {
|
|
this.SP = value;
|
|
}
|
|
else {
|
|
this.bankedSP = value >>> 0;
|
|
}
|
|
}
|
|
get SPmain() {
|
|
return this.SPSEL === StackPointerBank.SPmain ? this.SP : this.bankedSP;
|
|
}
|
|
set SPmain(value) {
|
|
if (this.SPSEL === StackPointerBank.SPmain) {
|
|
this.SP = value;
|
|
}
|
|
else {
|
|
this.bankedSP = value >>> 0;
|
|
}
|
|
}
|
|
exceptionEntry(exceptionNumber) {
|
|
// PushStack:
|
|
let framePtr = 0;
|
|
let framePtrAlign = 0;
|
|
if (this.SPSEL && this.currentMode === ExecutionMode.Mode_Thread) {
|
|
framePtrAlign = this.SPprocess & 0b100 ? 1 : 0;
|
|
this.SPprocess = (this.SPprocess - 0x20) & ~0b100;
|
|
framePtr = this.SPprocess;
|
|
}
|
|
else {
|
|
framePtrAlign = this.SPmain & 0b100 ? 1 : 0;
|
|
this.SPmain = (this.SPmain - 0x20) & ~0b100;
|
|
framePtr = this.SPmain;
|
|
}
|
|
/* only the stack locations, not the store order, are architected */
|
|
this.writeUint32(framePtr, this.registers[0]);
|
|
this.writeUint32(framePtr + 0x4, this.registers[1]);
|
|
this.writeUint32(framePtr + 0x8, this.registers[2]);
|
|
this.writeUint32(framePtr + 0xc, this.registers[3]);
|
|
this.writeUint32(framePtr + 0x10, this.registers[12]);
|
|
this.writeUint32(framePtr + 0x14, this.LR);
|
|
this.writeUint32(framePtr + 0x18, this.PC & ~1); // ReturnAddress(ExceptionType);
|
|
this.writeUint32(framePtr + 0x1c, (this.xPSR & ~(1 << 9)) | (framePtrAlign << 9));
|
|
if (this.currentMode == ExecutionMode.Mode_Handler) {
|
|
this.LR = 0xfffffff1;
|
|
}
|
|
else {
|
|
if (!this.SPSEL) {
|
|
this.LR = 0xfffffff9;
|
|
}
|
|
else {
|
|
this.LR = 0xfffffffd;
|
|
}
|
|
}
|
|
// ExceptionTaken:
|
|
this.currentMode = ExecutionMode.Mode_Handler; // Enter Handler Mode, now Privileged
|
|
this.IPSR = exceptionNumber;
|
|
this.switchStack(StackPointerBank.SPmain);
|
|
this.eventRegistered = true;
|
|
const vectorTable = this.VTOR;
|
|
this.PC = this.readUint32(vectorTable + 4 * exceptionNumber);
|
|
}
|
|
exceptionReturn(excReturn) {
|
|
let framePtr = this.SPmain;
|
|
switch (excReturn & 0xf) {
|
|
case 0b0001: // Return to Handler
|
|
this.currentMode = ExecutionMode.Mode_Handler;
|
|
this.switchStack(StackPointerBank.SPmain);
|
|
break;
|
|
case 0b1001: // Return to Thread using Main stack
|
|
this.currentMode = ExecutionMode.Mode_Thread;
|
|
this.switchStack(StackPointerBank.SPmain);
|
|
break;
|
|
case 0b1101: // Return to Thread using Process stack
|
|
framePtr = this.SPprocess;
|
|
this.currentMode = ExecutionMode.Mode_Thread;
|
|
this.switchStack(StackPointerBank.SPprocess);
|
|
break;
|
|
// Assigning CurrentMode to Mode_Thread causes a drop in privilege
|
|
// if CONTROL.nPRIV is set to 1
|
|
}
|
|
// PopStack:
|
|
this.registers[0] = this.readUint32(framePtr); // Stack accesses are performed as Unprivileged accesses if
|
|
this.registers[1] = this.readUint32(framePtr + 0x4); // CONTROL<0>=='1' && EXC_RETURN<3>=='1' Privileged otherwise
|
|
this.registers[2] = this.readUint32(framePtr + 0x8);
|
|
this.registers[3] = this.readUint32(framePtr + 0xc);
|
|
this.registers[12] = this.readUint32(framePtr + 0x10);
|
|
this.LR = this.readUint32(framePtr + 0x14);
|
|
this.PC = this.readUint32(framePtr + 0x18);
|
|
const psr = this.readUint32(framePtr + 0x1c);
|
|
const framePtrAlign = psr & (1 << 9) ? 0b100 : 0;
|
|
switch (excReturn & 0xf) {
|
|
case 0b0001: // Returning to Handler mode
|
|
this.SPmain = (this.SPmain + 0x20) | framePtrAlign;
|
|
break;
|
|
case 0b1001: // Returning to Thread mode using Main stack
|
|
this.SPmain = (this.SPmain + 0x20) | framePtrAlign;
|
|
break;
|
|
case 0b1101: // Returning to Thread mode using Process stack
|
|
this.SPprocess = (this.SPprocess + 0x20) | framePtrAlign;
|
|
break;
|
|
}
|
|
this.APSR = psr & 0xf0000000;
|
|
const forceThread = this.currentMode == ExecutionMode.Mode_Thread && this.nPRIV;
|
|
this.IPSR = forceThread ? 0 : psr & 0x3f;
|
|
this.interruptsUpdated = true;
|
|
// Thumb bit should always be one! EPSR<24> = psr<24>; // Load valid EPSR bits from memory
|
|
this.eventRegistered = true;
|
|
// if CurrentMode == Mode_Thread && SCR.SLEEPONEXIT == '1' then
|
|
// SleepOnExit(); // IMPLEMENTATION DEFINED
|
|
}
|
|
get pendSVPriority() {
|
|
return (this.SHPR3 >> 22) & 0x3;
|
|
}
|
|
get svCallPriority() {
|
|
return this.SHPR2 >>> 30;
|
|
}
|
|
get systickPriority() {
|
|
return this.SHPR3 >>> 30;
|
|
}
|
|
exceptionPriority(n) {
|
|
switch (n) {
|
|
case EXC_RESET:
|
|
return -3;
|
|
case EXC_NMI:
|
|
return -2;
|
|
case EXC_HARDFAULT:
|
|
return -1;
|
|
case EXC_SVCALL:
|
|
return this.svCallPriority;
|
|
case EXC_PENDSV:
|
|
return this.pendSVPriority;
|
|
case EXC_SYSTICK:
|
|
return this.systickPriority;
|
|
default: {
|
|
if (n < 16) {
|
|
return LOWEST_PRIORITY;
|
|
}
|
|
const intNum = n - 16;
|
|
for (let priority = 0; priority < 4; priority++) {
|
|
if (this.interruptPriorities[priority] & (1 << intNum)) {
|
|
return priority;
|
|
}
|
|
}
|
|
return LOWEST_PRIORITY;
|
|
}
|
|
}
|
|
}
|
|
get vectPending() {
|
|
if (this.pendingNMI) {
|
|
return EXC_NMI;
|
|
}
|
|
const { svCallPriority, systickPriority, pendSVPriority, pendingInterrupts } = this;
|
|
for (let priority = 0; priority < LOWEST_PRIORITY; priority++) {
|
|
const levelInterrupts = pendingInterrupts & this.interruptPriorities[priority];
|
|
if (this.pendingSVCall && priority === svCallPriority) {
|
|
return EXC_SVCALL;
|
|
}
|
|
if (this.pendingPendSV && priority === pendSVPriority) {
|
|
return EXC_PENDSV;
|
|
}
|
|
if (this.pendingSystick && priority === systickPriority) {
|
|
return EXC_SYSTICK;
|
|
}
|
|
if (levelInterrupts) {
|
|
for (let interruptNumber = 0; interruptNumber < 32; interruptNumber++) {
|
|
if (levelInterrupts & (1 << interruptNumber)) {
|
|
return 16 + interruptNumber;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
setInterrupt(irq, value) {
|
|
const irqBit = 1 << irq;
|
|
if (value && !(this.pendingInterrupts & irqBit)) {
|
|
this.pendingInterrupts |= irqBit;
|
|
this.interruptsUpdated = true;
|
|
if (this.waiting && this.checkForInterrupts()) {
|
|
this.waiting = false;
|
|
}
|
|
}
|
|
else if (!value) {
|
|
this.pendingInterrupts &= ~irqBit;
|
|
}
|
|
}
|
|
checkForInterrupts() {
|
|
/* If we're waiting for an interrupt (i.e. WFI/WFE), the ARM says:
|
|
> If PRIMASK.PM is set to 1, an asynchronous exception that has a higher group priority than any
|
|
> active exception results in a WFI instruction exit. If the group priority of the exception is less than or
|
|
> equal to the execution group priority, the exception is ignored.
|
|
*/
|
|
const currentPriority = this.waiting
|
|
? this.PM
|
|
? this.exceptionPriority(this.IPSR)
|
|
: LOWEST_PRIORITY
|
|
: Math.min(this.exceptionPriority(this.IPSR), this.PM ? 0 : LOWEST_PRIORITY);
|
|
const interruptSet = this.pendingInterrupts & this.enabledInterrupts;
|
|
const { svCallPriority, systickPriority, pendSVPriority } = this;
|
|
if (this.pendingNMI) {
|
|
this.pendingNMI = false;
|
|
this.exceptionEntry(EXC_NMI);
|
|
return true;
|
|
}
|
|
for (let priority = 0; priority < currentPriority; priority++) {
|
|
const levelInterrupts = interruptSet & this.interruptPriorities[priority];
|
|
if (this.pendingSVCall && priority === svCallPriority) {
|
|
this.pendingSVCall = false;
|
|
this.exceptionEntry(EXC_SVCALL);
|
|
return true;
|
|
}
|
|
if (this.pendingPendSV && priority === pendSVPriority) {
|
|
this.pendingPendSV = false;
|
|
this.exceptionEntry(EXC_PENDSV);
|
|
return true;
|
|
}
|
|
if (this.pendingSystick && priority === systickPriority) {
|
|
this.pendingSystick = false;
|
|
this.exceptionEntry(EXC_SYSTICK);
|
|
return true;
|
|
}
|
|
if (levelInterrupts) {
|
|
for (let interruptNumber = 0; interruptNumber < 32; interruptNumber++) {
|
|
if (levelInterrupts & (1 << interruptNumber)) {
|
|
if (interruptNumber > MAX_HARDWARE_IRQ) {
|
|
this.pendingInterrupts &= ~(1 << interruptNumber);
|
|
}
|
|
this.exceptionEntry(16 + interruptNumber);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.interruptsUpdated = false;
|
|
return false;
|
|
}
|
|
readSpecialRegister(sysm) {
|
|
switch (sysm) {
|
|
case SYSM_APSR:
|
|
return this.APSR;
|
|
case SYSM_XPSR:
|
|
return this.xPSR;
|
|
case SYSM_IPSR:
|
|
return this.IPSR;
|
|
case SYSM_PRIMASK:
|
|
return this.PM ? 1 : 0;
|
|
case SYSM_MSP:
|
|
return this.SPmain;
|
|
case SYSM_PSP:
|
|
return this.SPprocess;
|
|
case SYSM_CONTROL:
|
|
return (this.SPSEL === StackPointerBank.SPprocess ? 2 : 0) | (this.nPRIV ? 1 : 0);
|
|
default:
|
|
this.logger.warn(LOG_NAME, `MRS with unimplemented SYSm value: ${sysm}`);
|
|
return 0;
|
|
}
|
|
}
|
|
writeSpecialRegister(sysm, value) {
|
|
switch (sysm) {
|
|
case SYSM_APSR:
|
|
this.APSR = value;
|
|
break;
|
|
case SYSM_XPSR:
|
|
this.xPSR = value;
|
|
break;
|
|
case SYSM_IPSR:
|
|
this.IPSR = value;
|
|
break;
|
|
case SYSM_PRIMASK:
|
|
this.PM = !!(value & 1);
|
|
this.interruptsUpdated = true;
|
|
break;
|
|
case SYSM_MSP:
|
|
this.SPmain = value;
|
|
break;
|
|
case SYSM_PSP:
|
|
this.SPprocess = value;
|
|
break;
|
|
case SYSM_CONTROL:
|
|
this.nPRIV = !!(value & 1);
|
|
if (this.currentMode === ExecutionMode.Mode_Thread) {
|
|
this.switchStack(value & 2 ? StackPointerBank.SPprocess : StackPointerBank.SPmain);
|
|
}
|
|
break;
|
|
default:
|
|
this.logger.warn(LOG_NAME, `MRS with unimplemented SYSm value: ${sysm}`);
|
|
return 0;
|
|
}
|
|
}
|
|
BXWritePC(address) {
|
|
if (this.currentMode == ExecutionMode.Mode_Handler && address >>> 28 == 0b1111) {
|
|
this.exceptionReturn(address & 0x0fffffff);
|
|
}
|
|
else {
|
|
this.PC = address & ~1;
|
|
}
|
|
}
|
|
substractUpdateFlags(minuend, subtrahend) {
|
|
const result = minuend - subtrahend;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = (result & 0xffffffff) === 0;
|
|
this.C = minuend >= subtrahend;
|
|
this.V =
|
|
(!!(result & 0x80000000) && !(minuend & 0x80000000) && !!(subtrahend & 0x80000000)) ||
|
|
(!(result & 0x80000000) && !!(minuend & 0x80000000) && !(subtrahend & 0x80000000));
|
|
return result;
|
|
}
|
|
addUpdateFlags(addend1, addend2) {
|
|
const unsignedSum = (addend1 + addend2) >>> 0;
|
|
const signedSum = (addend1 | 0) + (addend2 | 0);
|
|
const result = addend1 + addend2;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = (result & 0xffffffff) === 0;
|
|
this.C = result === unsignedSum ? false : true;
|
|
this.V = (result | 0) === signedSum ? false : true;
|
|
return result & 0xffffffff;
|
|
}
|
|
cyclesIO(addr, write = false) {
|
|
addr = addr >>> 0;
|
|
if (addr >= SIO_START_ADDRESS && addr < SIO_START_ADDRESS + 0x10000000) {
|
|
return 0;
|
|
}
|
|
if (addr >= APB_START_ADDRESS && addr < APB_START_ADDRESS + 0x10000000) {
|
|
return write ? 4 : 3;
|
|
}
|
|
return 1;
|
|
}
|
|
executeInstruction() {
|
|
if (this.interruptsUpdated) {
|
|
if (this.checkForInterrupts()) {
|
|
this.waiting = false;
|
|
}
|
|
}
|
|
// ARM Thumb instruction encoding - 16 bits / 2 bytes
|
|
const opcodePC = this.PC & ~1; //ensure no LSB set PC are executed
|
|
const opcode = this.readUint16(opcodePC);
|
|
const wideInstruction = opcode >> 12 === 0b1111 || opcode >> 11 === 0b11101;
|
|
const opcode2 = wideInstruction ? this.readUint16(opcodePC + 2) : 0;
|
|
this.PC += 2;
|
|
let deltaCycles = 1;
|
|
// ADCS
|
|
if (opcode >> 6 === 0b0100000101) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
this.registers[Rdn] = this.addUpdateFlags(this.registers[Rm], this.registers[Rdn] + (this.C ? 1 : 0));
|
|
}
|
|
// ADD (register = SP plus immediate)
|
|
else if (opcode >> 11 === 0b10101) {
|
|
const imm8 = opcode & 0xff;
|
|
const Rd = (opcode >> 8) & 0x7;
|
|
this.registers[Rd] = this.SP + (imm8 << 2);
|
|
}
|
|
// ADD (SP plus immediate)
|
|
else if (opcode >> 7 === 0b101100000) {
|
|
const imm32 = (opcode & 0x7f) << 2;
|
|
this.SP += imm32;
|
|
}
|
|
// ADDS (Encoding T1)
|
|
else if (opcode >> 9 === 0b0001110) {
|
|
const imm3 = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
this.registers[Rd] = this.addUpdateFlags(this.registers[Rn], imm3);
|
|
}
|
|
// ADDS (Encoding T2)
|
|
else if (opcode >> 11 === 0b00110) {
|
|
const imm8 = opcode & 0xff;
|
|
const Rdn = (opcode >> 8) & 0x7;
|
|
this.registers[Rdn] = this.addUpdateFlags(this.registers[Rdn], imm8);
|
|
}
|
|
// ADDS (register)
|
|
else if (opcode >> 9 === 0b0001100) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
this.registers[Rd] = this.addUpdateFlags(this.registers[Rn], this.registers[Rm]);
|
|
}
|
|
// ADD (register)
|
|
else if (opcode >> 8 === 0b01000100) {
|
|
const Rm = (opcode >> 3) & 0xf;
|
|
const Rdn = ((opcode & 0x80) >> 4) | (opcode & 0x7);
|
|
const leftValue = Rdn === pcRegister ? this.PC + 2 : this.registers[Rdn];
|
|
const rightValue = Rm === pcRegister ? this.PC + 2 : this.registers[Rm];
|
|
const result = leftValue + rightValue;
|
|
if (Rdn !== spRegister && Rdn !== pcRegister) {
|
|
this.registers[Rdn] = result;
|
|
}
|
|
else if (Rdn === pcRegister) {
|
|
this.registers[Rdn] = result & ~0x1;
|
|
deltaCycles++;
|
|
}
|
|
else if (Rdn === spRegister) {
|
|
this.registers[Rdn] = result & ~0x3;
|
|
}
|
|
}
|
|
// ADR
|
|
else if (opcode >> 11 === 0b10100) {
|
|
const imm8 = opcode & 0xff;
|
|
const Rd = (opcode >> 8) & 0x7;
|
|
this.registers[Rd] = (opcodePC & 0xfffffffc) + 4 + (imm8 << 2);
|
|
}
|
|
// ANDS (Encoding T2)
|
|
else if (opcode >> 6 === 0b0100000000) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
const result = this.registers[Rdn] & this.registers[Rm];
|
|
this.registers[Rdn] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = (result & 0xffffffff) === 0;
|
|
}
|
|
// ASRS (immediate)
|
|
else if (opcode >> 11 === 0b00010) {
|
|
const imm5 = (opcode >> 6) & 0x1f;
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
const input = this.registers[Rm];
|
|
const shiftN = imm5 ? imm5 : 32;
|
|
const result = shiftN < 32 ? input >> shiftN : (input & 0x80000000) >> 31;
|
|
this.registers[Rd] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = (result & 0xffffffff) === 0;
|
|
this.C = input & (1 << (shiftN - 1)) ? true : false;
|
|
}
|
|
// ASRS (register)
|
|
else if (opcode >> 6 === 0b0100000100) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
const input = this.registers[Rdn];
|
|
const shiftN = (this.registers[Rm] & 0xff) < 32 ? this.registers[Rm] & 0xff : 32;
|
|
const result = shiftN < 32 ? input >> shiftN : (input & 0x80000000) >> 31;
|
|
this.registers[Rdn] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = (result & 0xffffffff) === 0;
|
|
this.C = input & (1 << (shiftN - 1)) ? true : false;
|
|
}
|
|
// B (with cond)
|
|
else if (opcode >> 12 === 0b1101 && ((opcode >> 9) & 0x7) !== 0b111) {
|
|
let imm8 = (opcode & 0xff) << 1;
|
|
const cond = (opcode >> 8) & 0xf;
|
|
if (imm8 & (1 << 8)) {
|
|
imm8 = (imm8 & 0x1ff) - 0x200;
|
|
}
|
|
if (this.checkCondition(cond)) {
|
|
this.PC += imm8 + 2;
|
|
deltaCycles++;
|
|
}
|
|
}
|
|
// B
|
|
else if (opcode >> 11 === 0b11100) {
|
|
let imm11 = (opcode & 0x7ff) << 1;
|
|
if (imm11 & (1 << 11)) {
|
|
imm11 = (imm11 & 0x7ff) - 0x800;
|
|
}
|
|
this.PC += imm11 + 2;
|
|
deltaCycles++;
|
|
}
|
|
// BICS
|
|
else if (opcode >> 6 === 0b0100001110) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
const result = (this.registers[Rdn] &= ~this.registers[Rm]);
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = result === 0;
|
|
}
|
|
// BKPT
|
|
else if (opcode >> 8 === 0b10111110) {
|
|
const imm8 = opcode & 0xff;
|
|
this.breakRewind = 2;
|
|
this.rp2040.onBreak(imm8);
|
|
}
|
|
// BL
|
|
else if (opcode >> 11 === 0b11110 && opcode2 >> 14 === 0b11 && ((opcode2 >> 12) & 0x1) == 1) {
|
|
const imm11 = opcode2 & 0x7ff;
|
|
const J2 = (opcode2 >> 11) & 0x1;
|
|
const J1 = (opcode2 >> 13) & 0x1;
|
|
const imm10 = opcode & 0x3ff;
|
|
const S = (opcode >> 10) & 0x1;
|
|
const I1 = 1 - (S ^ J1);
|
|
const I2 = 1 - (S ^ J2);
|
|
const imm32 = ((S ? 0b11111111 : 0) << 24) | ((I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1));
|
|
this.LR = (this.PC + 2) | 0x1;
|
|
this.PC += 2 + imm32;
|
|
deltaCycles += 2;
|
|
this.blTaken(this, false);
|
|
}
|
|
// BLX
|
|
else if (opcode >> 7 === 0b010001111 && (opcode & 0x7) === 0) {
|
|
const Rm = (opcode >> 3) & 0xf;
|
|
this.LR = this.PC | 0x1;
|
|
this.PC = this.registers[Rm] & ~1;
|
|
deltaCycles++;
|
|
this.blTaken(this, true);
|
|
}
|
|
// BX
|
|
else if (opcode >> 7 === 0b010001110 && (opcode & 0x7) === 0) {
|
|
const Rm = (opcode >> 3) & 0xf;
|
|
this.BXWritePC(this.registers[Rm]);
|
|
deltaCycles++;
|
|
}
|
|
// CMN (register)
|
|
else if (opcode >> 6 === 0b0100001011) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rn = opcode & 0x7;
|
|
this.addUpdateFlags(this.registers[Rn], this.registers[Rm]);
|
|
}
|
|
// CMP immediate
|
|
else if (opcode >> 11 === 0b00101) {
|
|
const Rn = (opcode >> 8) & 0x7;
|
|
const imm8 = opcode & 0xff;
|
|
this.substractUpdateFlags(this.registers[Rn], imm8);
|
|
}
|
|
// CMP (register)
|
|
else if (opcode >> 6 === 0b0100001010) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rn = opcode & 0x7;
|
|
this.substractUpdateFlags(this.registers[Rn], this.registers[Rm]);
|
|
}
|
|
// CMP (register) encoding T2
|
|
else if (opcode >> 8 === 0b01000101) {
|
|
const Rm = (opcode >> 3) & 0xf;
|
|
const Rn = ((opcode >> 4) & 0x8) | (opcode & 0x7);
|
|
this.substractUpdateFlags(this.registers[Rn], this.registers[Rm]);
|
|
}
|
|
// CPSID i
|
|
else if (opcode === 0xb672) {
|
|
this.PM = true;
|
|
}
|
|
// CPSIE i
|
|
else if (opcode === 0xb662) {
|
|
this.PM = false;
|
|
this.interruptsUpdated = true;
|
|
}
|
|
// DMB SY
|
|
else if (opcode === 0xf3bf && (opcode2 & 0xfff0) === 0x8f50) {
|
|
this.PC += 2;
|
|
deltaCycles += 2;
|
|
}
|
|
// DSB SY
|
|
else if (opcode === 0xf3bf && (opcode2 & 0xfff0) === 0x8f40) {
|
|
this.PC += 2;
|
|
deltaCycles += 2;
|
|
}
|
|
// EORS
|
|
else if (opcode >> 6 === 0b0100000001) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
const result = this.registers[Rm] ^ this.registers[Rdn];
|
|
this.registers[Rdn] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = result === 0;
|
|
}
|
|
// ISB SY
|
|
else if (opcode === 0xf3bf && (opcode2 & 0xfff0) === 0x8f60) {
|
|
this.PC += 2;
|
|
deltaCycles += 2;
|
|
}
|
|
// LDMIA
|
|
else if (opcode >> 11 === 0b11001) {
|
|
const Rn = (opcode >> 8) & 0x7;
|
|
const registers = opcode & 0xff;
|
|
let address = this.registers[Rn];
|
|
for (let i = 0; i < 8; i++) {
|
|
if (registers & (1 << i)) {
|
|
this.registers[i] = this.readUint32(address);
|
|
address += 4;
|
|
deltaCycles++;
|
|
}
|
|
}
|
|
// Write back
|
|
if (!(registers & (1 << Rn))) {
|
|
this.registers[Rn] = address;
|
|
}
|
|
}
|
|
// LDR (immediate)
|
|
else if (opcode >> 11 === 0b01101) {
|
|
const imm5 = ((opcode >> 6) & 0x1f) << 2;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const addr = this.registers[Rn] + imm5;
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = this.readUint32(addr);
|
|
}
|
|
// LDR (sp + immediate)
|
|
else if (opcode >> 11 === 0b10011) {
|
|
const Rt = (opcode >> 8) & 0x7;
|
|
const imm8 = opcode & 0xff;
|
|
const addr = this.SP + (imm8 << 2);
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = this.readUint32(addr);
|
|
}
|
|
// LDR (literal)
|
|
else if (opcode >> 11 === 0b01001) {
|
|
const imm8 = (opcode & 0xff) << 2;
|
|
const Rt = (opcode >> 8) & 7;
|
|
const nextPC = this.PC + 2;
|
|
const addr = (nextPC & 0xfffffffc) + imm8;
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = this.readUint32(addr);
|
|
}
|
|
// LDR (register)
|
|
else if (opcode >> 9 === 0b0101100) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const addr = this.registers[Rm] + this.registers[Rn];
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = this.readUint32(addr);
|
|
}
|
|
// LDRB (immediate)
|
|
else if (opcode >> 11 === 0b01111) {
|
|
const imm5 = (opcode >> 6) & 0x1f;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const addr = this.registers[Rn] + imm5;
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = this.readUint8(addr);
|
|
}
|
|
// LDRB (register)
|
|
else if (opcode >> 9 === 0b0101110) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const addr = this.registers[Rm] + this.registers[Rn];
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = this.readUint8(addr);
|
|
}
|
|
// LDRH (immediate)
|
|
else if (opcode >> 11 === 0b10001) {
|
|
const imm5 = (opcode >> 6) & 0x1f;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const addr = this.registers[Rn] + (imm5 << 1);
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = this.readUint16(addr);
|
|
}
|
|
// LDRH (register)
|
|
else if (opcode >> 9 === 0b0101101) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const addr = this.registers[Rm] + this.registers[Rn];
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = this.readUint16(addr);
|
|
}
|
|
// LDRSB
|
|
else if (opcode >> 9 === 0b0101011) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const addr = this.registers[Rm] + this.registers[Rn];
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = signExtend8(this.readUint8(addr));
|
|
}
|
|
// LDRSH
|
|
else if (opcode >> 9 === 0b0101111) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const addr = this.registers[Rm] + this.registers[Rn];
|
|
deltaCycles += this.cyclesIO(addr);
|
|
this.registers[Rt] = signExtend16(this.readUint16(addr));
|
|
}
|
|
// LSLS (immediate)
|
|
else if (opcode >> 11 === 0b00000) {
|
|
const imm5 = (opcode >> 6) & 0x1f;
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
const input = this.registers[Rm];
|
|
const result = input << imm5;
|
|
this.registers[Rd] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = result === 0;
|
|
this.C = imm5 ? !!(input & (1 << (32 - imm5))) : this.C;
|
|
}
|
|
// LSLS (register)
|
|
else if (opcode >> 6 === 0b0100000010) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
const input = this.registers[Rdn];
|
|
const shiftCount = this.registers[Rm] & 0xff;
|
|
const result = shiftCount >= 32 ? 0 : input << shiftCount;
|
|
this.registers[Rdn] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = result === 0;
|
|
this.C = shiftCount ? !!(input & (1 << (32 - shiftCount))) : this.C;
|
|
}
|
|
// LSRS (immediate)
|
|
else if (opcode >> 11 === 0b00001) {
|
|
const imm5 = (opcode >> 6) & 0x1f;
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
const input = this.registers[Rm];
|
|
const result = imm5 ? input >>> imm5 : 0;
|
|
this.registers[Rd] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = result === 0;
|
|
this.C = !!((input >>> (imm5 ? imm5 - 1 : 31)) & 0x1);
|
|
}
|
|
// LSRS (register)
|
|
else if (opcode >> 6 === 0b0100000011) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
const shiftAmount = this.registers[Rm] & 0xff;
|
|
const input = this.registers[Rdn];
|
|
const result = shiftAmount < 32 ? input >>> shiftAmount : 0;
|
|
this.registers[Rdn] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = result === 0;
|
|
this.C = shiftAmount <= 32 ? !!((input >>> (shiftAmount - 1)) & 0x1) : false;
|
|
}
|
|
// MOV
|
|
else if (opcode >> 8 === 0b01000110) {
|
|
const Rm = (opcode >> 3) & 0xf;
|
|
const Rd = ((opcode >> 4) & 0x8) | (opcode & 0x7);
|
|
let value = Rm === pcRegister ? this.PC + 2 : this.registers[Rm];
|
|
if (Rd === pcRegister) {
|
|
deltaCycles++;
|
|
value &= ~1;
|
|
}
|
|
else if (Rd === spRegister) {
|
|
value &= ~3;
|
|
}
|
|
this.registers[Rd] = value;
|
|
}
|
|
// MOVS
|
|
else if (opcode >> 11 === 0b00100) {
|
|
const value = opcode & 0xff;
|
|
const Rd = (opcode >> 8) & 7;
|
|
this.registers[Rd] = value;
|
|
this.N = !!(value & 0x80000000);
|
|
this.Z = value === 0;
|
|
}
|
|
// MRS
|
|
else if (opcode === 0b1111001111101111 && opcode2 >> 12 == 0b1000) {
|
|
const SYSm = opcode2 & 0xff;
|
|
const Rd = (opcode2 >> 8) & 0xf;
|
|
this.registers[Rd] = this.readSpecialRegister(SYSm);
|
|
this.PC += 2;
|
|
deltaCycles += 2;
|
|
}
|
|
// MSR
|
|
else if (opcode >> 4 === 0b111100111000 && opcode2 >> 8 == 0b10001000) {
|
|
const SYSm = opcode2 & 0xff;
|
|
const Rn = opcode & 0xf;
|
|
this.writeSpecialRegister(SYSm, this.registers[Rn]);
|
|
this.PC += 2;
|
|
deltaCycles += 2;
|
|
}
|
|
// MULS
|
|
else if (opcode >> 6 === 0b0100001101) {
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rdm = opcode & 0x7;
|
|
const result = Math.imul(this.registers[Rn], this.registers[Rdm]);
|
|
this.registers[Rdm] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = (result & 0xffffffff) === 0;
|
|
}
|
|
// MVNS
|
|
else if (opcode >> 6 === 0b0100001111) {
|
|
const Rm = (opcode >> 3) & 7;
|
|
const Rd = opcode & 7;
|
|
const result = ~this.registers[Rm];
|
|
this.registers[Rd] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = result === 0;
|
|
}
|
|
// ORRS (Encoding T2)
|
|
else if (opcode >> 6 === 0b0100001100) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
const result = this.registers[Rdn] | this.registers[Rm];
|
|
this.registers[Rdn] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = (result & 0xffffffff) === 0;
|
|
}
|
|
// POP
|
|
else if (opcode >> 9 === 0b1011110) {
|
|
const P = (opcode >> 8) & 1;
|
|
let address = this.SP;
|
|
for (let i = 0; i <= 7; i++) {
|
|
if (opcode & (1 << i)) {
|
|
this.registers[i] = this.readUint32(address);
|
|
address += 4;
|
|
deltaCycles++;
|
|
}
|
|
}
|
|
if (P) {
|
|
this.SP = address + 4;
|
|
this.BXWritePC(this.readUint32(address));
|
|
deltaCycles += 2;
|
|
}
|
|
else {
|
|
this.SP = address;
|
|
}
|
|
}
|
|
// PUSH
|
|
else if (opcode >> 9 === 0b1011010) {
|
|
let bitCount = 0;
|
|
for (let i = 0; i <= 8; i++) {
|
|
if (opcode & (1 << i)) {
|
|
bitCount++;
|
|
}
|
|
}
|
|
let address = this.SP - 4 * bitCount;
|
|
for (let i = 0; i <= 7; i++) {
|
|
if (opcode & (1 << i)) {
|
|
this.writeUint32(address, this.registers[i]);
|
|
deltaCycles++;
|
|
address += 4;
|
|
}
|
|
}
|
|
if (opcode & (1 << 8)) {
|
|
this.writeUint32(address, this.registers[14]);
|
|
}
|
|
this.SP -= 4 * bitCount;
|
|
}
|
|
// REV
|
|
else if (opcode >> 6 === 0b1011101000) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
const input = this.registers[Rm];
|
|
this.registers[Rd] =
|
|
((input & 0xff) << 24) |
|
|
(((input >> 8) & 0xff) << 16) |
|
|
(((input >> 16) & 0xff) << 8) |
|
|
((input >> 24) & 0xff);
|
|
}
|
|
// REV16
|
|
else if (opcode >> 6 === 0b1011101001) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
const input = this.registers[Rm];
|
|
this.registers[Rd] =
|
|
(((input >> 16) & 0xff) << 24) |
|
|
(((input >> 24) & 0xff) << 16) |
|
|
((input & 0xff) << 8) |
|
|
((input >> 8) & 0xff);
|
|
}
|
|
// REVSH
|
|
else if (opcode >> 6 === 0b1011101011) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
const input = this.registers[Rm];
|
|
this.registers[Rd] = signExtend16(((input & 0xff) << 8) | ((input >> 8) & 0xff));
|
|
}
|
|
// ROR
|
|
else if (opcode >> 6 === 0b0100000111) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
const input = this.registers[Rdn];
|
|
const shift = (this.registers[Rm] & 0xff) % 32;
|
|
const result = (input >>> shift) | (input << (32 - shift));
|
|
this.registers[Rdn] = result;
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = result === 0;
|
|
this.C = !!(result & 0x80000000);
|
|
}
|
|
// NEGS / RSBS
|
|
else if (opcode >> 6 === 0b0100001001) {
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
this.registers[Rd] = this.substractUpdateFlags(0, this.registers[Rn]);
|
|
}
|
|
// NOP
|
|
else if (opcode === 0b1011111100000000) {
|
|
// Do nothing!
|
|
}
|
|
// SBCS (Encoding T1)
|
|
else if (opcode >> 6 === 0b0100000110) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rdn = opcode & 0x7;
|
|
this.registers[Rdn] = this.substractUpdateFlags(this.registers[Rdn], this.registers[Rm] + (1 - (this.C ? 1 : 0)));
|
|
}
|
|
// SEV
|
|
else if (opcode === 0b1011111101000000) {
|
|
this.logger.info(LOG_NAME, 'SEV');
|
|
}
|
|
// STMIA
|
|
else if (opcode >> 11 === 0b11000) {
|
|
const Rn = (opcode >> 8) & 0x7;
|
|
const registers = opcode & 0xff;
|
|
let address = this.registers[Rn];
|
|
for (let i = 0; i < 8; i++) {
|
|
if (registers & (1 << i)) {
|
|
this.writeUint32(address, this.registers[i]);
|
|
address += 4;
|
|
deltaCycles++;
|
|
}
|
|
}
|
|
// Write back
|
|
if (!(registers & (1 << Rn))) {
|
|
this.registers[Rn] = address;
|
|
}
|
|
}
|
|
// STR (immediate)
|
|
else if (opcode >> 11 === 0b01100) {
|
|
const imm5 = ((opcode >> 6) & 0x1f) << 2;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const address = this.registers[Rn] + imm5;
|
|
deltaCycles += this.cyclesIO(address, true);
|
|
this.writeUint32(address, this.registers[Rt]);
|
|
}
|
|
// STR (sp + immediate)
|
|
else if (opcode >> 11 === 0b10010) {
|
|
const Rt = (opcode >> 8) & 0x7;
|
|
const imm8 = opcode & 0xff;
|
|
const address = this.SP + (imm8 << 2);
|
|
deltaCycles += this.cyclesIO(address, true);
|
|
this.writeUint32(address, this.registers[Rt]);
|
|
}
|
|
// STR (register)
|
|
else if (opcode >> 9 === 0b0101000) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const address = this.registers[Rm] + this.registers[Rn];
|
|
deltaCycles += this.cyclesIO(address, true);
|
|
this.writeUint32(address, this.registers[Rt]);
|
|
}
|
|
// STRB (immediate)
|
|
else if (opcode >> 11 === 0b01110) {
|
|
const imm5 = (opcode >> 6) & 0x1f;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const address = this.registers[Rn] + imm5;
|
|
deltaCycles += this.cyclesIO(address, true);
|
|
this.writeUint8(address, this.registers[Rt]);
|
|
}
|
|
// STRB (register)
|
|
else if (opcode >> 9 === 0b0101010) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const address = this.registers[Rm] + this.registers[Rn];
|
|
deltaCycles += this.cyclesIO(address, true);
|
|
this.writeUint8(address, this.registers[Rt]);
|
|
}
|
|
// STRH (immediate)
|
|
else if (opcode >> 11 === 0b10000) {
|
|
const imm5 = ((opcode >> 6) & 0x1f) << 1;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const address = this.registers[Rn] + imm5;
|
|
deltaCycles += this.cyclesIO(address, true);
|
|
this.writeUint16(address, this.registers[Rt]);
|
|
}
|
|
// STRH (register)
|
|
else if (opcode >> 9 === 0b0101001) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rt = opcode & 0x7;
|
|
const address = this.registers[Rm] + this.registers[Rn];
|
|
deltaCycles += this.cyclesIO(address, true);
|
|
this.writeUint16(address, this.registers[Rt]);
|
|
}
|
|
// SUB (SP minus immediate)
|
|
else if (opcode >> 7 === 0b101100001) {
|
|
const imm32 = (opcode & 0x7f) << 2;
|
|
this.SP -= imm32;
|
|
}
|
|
// SUBS (Encoding T1)
|
|
else if (opcode >> 9 === 0b0001111) {
|
|
const imm3 = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
this.registers[Rd] = this.substractUpdateFlags(this.registers[Rn], imm3);
|
|
}
|
|
// SUBS (Encoding T2)
|
|
else if (opcode >> 11 === 0b00111) {
|
|
const imm8 = opcode & 0xff;
|
|
const Rdn = (opcode >> 8) & 0x7;
|
|
this.registers[Rdn] = this.substractUpdateFlags(this.registers[Rdn], imm8);
|
|
}
|
|
// SUBS (register)
|
|
else if (opcode >> 9 === 0b0001101) {
|
|
const Rm = (opcode >> 6) & 0x7;
|
|
const Rn = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
this.registers[Rd] = this.substractUpdateFlags(this.registers[Rn], this.registers[Rm]);
|
|
}
|
|
// SVC
|
|
else if (opcode >> 8 === 0b11011111) {
|
|
this.pendingSVCall = true;
|
|
this.interruptsUpdated = true;
|
|
}
|
|
// SXTB
|
|
else if (opcode >> 6 === 0b1011001001) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
this.registers[Rd] = signExtend8(this.registers[Rm]);
|
|
}
|
|
// SXTH
|
|
else if (opcode >> 6 === 0b1011001000) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
this.registers[Rd] = signExtend16(this.registers[Rm]);
|
|
}
|
|
// TST
|
|
else if (opcode >> 6 == 0b0100001000) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rn = opcode & 0x7;
|
|
const result = this.registers[Rn] & this.registers[Rm];
|
|
this.N = !!(result & 0x80000000);
|
|
this.Z = result === 0;
|
|
}
|
|
// UDF
|
|
else if (opcode >> 8 == 0b11011110) {
|
|
const imm8 = opcode & 0xff;
|
|
this.breakRewind = 2;
|
|
this.rp2040.onBreak(imm8);
|
|
}
|
|
// UDF (Encoding T2)
|
|
else if (opcode >> 4 === 0b111101111111 && opcode2 >> 12 === 0b1010) {
|
|
const imm4 = opcode & 0xf;
|
|
const imm12 = opcode2 & 0xfff;
|
|
this.breakRewind = 4;
|
|
this.rp2040.onBreak((imm4 << 12) | imm12);
|
|
this.PC += 2;
|
|
}
|
|
// UXTB
|
|
else if (opcode >> 6 == 0b1011001011) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
this.registers[Rd] = this.registers[Rm] & 0xff;
|
|
}
|
|
// UXTH
|
|
else if (opcode >> 6 == 0b1011001010) {
|
|
const Rm = (opcode >> 3) & 0x7;
|
|
const Rd = opcode & 0x7;
|
|
this.registers[Rd] = this.registers[Rm] & 0xffff;
|
|
}
|
|
// WFE
|
|
else if (opcode === 0b1011111100100000) {
|
|
deltaCycles++;
|
|
if (this.eventRegistered) {
|
|
this.eventRegistered = false;
|
|
}
|
|
else {
|
|
this.waiting = true;
|
|
}
|
|
}
|
|
// WFI
|
|
else if (opcode === 0b1011111100110000) {
|
|
deltaCycles++;
|
|
this.waiting = true;
|
|
}
|
|
// YIELD
|
|
else if (opcode === 0b1011111100010000) {
|
|
// do nothing for now. Wait for event!
|
|
this.logger.info(LOG_NAME, 'Yield');
|
|
}
|
|
else {
|
|
this.logger.warn(LOG_NAME, `Warning: Instruction at ${opcodePC.toString(16)} is not implemented yet!`);
|
|
this.logger.warn(LOG_NAME, `Opcode: 0x${opcode.toString(16)} (0x${opcode2.toString(16)})`);
|
|
}
|
|
this.cycles += deltaCycles;
|
|
return deltaCycles;
|
|
}
|
|
}
|