diff --git a/src/instructions.spec.ts b/src/instructions.spec.ts index d370b8c..db3342c 100644 --- a/src/instructions.spec.ts +++ b/src/instructions.spec.ts @@ -65,6 +65,10 @@ import { opcodeWFI, opcodeYIELD, } from './utils/assembler'; +import { GDBClient } from './utils/gdbclient'; +import { ICortexTestDriver } from './utils/test-driver'; +import { GDBTestDriver } from './utils/test-driver-gdb'; +import { RP2040TestDriver } from './utils/test-driver-rp2040'; const r0 = 0; const r1 = 1; @@ -84,1134 +88,1120 @@ const VTOR = 0xe000ed08; const EXC_SVCALL = 11; describe('Cortex-M0+ Instruction Set', () => { - it('should execute `adcs r5, r4` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeADCS(r5, r4); - rp2040.registers[r4] = 55; - rp2040.registers[r5] = 66; - rp2040.C = true; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(122); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - expect(rp2040.V).toEqual(false); - }); - - it('should execute `adcs r5, r4` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeADCS(r5, r4); - rp2040.registers[r4] = 0x7fffffff; // Max signed INT32 - rp2040.registers[r5] = 0; - rp2040.C = true; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0x80000000); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - expect(rp2040.V).toEqual(true); - }); - - it('should execute a `add sp, 0x10` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.SP = 0x10000040; - rp2040.flash16[0] = opcodeADDsp2(0x10); - rp2040.executeInstruction(); - expect(rp2040.SP).toEqual(0x10000050); - }); - - it('should execute a `add r1, sp, #4` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.SP = 0x55; - rp2040.flash16[0] = opcodeADDspPlusImm(r1, 0x10); - rp2040.registers[1] = 0; - rp2040.executeInstruction(); - expect(rp2040.SP).toEqual(0x55); - expect(rp2040.registers[1]).toEqual(0x65); - }); - - it('should execute `adds r1, r2, #3` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeADDS1(r1, r2, 3); - rp2040.registers[r2] = 2; - rp2040.executeInstruction(); - expect(rp2040.registers[r1]).toEqual(5); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - expect(rp2040.V).toEqual(false); - }); - - it('should execute `adds r1, #1` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeADDS2(r1, 1); - rp2040.registers[r1] = 0xffffffff; - rp2040.executeInstruction(); - expect(rp2040.registers[r1]).toEqual(0); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(true); - expect(rp2040.C).toEqual(true); - expect(rp2040.V).toEqual(false); - }); - - it('should execute `adds r1, r2, r7` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeADDSreg(r1, r2, r7); - rp2040.registers[r2] = 2; - rp2040.registers[r7] = 27; - rp2040.executeInstruction(); - expect(rp2040.registers[r1]).toEqual(29); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - expect(rp2040.V).toEqual(false); - }); - - it('should execute `add r1, ip` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeADDreg(r1, ip); - rp2040.registers[r1] = 66; - rp2040.registers[ip] = 44; - rp2040.executeInstruction(); - expect(rp2040.registers[r1]).toEqual(110); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - expect(rp2040.V).toEqual(false); - }); - - it('should execute `add sp, r8` instruction and not update the flags', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.SP = 0x20030000; - rp2040.Z = true; - rp2040.flash16[0] = opcodeADDreg(sp, r8); - rp2040.registers[r8] = 0x11; - rp2040.executeInstruction(); - expect(rp2040.SP).toEqual(0x20030011); - expect(rp2040.Z).toEqual(true); // assert it didn't update the flags - }); - - it('should execute `add pc, r8` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeADDreg(pc, r8); - rp2040.registers[r8] = 0x11; - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000014); - }); - - it('should execute `adr r4, #0x50` instruction and set the overflow flag correctly', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeADR(r4, 0x50); - rp2040.executeInstruction(); - expect(rp2040.registers[r4]).toEqual(0x10000054); - }); - - it('should execute `ands r5, r0` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeANDS(r5, r0); - rp2040.registers[r5] = 0xffff0000; - rp2040.registers[r0] = 0xf00fffff; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0xf00f0000); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - }); - - it('should execute an `asrs r3, r2, #31` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeASRS(r3, r2, 31); - rp2040.registers[r2] = 0x80000000; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0xffffffff); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - }); - - it('should execute an `asrs r3, r2, #0` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeASRS(r3, r2, 0); - rp2040.registers[r2] = 0x80000000; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0xffffffff); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(true); - }); - - it('should execute an `asrs r3, r4` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeASRSreg(r3, r4); - rp2040.registers[r3] = 0x80000040; - rp2040.registers[r4] = 0xff500007; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0xff000000); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(true); - }); - - it('should execute `bics r0, r3` correctly', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.registers[r0] = 0xff; - rp2040.registers[r3] = 0x0f; - rp2040.flash16[0] = opcodeBICS(r0, r3); - rp2040.executeInstruction(); - expect(rp2040.registers[r0]).toEqual(0xf0); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - }); - - it('should execute `bics r0, r3` instruction and set the negative flag correctly', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.registers[r0] = 0xffffffff; - rp2040.registers[r3] = 0x0000ffff; - rp2040.flash16[0] = opcodeBICS(r0, r3); - rp2040.executeInstruction(); - expect(rp2040.registers[r0]).toEqual(0xffff0000); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - }); - - it('should execute `bl 0x34` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flashView.setUint32(0, opcodeBL(0x34), true); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000038); - expect(rp2040.LR).toEqual(0x10000005); - }); - - it('should execute `bl -0x10` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flashView.setUint32(0, opcodeBL(-0x10), true); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000004 - 0x10); - expect(rp2040.LR).toEqual(0x10000005); - }); - - it('should execute `bl -3242` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flashView.setUint32(0, opcodeBL(-3242), true); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000004 - 3242); - expect(rp2040.LR).toEqual(0x10000005); - }); - - it('should execute `blx r3` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.registers[3] = 0x10000201; - rp2040.flashView.setUint32(0, opcodeBLX(r3), true); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000200); - expect(rp2040.LR).toEqual(0x10000003); - }); - - it('should execute a `b.n .-20` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000 + 9 * 2; - rp2040.flash16[9] = 0xe7f6; // b.n .-20 - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute a `bne.n .-6` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000 + 9 * 2; - rp2040.Z = false; - rp2040.flash16[9] = 0xd1fc; // bne.n .-6 - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x1000000e); - }); - - it('should execute `bx lr` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.LR = 0x10000200; - rp2040.flashView.setUint32(0, opcodeBX(lr), true); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000200); - }); - - it('should execute an `cmn r5, r2` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeCMN(r7, r2); - rp2040.registers[r2] = 1; - rp2040.registers[r7] = -2; - rp2040.executeInstruction(); - expect(rp2040.registers[r2]).toEqual(1); - expect(rp2040.registers[r7]).toEqual(-2 >>> 0); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - expect(rp2040.V).toEqual(false); - }); - - it('should execute an `cmp r5, #66` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x2d42; // cmp r5, #66 - rp2040.registers[r5] = 60; - rp2040.executeInstruction(); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - expect(rp2040.V).toEqual(false); - }); - - it('should execute an `cmp r5, r0` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x4285; // cmp r5, r0 - rp2040.registers[r5] = 60; - rp2040.registers[r0] = 56; - rp2040.executeInstruction(); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(true); - expect(rp2040.V).toEqual(false); - }); - - it('should execute an `cmp ip, r6` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x45b4; // cmp ip (r12), r6 - rp2040.registers[ip] = 60; - rp2040.registers[r6] = 56; - rp2040.executeInstruction(); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(true); - expect(rp2040.V).toEqual(false); - }); - - it('should correctly decode a `dmb sy` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0xf3bf; // DMB SY - rp2040.flash16[1] = 0x8f50; // ... - rp2040.executeInstruction(); - expect(rp2040.PC).toBe(0x10000004); - }); - - it('should correctly decode a `dsb sy` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0xf3bf; // DSB SY - rp2040.flash16[1] = 0x8f4f; // ... - rp2040.executeInstruction(); - expect(rp2040.PC).toBe(0x10000004); - }); - - it('should execute an `eors r1, r3` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeEORS(r1, r3); - rp2040.registers[r1] = 0xf0f0f0f0; - rp2040.registers[r3] = 0x08ff3007; - rp2040.executeInstruction(); - expect(rp2040.registers[r1]).toEqual(0xf80fc0f7); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - }); - - it('should correctly decode a `isb sy` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0xf3bf; // ISB SY - rp2040.flash16[1] = 0x8f6f; // ... - rp2040.executeInstruction(); - expect(rp2040.PC).toBe(0x10000004); - }); - - it('should execute a `mov r3, r8` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeMOV(r3, r8); - rp2040.registers[r8] = 55; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(55); - }); - - it('should execute a `mov r3, pc` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeMOV(r3, pc); - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x10000004); - }); - - it('should execute a `muls r0, r2` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeMULS(r0, r2); - rp2040.registers[r0] = 5; - rp2040.registers[r2] = 1000000; - rp2040.executeInstruction(); - expect(rp2040.registers[r2]).toEqual(5000000); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - }); - - it('should execute a muls instruction with large 32-bit numbers and produce the correct result', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeMULS(r0, r2); - rp2040.registers[r0] = 2654435769; - rp2040.registers[r2] = 340573321; - rp2040.executeInstruction(); - expect(rp2040.registers[r2]).toEqual(1); - }); - - it('should execute a `muls r0, r2` instruction and set the Z flag when the result is zero', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeMULS(r0, r2); - rp2040.registers[r0] = 0; - rp2040.registers[r2] = 1000000; - rp2040.executeInstruction(); - expect(rp2040.registers[r2]).toEqual(0); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(true); - }); - - it('should execute a `muls r0, r2` instruction and set the N flag when the result is negative', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeMULS(r0, r2); - rp2040.registers[r0] = -1; - rp2040.registers[r2] = 1000000; - rp2040.executeInstruction(); - expect(rp2040.registers[r2]).toEqual(-1000000 >>> 0); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - }); - - it('should execute a `mvns r4, r3` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeMVNS(r4, r3); - rp2040.registers[r3] = 0x11115555; - rp2040.executeInstruction(); - expect(rp2040.registers[r4]).toEqual(0xeeeeaaaa); - expect(rp2040.Z).toBe(false); - expect(rp2040.N).toBe(true); - }); - - it('should execute a `nop` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeNOP(); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute `orrs r5, r0` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeORRS(r5, r0); - rp2040.registers[r5] = 0xf00f0000; - rp2040.registers[r0] = 0xf000ffff; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0xf00fffff); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - }); - - it('should execute a `pop pc, {r4, r5, r6}` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.SP = RAM_START_ADDRESS + 0xf0; - rp2040.flash16[0] = opcodePOP(true, (1 << r4) | (1 << r5) | (1 << r6)); - rp2040.sram[0xf0] = 0x40; - rp2040.sram[0xf4] = 0x50; - rp2040.sram[0xf8] = 0x60; - rp2040.sram[0xfc] = 0x42; - rp2040.executeInstruction(); - expect(rp2040.SP).toEqual(RAM_START_ADDRESS + 0x100); + let cpu: ICortexTestDriver; + + beforeEach(async () => { + if (process.env.TEST_GDB_SERVER) { + const client = new GDBClient(); + await client.connect(process.env.TEST_GDB_SERVER); + cpu = new GDBTestDriver(client); + await cpu.init(); + } else { + cpu = new RP2040TestDriver(new RP2040()); + } + }); + + afterEach(async () => { + await cpu.tearDown(); + }); + + it('should execute `adcs r5, r4` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeADCS(r5, r4)); + await cpu.setRegisters({ + r4: 55, + r5: 66, + xPSR: 0x01000010, + C: true, + }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(122); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + expect(registers.V).toEqual(false); + }); + + it('should execute `adcs r5, r4` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeADCS(r5, r4)); + cpu.setRegisters({ + r4: 0x7fffffff, // Max signed INT32 + r5: 0, + C: true, + }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0x80000000); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + expect(registers.V).toEqual(true); + }); + + it('should execute a `add sp, 0x10` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ sp: 0x10000040 }); + await cpu.writeUint16(0x20000000, opcodeADDsp2(0x10)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.sp).toEqual(0x10000050); + }); + + it('should execute a `add r1, sp, #4` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ sp: 0x55 }); + await cpu.writeUint16(0x20000000, opcodeADDspPlusImm(r1, 0x10)); + await cpu.setRegisters({ r1: 0 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.sp).toEqual(0x55); + expect(registers.r1).toEqual(0x65); + }); + + it('should execute `adds r1, r2, #3` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeADDS1(r1, r2, 3)); + await cpu.setRegisters({ r2: 2 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r1).toEqual(5); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + expect(registers.V).toEqual(false); + }); + + it('should execute `adds r1, #1` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeADDS2(r1, 1)); + await cpu.setRegisters({ r1: 0xffffffff }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r1).toEqual(0); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(true); + expect(registers.C).toEqual(true); + expect(registers.V).toEqual(false); + }); + + it('should execute `adds r1, r2, r7` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeADDSreg(r1, r2, r7)); + await cpu.setRegisters({ r2: 2 }); + await cpu.setRegisters({ r7: 27 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r1).toEqual(29); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + expect(registers.V).toEqual(false); + }); + + it('should execute `add r1, ip` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeADDreg(r1, ip)); + await cpu.setRegisters({ r1: 66 }); + await cpu.setRegisters({ r12: 44 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r1).toEqual(110); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + expect(registers.V).toEqual(false); + }); + + it('should execute `add sp, r8` instruction and not update the flags', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeADDreg(sp, r8)); + await cpu.setRegisters({ sp: 0x20030000, Z: true, r8: 0x11 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.sp).toEqual(0x20030011); + expect(registers.Z).toEqual(true); // assert it didn't update the flags + }); + + it('should execute `add pc, r8` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeADDreg(pc, r8)); + await cpu.setRegisters({ r8: 0x11 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000014); + }); + + it('should execute `adr r4, #0x50` instruction and set the overflow flag correctly', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeADR(r4, 0x50)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r4).toEqual(0x20000054); + }); + + it('should execute `ands r5, r0` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeANDS(r5, r0)); + await cpu.setRegisters({ r5: 0xffff0000 }); + await cpu.setRegisters({ r0: 0xf00fffff }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0xf00f0000); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + }); + + it('should execute an `asrs r3, r2, #31` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeASRS(r3, r2, 31)); + await cpu.setRegisters({ r2: 0x80000000 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0xffffffff); + expect(registers.pc).toEqual(0x20000002); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + }); + + it('should execute an `asrs r3, r2, #0` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeASRS(r3, r2, 0)); + await cpu.setRegisters({ r2: 0x80000000 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0xffffffff); + expect(registers.pc).toEqual(0x20000002); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(true); + }); + + it('should execute an `asrs r3, r4` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeASRSreg(r3, r4)); + await cpu.setRegisters({ r3: 0x80000040 }); + await cpu.setRegisters({ r4: 0xff500007 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0xff000000); + expect(registers.pc).toEqual(0x20000002); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(true); + }); + + it('should execute `bics r0, r3` correctly', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ r0: 0xff }); + await cpu.setRegisters({ r3: 0x0f }); + await cpu.writeUint16(0x20000000, opcodeBICS(r0, r3)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r0).toEqual(0xf0); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + }); + + it('should execute `bics r0, r3` instruction and set the negative flag correctly', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ r0: 0xffffffff }); + await cpu.setRegisters({ r3: 0x0000ffff }); + await cpu.writeUint16(0x20000000, opcodeBICS(r0, r3)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r0).toEqual(0xffff0000); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + }); + + it('should execute `bl 0x34` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint32(0x20000000, opcodeBL(0x34)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000038); + expect(registers.lr).toEqual(0x20000005); + }); + + it('should execute `bl -0x10` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint32(0x20000000, opcodeBL(-0x10)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000004 - 0x10); + expect(registers.lr).toEqual(0x20000005); + }); + + it('should execute `bl -3242` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint32(0x20000000, opcodeBL(-3242)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000004 - 3242); + expect(registers.lr).toEqual(0x20000005); + }); + + it('should execute `blx r3` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ r3: 0x20000201 }); + await cpu.writeUint32(0x20000000, opcodeBLX(r3)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000200); + expect(registers.lr).toEqual(0x20000003); + }); + + it('should execute a `b.n .-20` instruction', async () => { + await cpu.setPC(0x20000000 + 9 * 2); + await cpu.writeUint16(0x20000000 + 9 * 2, 0xe7f6); // b.n .-20 + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute a `bne.n .-6` instruction', async () => { + await cpu.setPC(0x20000000 + 9 * 2); + await cpu.setRegisters({ Z: false }); + await cpu.writeUint16(0x20000000 + 9 * 2, 0xd1fc); // bne.n .-6 + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x2000000e); + }); + + it('should execute `bx lr` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ lr: 0x10000200 }); + await cpu.writeUint32(0x20000000, opcodeBX(lr)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x10000200); + }); + + it('should execute an `cmn r5, r2` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeCMN(r7, r2)); + await cpu.setRegisters({ r2: 1 }); + await cpu.setRegisters({ r7: -2 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r2).toEqual(1); + expect(registers.r7).toEqual(-2 >>> 0); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + expect(registers.V).toEqual(false); + }); + + it('should execute an `cmp r5, #66` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x2d42); // cmp r5, #66 + await cpu.setRegisters({ r5: 60 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + expect(registers.V).toEqual(false); + }); + + it('should execute an `cmp r5, r0` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x4285); // cmp r5, r0 + await cpu.setRegisters({ r5: 60 }); + await cpu.setRegisters({ r0: 56 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(true); + expect(registers.V).toEqual(false); + }); + + it('should execute an `cmp ip, r6` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x45b4); // cmp ip (r12), r6 + await cpu.setRegisters({ r6: 56, r12: 60 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(true); + expect(registers.V).toEqual(false); + }); + + it('should correctly decode a `dmb sy` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint32(0x20000000, 0x8f50f3bf); // DMB SY + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toBe(0x20000004); + }); + + it('should correctly decode a `dsb sy` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint32(0x20000000, 0x8f4ff3bf); // DSB SY + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toBe(0x20000004); + }); + + it('should execute an `eors r1, r3` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeEORS(r1, r3)); + await cpu.setRegisters({ r1: 0xf0f0f0f0 }); + await cpu.setRegisters({ r3: 0x08ff3007 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r1).toEqual(0xf80fc0f7); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + }); + + it('should correctly decode a `isb sy` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint32(0x20000000, 0x8f6ff3bf); // ISB SY + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toBe(0x20000004); + }); + + it('should execute a `mov r3, r8` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeMOV(r3, r8)); + await cpu.setRegisters({ r8: 55 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(55); + }); + + it('should execute a `mov r3, pc` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeMOV(r3, pc)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x20000004); + }); + + it('should execute a `muls r0, r2` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeMULS(r0, r2)); + await cpu.setRegisters({ r0: 5 }); + await cpu.setRegisters({ r2: 1000000 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r2).toEqual(5000000); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + }); + + it('should execute a muls instruction with large 32-bit numbers and produce the correct result', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeMULS(r0, r2)); + await cpu.setRegisters({ r0: 2654435769 }); + await cpu.setRegisters({ r2: 340573321 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r2).toEqual(1); + }); + + it('should execute a `muls r0, r2` instruction and set the Z flag when the result is zero', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeMULS(r0, r2)); + await cpu.setRegisters({ r0: 0 }); + await cpu.setRegisters({ r2: 1000000 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r2).toEqual(0); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(true); + }); + + it('should execute a `muls r0, r2` instruction and set the N flag when the result is negative', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeMULS(r0, r2)); + await cpu.setRegisters({ r0: -1 }); + await cpu.setRegisters({ r2: 1000000 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r2).toEqual(-1000000 >>> 0); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + }); + + it('should execute a `mvns r4, r3` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeMVNS(r4, r3)); + await cpu.setRegisters({ r3: 0x11115555 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r4).toEqual(0xeeeeaaaa); + expect(registers.Z).toBe(false); + expect(registers.N).toBe(true); + }); + + it('should execute a `nop` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeNOP()); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute `orrs r5, r0` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeORRS(r5, r0)); + await cpu.setRegisters({ r5: 0xf00f0000 }); + await cpu.setRegisters({ r0: 0xf000ffff }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0xf00fffff); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + }); + + it('should execute a `pop pc, {r4, r5, r6}` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ sp: RAM_START_ADDRESS + 0xf0 }); + await cpu.writeUint16(0x20000000, opcodePOP(true, (1 << r4) | (1 << r5) | (1 << r6))); + await cpu.writeUint8(0x200000f0, 0x40); + await cpu.writeUint8(0x200000f4, 0x50); + await cpu.writeUint8(0x200000f8, 0x60); + await cpu.writeUint8(0x200000fc, 0x42); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.sp).toEqual(RAM_START_ADDRESS + 0x100); // assert that the values of r4, r5, r6, pc were poped from the stack correctly - expect(rp2040.registers[r4]).toEqual(0x40); - expect(rp2040.registers[r5]).toEqual(0x50); - expect(rp2040.registers[r6]).toEqual(0x60); - expect(rp2040.PC).toEqual(0x42); - }); - - it('should execute a `push {r4, r5, r6, lr}` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.SP = RAM_START_ADDRESS + 0x100; - rp2040.flash16[0] = 0xb570; // push {r4, r5, r6, lr} - rp2040.registers[r4] = 0x40; - rp2040.registers[r5] = 0x50; - rp2040.registers[r6] = 0x60; - rp2040.LR = 0x42; - rp2040.executeInstruction(); + expect(registers.r4).toEqual(0x40); + expect(registers.r5).toEqual(0x50); + expect(registers.r6).toEqual(0x60); + expect(registers.pc).toEqual(0x42); + }); + + it('should execute a `push {r4, r5, r6, lr}` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ sp: RAM_START_ADDRESS + 0x100 }); + await cpu.writeUint16(0x20000000, 0xb570); // push {r4, r5, r6, lr} + await cpu.setRegisters({ r4: 0x40, r5: 0x50, r6: 0x60, lr: 0x42 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); // assert that the values of r4, r5, r6, lr were pushed into the stack - expect(rp2040.SP).toEqual(RAM_START_ADDRESS + 0xf0); - expect(rp2040.sram[0xf0]).toEqual(0x40); - expect(rp2040.sram[0xf4]).toEqual(0x50); - expect(rp2040.sram[0xf8]).toEqual(0x60); - expect(rp2040.sram[0xfc]).toEqual(0x42); - }); - - it('should execute a `mrs r0, ipsr` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flashView.setUint32(0, opcodeMRS(r0, 5), true); // 5 === ipsr - rp2040.registers[r0] = 55; - rp2040.executeInstruction(); - expect(rp2040.registers[r0]).toEqual(0); - expect(rp2040.PC).toEqual(0x10000004); - }); - - it('should execute a `msr ipsr, r0` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flashView.setUint32(0, opcodeMSR(8, r0), true); // 5 === ipsr - rp2040.registers[r0] = 0x1234; - rp2040.executeInstruction(); - expect(rp2040.SP).toEqual(0x1234); - expect(rp2040.PC).toEqual(0x10000004); - }); - - it('should execute a `movs r5, #128` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeMOVS(r5, 128); - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(128); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute a `ldmia r0!, {r1, r2}` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDMIA(r0, (1 << r1) | (1 << r2)); - rp2040.registers[r0] = 0x20000000; - rp2040.sramView.setUint32(0, 0xf00df00d, true); - rp2040.sramView.setUint32(4, 0x4242, true); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.registers[r0]).toEqual(0x20000008); - expect(rp2040.registers[r1]).toEqual(0xf00df00d); - expect(rp2040.registers[r2]).toEqual(0x4242); - }); - - it('should execute a `ldmia r5!, {r5}` instruction without writing back the address to r5', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDMIA(r5, 1 << r5); - rp2040.registers[r5] = 0x20000000; - rp2040.sramView.setUint32(0, 0xf00df00d, true); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.registers[r5]).toEqual(0xf00df00d); - }); - - it('should execute an `ldr r0, [pc, #148]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x4825; // ldr r0, [pc, #148] - rp2040.flash[152] = 0x42; - rp2040.flash.fill(0, 153, 156); - rp2040.executeInstruction(); - expect(rp2040.registers[r0]).toEqual(0x42); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute an `ldr r3, [r2, #24]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x6993; // ldr r3, [r2, #24] - rp2040.registers[r2] = 0x20000000; - rp2040.sram[24] = 0x55; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x55); - }); - - it('should execute an `ldr r3, [sp, #12]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.SP = 0x20000000; - rp2040.flash16[0] = opcodeLDRsp(r3, 12); - rp2040.sram[12] = 0x55; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x55); - }); - - it('should execute an `ldr r3, [r5, r6]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDRreg(r3, r5, r6); - rp2040.registers[r5] = 0x20000000; - rp2040.registers[r6] = 0x8; - rp2040.sram[8] = 0x11; - rp2040.sram[9] = 0x42; - rp2040.sram[10] = 0x55; - rp2040.sram[11] = 0xff; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0xff554211); - }); - - it('should execute an `ldrb r4, [r2, 5]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDRB(r4, r2, 5); - rp2040.registers[r2] = 0x20000000; - rp2040.sram[5] = 0x66; - rp2040.sram[6] = 0x77; - rp2040.executeInstruction(); - expect(rp2040.registers[r4]).toEqual(0x66); - }); - - it('should execute an `ldrb r3, [r5, r6]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDRBreg(r3, r5, r6); - rp2040.registers[r5] = 0x20000000; - rp2040.registers[r6] = 0x8; - rp2040.sram[8] = 0x11; - rp2040.sram[9] = 0x42; - rp2040.sram[10] = 0x55; - rp2040.sram[11] = 0xff; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x11); - }); - - it('should execute an `ldrh r3, [r7, #4]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDRH(r3, r7, 4); - rp2040.registers[r7] = 0x20000000; - rp2040.sram[4] = 0x66; - rp2040.sram[5] = 0x77; - rp2040.sram[6] = 0xff; - rp2040.sram[7] = 0xff; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x7766); - }); - - it('should execute an `ldrh r3, [r7, #6]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDRH(r3, r7, 6); - rp2040.registers[r7] = 0x20000000; - rp2040.sram[4] = 0x66; - rp2040.sram[5] = 0x77; - rp2040.sram[6] = 0x44; - rp2040.sram[7] = 0x33; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x3344); - }); - - it('should execute an `ldrh r3, [r5, r6]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDRHreg(r3, r5, r6); - rp2040.registers[r5] = 0x20000000; - rp2040.registers[r6] = 0x8; - rp2040.sram[8] = 0x11; - rp2040.sram[9] = 0x42; - rp2040.sram[10] = 0x55; - rp2040.sram[11] = 0xff; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x4211); - }); - - it('should execute an `ldrsb r5, [r3, r5]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDRSB(r5, r3, r5); - rp2040.registers[r3] = 0x20000000; - rp2040.registers[r5] = 6; - rp2040.sram[6] = 0x85; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0xffffff85); - }); - - it('should execute an `ldrsh r5, [r3, r5]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLDRSH(r5, r3, r5); - rp2040.registers[r3] = 0x20000000; - rp2040.registers[r5] = 6; - rp2040.sram[6] = 0x55; - rp2040.sram[7] = 0xf0; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0xfffff055); + expect(registers.sp).toEqual(RAM_START_ADDRESS + 0xf0); + expect(await cpu.readUint8(0x200000f0)).toEqual(0x40); + expect(await cpu.readUint8(0x200000f4)).toEqual(0x50); + expect(await cpu.readUint8(0x200000f8)).toEqual(0x60); + expect(await cpu.readUint8(0x200000fc)).toEqual(0x42); + }); + + it('should execute a `mrs r0, ipsr` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint32(0x20000000, opcodeMRS(r0, 5)); // 5 ==sr + await cpu.setRegisters({ r0: 55 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r0).toEqual(0); + expect(registers.pc).toEqual(0x20000004); + }); + + it('should execute a `msr ipsr, r0` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint32(0x20000000, opcodeMSR(8, r0)); // 5 ==sr + await cpu.setRegisters({ r0: 0x1234 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.sp).toEqual(0x1234); + expect(registers.pc).toEqual(0x20000004); + }); + + it('should execute a `movs r5, #128` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeMOVS(r5, 128)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(128); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute a `ldmia r0!, {r1, r2}` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDMIA(r0, (1 << r1) | (1 << r2))); + await cpu.setRegisters({ r0: 0x20000010 }); + await cpu.writeUint32(0x20000010, 0xf00df00d); + await cpu.writeUint32(0x20000014, 0x4242); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000002); + expect(registers.r0).toEqual(0x20000018); + expect(registers.r1).toEqual(0xf00df00d); + expect(registers.r2).toEqual(0x4242); + }); + + it('should execute a `ldmia r5!, {r5}` instruction without writing back the address to r5', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDMIA(r5, 1 << r5)); + await cpu.setRegisters({ r5: 0x20000010 }); + await cpu.writeUint32(0x20000010, 0xf00df00d); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000002); + expect(registers.r5).toEqual(0xf00df00d); + }); + + it('should execute an `ldr r0, [pc, #148]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x4825); // ldr r0, [pc, #148] + await cpu.writeUint32(0x20000000 + 152, 0x42); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r0).toEqual(0x42); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute an `ldr r3, [r2, #24]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x6993); // ldr r3, [r2, #24] + await cpu.setRegisters({ r2: 0x20000000 }); + await cpu.writeUint8(0x20000000 + 24, 0x55); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x55); + }); + + it('should execute an `ldr r3, [sp, #12]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ sp: 0x20000000 }); + await cpu.writeUint16(0x20000000, opcodeLDRsp(r3, 12)); + await cpu.writeUint8(0x20000000 + 12, 0x55); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x55); + }); + + it('should execute an `ldr r3, [r5, r6]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDRreg(r3, r5, r6)); + await cpu.setRegisters({ r5: 0x20000000 }); + await cpu.setRegisters({ r6: 0x8 }); + await cpu.writeUint32(0x20000008, 0xff554211); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0xff554211); + }); + + it('should execute an `ldrb r4, [r2, 5]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDRB(r4, r2, 5)); + await cpu.setRegisters({ r2: 0x20000000 }); + await cpu.writeUint16(0x20000005, 0x7766); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r4).toEqual(0x66); + }); + + it('should execute an `ldrb r3, [r5, r6]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDRBreg(r3, r5, r6)); + await cpu.setRegisters({ r5: 0x20000000 }); + await cpu.setRegisters({ r6: 0x8 }); + await cpu.writeUint32(0x20000008, 0xff554211); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x11); + }); + + it('should execute an `ldrh r3, [r7, #4]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDRH(r3, r7, 4)); + await cpu.setRegisters({ r7: 0x20000000 }); + await cpu.writeUint32(0x20000004, 0xffff7766); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x7766); + }); + + it('should execute an `ldrh r3, [r7, #6]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDRH(r3, r7, 6)); + await cpu.setRegisters({ r7: 0x20000000 }); + await cpu.writeUint32(0x20000004, 0x33447766); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x3344); + }); + + it('should execute an `ldrh r3, [r5, r6]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDRHreg(r3, r5, r6)); + await cpu.setRegisters({ r5: 0x20000000, r6: 0x8 }); + await cpu.writeUint32(0x20000008, 0xff554211); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x4211); + }); + + it('should execute an `ldrsb r5, [r3, r5]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDRSB(r5, r3, r5)); + await cpu.setRegisters({ r3: 0x20000000, r5: 6 }); + await cpu.writeUint32(0x20000006, 0x85); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0xffffff85); + }); + + it('should execute an `ldrsh r5, [r3, r5]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLDRSH(r5, r3, r5)); + await cpu.setRegisters({ r3: 0x20000000 }); + await cpu.setRegisters({ r5: 6 }); + await cpu.writeUint16(0x20000006, 0xf055); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0xfffff055); }); it('should execute a `udf 1` instruction', () => { const breakMock = jest.fn(); const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0xde01; // udf 1 + rp2040.PC = 0x20000000; + rp2040.writeUint16(0x20000000, 0xde01); // udf 1 rp2040.onBreak = breakMock; rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000002); + expect(rp2040.PC).toEqual(0x20000002); expect(breakMock).toHaveBeenCalledWith(1); }); it('should execute a `udf.w #0` (T2 encoding) instruction', () => { const breakMock = jest.fn(); const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flashView.setUint32(0, opcodeUDF2(0), true); + rp2040.PC = 0x20000000; + rp2040.writeUint32(0x20000000, opcodeUDF2(0)); rp2040.onBreak = breakMock; rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000004); + expect(rp2040.PC).toEqual(0x20000004); expect(breakMock).toHaveBeenCalledWith(0); }); - it('should execute a `lsls r5, r5, #18` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x04ad; // lsls r5, r5, #18 - rp2040.registers[r5] = 0b00000000000000000011; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0b11000000000000000000); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.C).toEqual(false); - }); - - it('should execute a `lsls r5, r0` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLSLSreg(r5, r0); - rp2040.registers[r5] = 0b00000000000000000011; - rp2040.registers[r0] = 0xff003302; // bottom byte: 02 - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0b00000000000000001100); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.C).toEqual(false); - }); - - it('should execute a `lsls r5, r5, #18` instruction with carry', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x04ad; // lsls r5, r5, #18 - rp2040.registers[r5] = 0x00004001; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0x40000); - expect(rp2040.C).toEqual(true); - }); - - it('should execute a `lsrs r5, r0` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLSRSreg(r5, r0); - rp2040.registers[r5] = 0xff00000f; - rp2040.registers[r0] = 0xff003302; // Shift amount: 02 - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0x3fc00003); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.C).toEqual(true); - }); - - it('should execute a `lsrs r1, r1, #1` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLSRS(r1, r1, 1); - rp2040.registers[r1] = 0b10; - rp2040.executeInstruction(); - expect(rp2040.registers[r1]).toEqual(0b1); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.C).toEqual(false); - }); - - it('should execute a `lsrs r1, r1, 0` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeLSRS(r1, r1, 0); - rp2040.registers[r1] = 0xffffffff; - rp2040.executeInstruction(); - expect(rp2040.registers[r1]).toEqual(0); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.C).toEqual(true); - }); - - it('should execute a `movs r6, r5` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x002e; // movs r6, r5 - rp2040.registers[r5] = 0x50; - rp2040.executeInstruction(); - expect(rp2040.registers[r6]).toEqual(0x50); - }); - - it('should execute a `rsbs r0, r3` instruction', () => { - // This instruction is also called `negs` - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeRSBS(r0, r3); - rp2040.registers[r3] = 100; - rp2040.executeInstruction(); - expect(rp2040.registers[r0] | 0).toEqual(-100); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - expect(rp2040.V).toEqual(false); - }); - - it('should execute a `rev r3, r1` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeREV(r2, r3); - rp2040.registers[r3] = 0x11223344; - rp2040.executeInstruction(); - expect(rp2040.registers[r2]).toEqual(0x44332211); - }); - - it('should execute a `rev16 r0, r5` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeREV16(r0, r5); - rp2040.registers[r5] = 0x11223344; - rp2040.executeInstruction(); - expect(rp2040.registers[r0]).toEqual(0x22114433); - }); - - it('should execute a `revsh r1, r2` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeREVSH(r1, r2); - rp2040.registers[r2] = 0xeeaa55f0; - rp2040.executeInstruction(); - expect(rp2040.registers[r1]).toEqual(0xfffff055); - }); - - it('should execute a `ror r5, r3` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeROR(r5, r3); - rp2040.registers[r5] = 0x12345678; - rp2040.registers[r3] = 0x2004; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x2004); - expect(rp2040.registers[r5]).toEqual(0x81234567); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(true); - }); - - it('should execute a `ror r5, r3` instruction when r3 > 32', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeROR(r5, r3); - rp2040.registers[r5] = 0x12345678; - rp2040.registers[r3] = 0x2044; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x2044); - expect(rp2040.registers[r5]).toEqual(0x81234567); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(true); - }); - - it('should execute a `rsbs r0, r3` instruction', () => { - // This instruction is also called `negs` - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeRSBS(r0, r3); - rp2040.registers[r3] = 0; - rp2040.executeInstruction(); - expect(rp2040.registers[r0] | 0).toEqual(0); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(true); - expect(rp2040.C).toEqual(true); - expect(rp2040.V).toEqual(false); - }); - - it('should execute a `sbcs r0, r3` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSBCS(r0, r3); - rp2040.registers[r0] = 100; - rp2040.registers[r3] = 55; - rp2040.C = false; - rp2040.executeInstruction(); - expect(rp2040.registers[r0]).toEqual(44); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(true); - expect(rp2040.V).toEqual(false); - }); - - it('should execute a `sdmia r0!, {r1, r2}` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSTMIA(r0, (1 << r1) | (1 << r2)); - rp2040.registers[r0] = 0x20000000; - rp2040.registers[r1] = 0xf00df00d; - rp2040.registers[r2] = 0x4242; - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000002); - expect(rp2040.registers[r0]).toEqual(0x20000008); - expect(rp2040.sramView.getUint32(0, true)).toEqual(0xf00df00d); - expect(rp2040.sramView.getUint32(4, true)).toEqual(0x4242); - }); - - it('should execute a `str r6, [r4, #20]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSTR(r6, r4, 20); - rp2040.registers[r4] = RAM_START_ADDRESS + 0x20; - rp2040.registers[r6] = 0xf00d; - rp2040.executeInstruction(); - expect(rp2040.sramView.getUint32(0x20 + 20, true)).toEqual(0xf00d); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute a `str r6, [r4, r5]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSTRreg(r6, r4, r5); - rp2040.registers[r4] = RAM_START_ADDRESS + 0x20; - rp2040.registers[r5] = 20; - rp2040.registers[r6] = 0xf00d; - rp2040.executeInstruction(); - expect(rp2040.sramView.getUint32(0x20 + 20, true)).toEqual(0xf00d); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute an `str r3, [sp, #12]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.SP = 0x20000000; - rp2040.flash16[0] = opcodeSTRsp(r3, 12); - rp2040.registers[r3] = 0xaa55; - rp2040.executeInstruction(); - expect(rp2040.sram[12]).toEqual(0x55); - expect(rp2040.sram[13]).toEqual(0xaa); - }); - - it('should execute a `strb r6, [r4, #20]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.sramView.setUint32(0x20, 0xf5f4f3f2, true); - rp2040.flash16[0] = opcodeSTRB(r6, r4, 0x1); - rp2040.registers[r4] = RAM_START_ADDRESS + 0x20; - rp2040.registers[r6] = 0xf055; - rp2040.executeInstruction(); + it('should execute a `lsls r5, r5, #18` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x04ad); // lsls r5, r5, #18 + await cpu.setRegisters({ r5: 0b00000000000000000011 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0b11000000000000000000); + expect(registers.pc).toEqual(0x20000002); + expect(registers.C).toEqual(false); + }); + + it('should execute a `lsls r5, r0` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLSLSreg(r5, r0)); + await cpu.setRegisters({ r5: 0b00000000000000000011 }); + await cpu.setRegisters({ r0: 0xff003302 }); // bottom byte: 02 + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0b00000000000000001100); + expect(registers.pc).toEqual(0x20000002); + expect(registers.C).toEqual(false); + }); + + it('should execute a `lsls r5, r5, #18` instruction with carry', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x04ad); // lsls r5, r5, #18 + await cpu.setRegisters({ r5: 0x00004001 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0x40000); + expect(registers.C).toEqual(true); + }); + + it('should execute a `lsrs r5, r0` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLSRSreg(r5, r0)); + await cpu.setRegisters({ r5: 0xff00000f }); + await cpu.setRegisters({ r0: 0xff003302 }); // Shift amount: 02 + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0x3fc00003); + expect(registers.pc).toEqual(0x20000002); + expect(registers.C).toEqual(true); + }); + + it('should execute a `lsrs r1, r1, #1` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLSRS(r1, r1, 1)); + await cpu.setRegisters({ r1: 0b10 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r1).toEqual(0b1); + expect(registers.pc).toEqual(0x20000002); + expect(registers.C).toEqual(false); + }); + + it('should execute a `lsrs r1, r1, 0` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeLSRS(r1, r1, 0)); + await cpu.setRegisters({ r1: 0xffffffff }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r1).toEqual(0); + expect(registers.pc).toEqual(0x20000002); + expect(registers.C).toEqual(true); + }); + + it('should execute a `movs r6, r5` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x002e); // movs r6, r5 + await cpu.setRegisters({ r5: 0x50 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r6).toEqual(0x50); + }); + + it('should execute a `rsbs r0, r3` instruction', async () => { + // This instruction is also calledasync `negs` + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeRSBS(r0, r3)); + await cpu.setRegisters({ r3: 100 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r0 | 0).toEqual(-100); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + expect(registers.V).toEqual(false); + }); + + it('should execute a `rev r3, r1` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeREV(r2, r3)); + await cpu.setRegisters({ r3: 0x11223344 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r2).toEqual(0x44332211); + }); + + it('should execute a `rev16 r0, r5` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeREV16(r0, r5)); + await cpu.setRegisters({ r5: 0x11223344 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r0).toEqual(0x22114433); + }); + + it('should execute a `revsh r1, r2` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeREVSH(r1, r2)); + await cpu.setRegisters({ r2: 0xeeaa55f0 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r1).toEqual(0xfffff055); + }); + + it('should execute a `ror r5, r3` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeROR(r5, r3)); + await cpu.setRegisters({ r5: 0x12345678 }); + await cpu.setRegisters({ r3: 0x2004 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x2004); + expect(registers.r5).toEqual(0x81234567); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(true); + }); + + it('should execute a `ror r5, r3` instruction when r3 > 32', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeROR(r5, r3)); + await cpu.setRegisters({ r5: 0x12345678 }); + await cpu.setRegisters({ r3: 0x2044 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x2044); + expect(registers.r5).toEqual(0x81234567); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(true); + }); + + it('should execute a `rsbs r0, r3` instruction', async () => { + // This instruction is also calledasync `negs` + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeRSBS(r0, r3)); + await cpu.setRegisters({ r3: 0 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r0 | 0).toEqual(0); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(true); + expect(registers.C).toEqual(true); + expect(registers.V).toEqual(false); + }); + + it('should execute a `sbcs r0, r3` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSBCS(r0, r3)); + await cpu.setRegisters({ r0: 100, r3: 55, C: false }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r0).toEqual(44); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(true); + expect(registers.V).toEqual(false); + }); + + it('should execute a `sdmia r0!, {r1, r2}` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSTMIA(r0, (1 << r1) | (1 << r2))); + await cpu.setRegisters({ r0: 0x20000010, r1: 0xf00df00d, r2: 0x4242 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000002); + expect(registers.r0).toEqual(0x20000018); + expect(await cpu.readUint32(0x20000010)).toEqual(0xf00df00d); + expect(await cpu.readUint32(0x20000014)).toEqual(0x4242); + }); + + it('should execute a `str r6, [r4, #20]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSTR(r6, r4, 20)); + await cpu.setRegisters({ r4: RAM_START_ADDRESS + 0x20, r6: 0xf00d }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(await cpu.readUint32(0x20000020 + 20)).toEqual(0xf00d); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute a `str r6, [r4, r5]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSTRreg(r6, r4, r5)); + await cpu.setRegisters({ r4: RAM_START_ADDRESS + 0x20 }); + await cpu.setRegisters({ r5: 20 }); + await cpu.setRegisters({ r6: 0xf00d }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(await cpu.readUint32(0x20000020 + 20)).toEqual(0xf00d); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute an `str r3, [sp, #12]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSTRsp(r3, 12)); + await cpu.setRegisters({ r3: 0xaa55, sp: 0x20000000 }); + await cpu.singleStep(); + expect(await cpu.readUint8(0x20000000 + 12)).toEqual(0x55); + expect(await cpu.readUint8(0x20000000 + 13)).toEqual(0xaa); + }); + + it('should execute a `strb r6, [r4, #20]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSTRB(r6, r4, 0x1)); + await cpu.writeUint32(0x20000020, 0xf5f4f3f2); + await cpu.setRegisters({ r4: RAM_START_ADDRESS + 0x20, r6: 0xf055 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); // assert that the 2nd byte (at 0x21) changed to 0x55 - expect(rp2040.sramView.getUint32(0x20, true)).toEqual(0xf5f455f2); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute a `strb r6, [r4, r5]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.sramView.setUint32(0x20, 0xf5f4f3f2, true); - rp2040.flash16[0] = opcodeSTRBreg(r6, r4, r5); - rp2040.registers[r4] = RAM_START_ADDRESS + 0x20; - rp2040.registers[r5] = 1; - rp2040.registers[r6] = 0xf055; - rp2040.executeInstruction(); + expect(await cpu.readUint32(0x20000020)).toEqual(0xf5f455f2); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute a `strb r6, [r4, r5]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSTRBreg(r6, r4, r5)); + await cpu.writeUint32(0x20000020, 0xf5f4f3f2); + await cpu.setRegisters({ r4: RAM_START_ADDRESS + 0x20 }); + await cpu.setRegisters({ r5: 1 }); + await cpu.setRegisters({ r6: 0xf055 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); // assert that the 2nd byte (at 0x21) changed to 0x55 - expect(rp2040.sramView.getUint32(0x20, true)).toEqual(0xf5f455f2); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute a `strh r6, [r4, #20]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.sramView.setUint32(0x20, 0xf5f4f3f2, true); - rp2040.flash16[0] = opcodeSTRH(r6, r4, 0x2); - rp2040.registers[r4] = RAM_START_ADDRESS + 0x20; - rp2040.registers[r6] = 0x6655; - rp2040.executeInstruction(); + expect(await cpu.readUint32(0x20000020)).toEqual(0xf5f455f2); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute a `strh r6, [r4, #20]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSTRH(r6, r4, 0x2)); + await cpu.writeUint32(0x20000020, 0xf5f4f3f2); + await cpu.setRegisters({ r4: RAM_START_ADDRESS + 0x20 }); + await cpu.setRegisters({ r6: 0x6655 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); // assert that the 3rd/4th byte (at 0x22) changed to 0x6655 - expect(rp2040.sramView.getUint32(0x20, true)).toEqual(0x6655f3f2); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute a `strh r6, [r4, r1]` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.sramView.setUint32(0x20, 0xf5f4f3f2, true); - rp2040.flash16[0] = opcodeSTRHreg(r6, r4, r1); - rp2040.registers[r4] = RAM_START_ADDRESS + 0x20; - rp2040.registers[r1] = 2; - rp2040.registers[r6] = 0x6655; - rp2040.executeInstruction(); + expect(await cpu.readUint32(0x20000020)).toEqual(0x6655f3f2); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute a `strh r6, [r4, r1]` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSTRHreg(r6, r4, r1)); + await cpu.writeUint32(0x20000020, 0xf5f4f3f2); + await cpu.setRegisters({ r4: RAM_START_ADDRESS + 0x20 }); + await cpu.setRegisters({ r1: 2 }); + await cpu.setRegisters({ r6: 0x6655 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); // assert that the 3rd/4th byte (at 0x22) changed to 0x6655 - expect(rp2040.sramView.getUint32(0x20, true)).toEqual(0x6655f3f2); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute a `sub sp, 0x10` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.SP = 0x10000040; - rp2040.flash16[0] = opcodeSUBsp(0x10); - rp2040.executeInstruction(); - expect(rp2040.SP).toEqual(0x10000030); - }); - - it('should execute a `subs r1, #1` instruction with overflow', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSUBS2(r1, 1); - rp2040.registers[r1] = -0x80000000; - rp2040.executeInstruction(); - expect(rp2040.registers[r1]).toEqual(0x7fffffff); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(true); - expect(rp2040.V).toEqual(true); - }); - - it('should execute a `subs r5, r3, 5` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSUBS1(r5, r3, 5); - rp2040.registers[r3] = 0; - rp2040.executeInstruction(); - expect(rp2040.registers[r5] | 0).toEqual(-5); - expect(rp2040.N).toEqual(true); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(false); - expect(rp2040.V).toEqual(false); - }); - - it('should execute a `subs r5, r3, r2` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSUBSreg(r5, r3, r2); - rp2040.registers[r3] = 6; - rp2040.registers[r2] = 5; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(1); - expect(rp2040.N).toEqual(false); - expect(rp2040.Z).toEqual(false); - expect(rp2040.C).toEqual(true); - expect(rp2040.V).toEqual(false); - }); - - it('should raise an SVCALL exception when `svc` instruction runs', () => { - const SVCALL_HANDLER = 0x10002000; - const rp2040 = new RP2040(); - rp2040.SP = 0x20004000; - rp2040.PC = 0x10004000; - rp2040.writeUint16(0x10004000, opcodeSVC(10)); - rp2040.registers[r0] = 0x44; - rp2040.writeUint32(VTOR, 0x10040000); - rp2040.writeUint32(0x10040000 + EXC_SVCALL * 4, SVCALL_HANDLER); - rp2040.writeUint16(SVCALL_HANDLER, opcodeMOVS(r0, 0x55)); - - rp2040.executeInstruction(); - expect(rp2040.pendingSVCall).toEqual(true); - - rp2040.executeInstruction(); // SVCall handler should run here - expect(rp2040.pendingSVCall).toEqual(false); - expect(rp2040.PC).toEqual(SVCALL_HANDLER + 2); - expect(rp2040.registers[r0]).toEqual(0x55); - }); - - it('should execute a `sxtb r2, r2` instruction with sign bit 1', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSXTB(r2, r2); - rp2040.registers[r2] = 0x22446688; - rp2040.executeInstruction(); - expect(rp2040.registers[r2]).toEqual(0xffffff88); - }); - - it('should execute a `sxtb r2, r2` instruction with sign bit 0', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSXTB(r2, r2); - rp2040.registers[r2] = 0x12345678; - rp2040.executeInstruction(); - expect(rp2040.registers[r2]).toEqual(0x78); - }); - - it('should execute a `sxth r2, r5` instruction with sign bit 1', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeSXTH(r2, r5); - rp2040.registers[r5] = 0x22448765; - rp2040.executeInstruction(); - expect(rp2040.registers[r2]).toEqual(0xffff8765); - }); - - it('should execute an `tst r1, r3` instruction when the result is negative', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x4219; // tst r1, r3 - rp2040.registers[r1] = 0xf0000000; - rp2040.registers[r3] = 0xf0004000; - rp2040.sram[24] = 0x55; - rp2040.executeInstruction(); - expect(rp2040.N).toEqual(true); - }); - - it('should execute an `tst r1, r3` instruction when the registers are equal', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = 0x4219; // tst r1, r3 - rp2040.registers[r1] = 0; - rp2040.registers[r3] = 55; - rp2040.sram[24] = 0x55; - rp2040.executeInstruction(); - expect(rp2040.Z).toEqual(true); - }); - - it('should execute an `uxtb r5, r3` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeUXTB(r5, r3); - rp2040.registers[r3] = 0x12345678; - rp2040.executeInstruction(); - expect(rp2040.registers[r5]).toEqual(0x78); - }); - - it('should execute an `uxth r3, r1` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeUXTH(r3, r1); - rp2040.registers[r1] = 0x12345678; - rp2040.executeInstruction(); - expect(rp2040.registers[r3]).toEqual(0x5678); - }); - - it('should execute a `wfi` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeWFI(); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000002); - }); - - it('should execute a `yield` instruction', () => { - const rp2040 = new RP2040(); - rp2040.PC = 0x10000000; - rp2040.flash16[0] = opcodeYIELD(); - rp2040.executeInstruction(); - expect(rp2040.PC).toEqual(0x10000002); + expect(await cpu.readUint32(0x20000020)).toEqual(0x6655f3f2); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute a `sub sp, 0x10` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.setRegisters({ sp: 0x10000040 }); + await cpu.writeUint16(0x20000000, opcodeSUBsp(0x10)); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.sp).toEqual(0x10000030); + }); + + it('should execute a `subs r1, #1` instruction with overflow', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSUBS2(r1, 1)); + await cpu.setRegisters({ r1: -0x80000000 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r1).toEqual(0x7fffffff); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(true); + expect(registers.V).toEqual(true); + }); + + it('should execute a `subs r5, r3, 5` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSUBS1(r5, r3, 5)); + await cpu.setRegisters({ r3: 0 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5 | 0).toEqual(-5); + expect(registers.N).toEqual(true); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(false); + expect(registers.V).toEqual(false); + }); + + it('should execute a `subs r5, r3, r2` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSUBSreg(r5, r3, r2)); + await cpu.setRegisters({ r3: 6 }); + await cpu.setRegisters({ r2: 5 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(1); + expect(registers.N).toEqual(false); + expect(registers.Z).toEqual(false); + expect(registers.C).toEqual(true); + expect(registers.V).toEqual(false); + }); + + it('should raise an SVCALL exception when `svc` instruction runs', async () => { + const SVCALL_HANDLER = 0x20002000; + await cpu.setRegisters({ sp: 0x20004000 }); + cpu.setPC(0x20004000); + await cpu.writeUint16(0x20004000, opcodeSVC(10)); + await cpu.setRegisters({ r0: 0x44 }); + await cpu.writeUint32(VTOR, 0x20040000); + await cpu.writeUint32(0x20040000 + EXC_SVCALL * 4, SVCALL_HANDLER); + await cpu.writeUint16(SVCALL_HANDLER, opcodeMOVS(r0, 0x55)); + + await cpu.singleStep(); + if (cpu instanceof RP2040TestDriver) { + expect(cpu.rp2040.pendingSVCall).toEqual(true); + } + + await cpu.singleStep(); // SVCall handler should run here + const registers2 = await cpu.readRegisters(); + if (cpu instanceof RP2040TestDriver) { + expect(cpu.rp2040.pendingSVCall).toEqual(false); + } + expect(registers2.pc).toEqual(SVCALL_HANDLER + 2); + expect(registers2.r0).toEqual(0x55); + }); + + it('should execute a `sxtb r2, r2` instruction with sign bit 1', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSXTB(r2, r2)); + await cpu.setRegisters({ r2: 0x22446688 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r2).toEqual(0xffffff88); + }); + + it('should execute a `sxtb r2, r2` instruction with sign bit 0', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSXTB(r2, r2)); + await cpu.setRegisters({ r2: 0x12345678 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r2).toEqual(0x78); + }); + + it('should execute a `sxth r2, r5` instruction with sign bit 1', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeSXTH(r2, r5)); + await cpu.setRegisters({ r5: 0x22448765 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r2).toEqual(0xffff8765); + }); + + it('should execute an `tst r1, r3` instruction when the result is negative', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x4219); // tst r1, r3 + await cpu.setRegisters({ r1: 0xf0000000 }); + await cpu.setRegisters({ r3: 0xf0004000 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.N).toEqual(true); + }); + + it('should execute an `tst r1, r3` instruction when the registers are different', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, 0x4219); // tst r1, r3 + await cpu.setRegisters({ r1: 0xf0, r3: 0x0f }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.Z).toEqual(true); + }); + + it('should execute an `uxtb r5, r3` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeUXTB(r5, r3)); + await cpu.setRegisters({ r3: 0x12345678 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r5).toEqual(0x78); + }); + + it('should execute an `uxth r3, r1` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeUXTH(r3, r1)); + await cpu.setRegisters({ r1: 0x12345678 }); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.r3).toEqual(0x5678); + }); + + it('should execute a `wfi` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeWFI()); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000002); + }); + + it('should execute a `yield` instruction', async () => { + await cpu.setPC(0x20000000); + await cpu.writeUint16(0x20000000, opcodeYIELD()); + await cpu.singleStep(); + const registers = await cpu.readRegisters(); + expect(registers.pc).toEqual(0x20000002); }); }); diff --git a/src/utils/gdbclient.ts b/src/utils/gdbclient.ts index bca13ea..0f76070 100644 --- a/src/utils/gdbclient.ts +++ b/src/utils/gdbclient.ts @@ -42,17 +42,16 @@ export class GDBClient { }); } - private readResponse() { + private readResponse(needAck = true) { return new Promise((resolve, reject) => { this.rejectCurrentResponse = reject; - let gotAck = false; let data = ''; const listener = (buffer: Buffer) => { data += buffer.toString(); - if (!gotAck) { + if (needAck) { if (data[0] === '+') { - gotAck = true; + needAck = false; data = data.substr(1); } else { this.socket.off('data', listener); @@ -78,6 +77,21 @@ export class GDBClient { return await this.readResponse(); } + async monitor(cmd: string) { + const buf = new Uint8Array(cmd.length); + for (let i = 0; i < cmd.length; i++) { + buf[i] = cmd.charCodeAt(i); + } + let response = await this.sendCommand(`qRcmd,${encodeHexBuf(buf)}`); + while (response !== 'OK' && response[0] === 'O') { + this.socket.write('+'); + response = await this.readResponse(false); + } + if (response !== 'OK') { + throw new Error(`Invalid monitor response: ${response}`); + } + } + async readRegisters() { const response = await this.sendCommand('g'); return decodeHexUint32Array(response); @@ -111,6 +125,9 @@ export class GDBClient { async readRegister(index: number) { const response = await this.sendCommand(`p${encodeHexByte(index)}`); + if (response.length === 2) { + return decodeHexBuf(response)[0]; + } return decodeHexUint32(response); } @@ -143,4 +160,9 @@ export class GDBClient { throw new Error(`Invalid writeRegister response: ${response}`); } } + + disconnect() { + this.rejectCurrentResponse = undefined; + this.socket.destroy(); + } } diff --git a/src/utils/test-driver-gdb.ts b/src/utils/test-driver-gdb.ts new file mode 100644 index 0000000..5099bfa --- /dev/null +++ b/src/utils/test-driver-gdb.ts @@ -0,0 +1,150 @@ +import { GDBClient } from './gdbclient'; +import { ICortexRegisterName, ICortexRegisters, ICortexTestDriver } from './test-driver'; + +const pc = 15; + +export class GDBTestDriver implements ICortexTestDriver { + constructor(private readonly gdbClient: GDBClient) {} + + async init() { + await this.gdbClient.monitor('reset init'); + } + + async tearDown() { + this.gdbClient.disconnect(); + } + + async setPC(pcValue: number) { + await this.gdbClient.writeRegister(pc, pcValue); + } + + async writeUint8(address: number, value: number) { + await this.gdbClient.writeMemory(address, new Uint8Array([value])); + } + + async writeUint16(address: number, value: number) { + await this.gdbClient.writeMemory(address, new Uint8Array(new Uint16Array([value]).buffer)); + } + + async writeUint32(address: number, value: number) { + await this.gdbClient.writeMemory(address, new Uint8Array(new Uint32Array([value]).buffer)); + } + + async setRegisters(registers: Partial) { + const registerMap = { + r0: 0, + r1: 1, + r2: 2, + r3: 3, + r4: 4, + r5: 5, + r6: 6, + r7: 7, + r8: 8, + r9: 9, + r10: 10, + r11: 11, + r12: 12, + sp: 13, + lr: 14, + pc: 15, + xPSR: 16, + MSP: 17, + PSP: 18, + PRIMASK: 19, + CONTROL: 22, + N: null, + Z: null, + C: null, + V: null, + }; + const xSPR = registerMap.xPSR; + let haveFlagRegisters = false; + for (const key of Object.keys(registers) as ICortexRegisterName[]) { + const registerIndex = registerMap[key]; + if (registerIndex != null) { + const value = registers[key] as number; + await this.gdbClient.writeRegister(registerIndex, value); + } else { + haveFlagRegisters = true; + } + } + if (haveFlagRegisters) { + let xPSRValue = await this.gdbClient.readRegister(xSPR); + const flagBits = { + N: 0x80000000, + Z: 0x40000000, + C: 0x20000000, + V: 0x10000000, + }; + for (const flag of Object.keys(flagBits) as (keyof typeof flagBits)[]) { + if (flag in registers) { + const flagBitMask = flagBits[flag]; + const flagValue = registers[flag]; + if (flagValue) { + xPSRValue |= flagBitMask; + } else { + xPSRValue &= ~flagBitMask; + } + } + } + await this.gdbClient.writeRegister(xSPR, xPSRValue); + } + } + + async setRegister(index: number, value: number) { + await this.gdbClient.writeRegister(index, value); + } + + async singleStep() { + await this.gdbClient.singleStep(); + } + + async readRegisters(): Promise { + const registers = await this.gdbClient.readRegisters(); + const xPSR = registers[16]; + return { + r0: registers[0], + r1: registers[1], + r2: registers[2], + r3: registers[3], + r4: registers[4], + r5: registers[5], + r6: registers[6], + r7: registers[7], + r8: registers[8], + r9: registers[9], + r10: registers[10], + r11: registers[11], + r12: registers[12], + sp: registers[13], + lr: registers[14], + pc: registers[15], + xPSR, + MSP: await this.gdbClient.readRegister(17), + PSP: await this.gdbClient.readRegister(18), + PRIMASK: await this.gdbClient.readRegister(19), + CONTROL: await this.gdbClient.readRegister(22), + + N: !!(xPSR & 0x80000000), + Z: !!(xPSR & 0x40000000), + C: !!(xPSR & 0x20000000), + V: !!(xPSR & 0x10000000), + }; + } + + async readUint8(address: number) { + const result = await this.gdbClient.readMemory(address, 1); + return result[0]; + } + + async readUint16(address: number) { + const result = await this.gdbClient.readMemory(address, 2); + return new Uint16Array(result.buffer)[0]; + } + + async readUint32(address: number) { + const result = await this.gdbClient.readMemory(address, 4); + return new Uint32Array(result.buffer)[0]; + } +} diff --git a/src/utils/test-driver-rp2040.ts b/src/utils/test-driver-rp2040.ts new file mode 100644 index 0000000..9ad6da6 --- /dev/null +++ b/src/utils/test-driver-rp2040.ts @@ -0,0 +1,163 @@ +import { RP2040, SYSM_CONTROL, SYSM_MSP, SYSM_PRIMASK, SYSM_PSP } from '../rp2040'; +import { ICortexRegisterName, ICortexRegisters, ICortexTestDriver } from './test-driver'; + +export class RP2040TestDriver implements ICortexTestDriver { + constructor(readonly rp2040: RP2040) {} + + async init() { + /* this page intentionally left blank ! */ + } + + async tearDown() { + /* this page intentionally left blank ! */ + } + + async setPC(pcValue: number) { + this.rp2040.PC = pcValue; + } + + async writeUint8(address: number, value: number) { + this.rp2040.writeUint8(address, value); + } + + async writeUint16(address: number, value: number) { + this.rp2040.writeUint16(address, value); + } + + async writeUint32(address: number, value: number) { + this.rp2040.writeUint32(address, value); + } + + async setRegisters(registers: Partial) { + const { rp2040 } = this; + for (const key of Object.keys(registers) as ICortexRegisterName[]) { + const value = registers[key] as number; + const boolValue = registers[key] as boolean; + switch (key) { + case 'r0': + rp2040.registers[0] = value; + break; + case 'r1': + rp2040.registers[1] = value; + break; + case 'r2': + rp2040.registers[2] = value; + break; + case 'r3': + rp2040.registers[3] = value; + break; + case 'r4': + rp2040.registers[4] = value; + break; + case 'r5': + rp2040.registers[5] = value; + break; + case 'r6': + rp2040.registers[6] = value; + break; + case 'r7': + rp2040.registers[7] = value; + break; + case 'r8': + rp2040.registers[8] = value; + break; + case 'r9': + rp2040.registers[9] = value; + break; + case 'r10': + rp2040.registers[10] = value; + break; + case 'r11': + rp2040.registers[11] = value; + break; + case 'r12': + rp2040.registers[12] = value; + break; + case 'sp': + rp2040.registers[13] = value; + break; + case 'lr': + rp2040.registers[14] = value; + break; + case 'pc': + rp2040.registers[15] = value; + break; + case 'xPSR': + rp2040.xPSR = value; + break; + case 'MSP': + rp2040.writeSpecialRegister(SYSM_MSP, value); + break; + case 'PSP': + rp2040.writeSpecialRegister(SYSM_PSP, value); + break; + case 'PRIMASK': + rp2040.writeSpecialRegister(SYSM_PRIMASK, value); + break; + case 'CONTROL': + rp2040.writeSpecialRegister(SYSM_CONTROL, value); + break; + case 'N': + rp2040.N = boolValue; + break; + case 'Z': + rp2040.Z = boolValue; + break; + case 'C': + rp2040.C = boolValue; + break; + case 'V': + rp2040.V = boolValue; + break; + } + } + } + + async singleStep() { + this.rp2040.executeInstruction(); + } + + async readRegisters(): Promise { + const { registers, xPSR } = this.rp2040; + return { + r0: registers[0], + r1: registers[1], + r2: registers[2], + r3: registers[3], + r4: registers[4], + r5: registers[5], + r6: registers[6], + r7: registers[7], + r8: registers[8], + r9: registers[9], + r10: registers[10], + r11: registers[11], + r12: registers[12], + sp: registers[13], + lr: registers[14], + pc: registers[15], + xPSR, + MSP: this.rp2040.readSpecialRegister(SYSM_MSP), + PSP: this.rp2040.readSpecialRegister(SYSM_PSP), + PRIMASK: this.rp2040.readSpecialRegister(SYSM_PRIMASK), + CONTROL: this.rp2040.readSpecialRegister(SYSM_CONTROL), + + N: !!(xPSR & 0x80000000), + Z: !!(xPSR & 0x40000000), + C: !!(xPSR & 0x20000000), + V: !!(xPSR & 0x10000000), + }; + } + + async readUint8(address: number) { + return this.rp2040.readUint8(address); + } + + async readUint16(address: number) { + return this.rp2040.readUint16(address); + } + + async readUint32(address: number) { + return this.rp2040.readUint32(address); + } +} diff --git a/src/utils/test-driver.ts b/src/utils/test-driver.ts new file mode 100644 index 0000000..273d77f --- /dev/null +++ b/src/utils/test-driver.ts @@ -0,0 +1,45 @@ +export interface ICortexRegisters { + r0: number; + r1: number; + r2: number; + r3: number; + r4: number; + r5: number; + r6: number; + r7: number; + r8: number; + r9: number; + r10: number; + r11: number; + r12: number; + sp: number; + lr: number; + pc: number; + xPSR: number; + MSP: number; + PSP: number; + PRIMASK: number; + CONTROL: number; + + N: boolean; + Z: boolean; + C: boolean; + V: boolean; +} + +export type ICortexRegisterName = keyof ICortexRegisters; + +export interface ICortexTestDriver { + init(): Promise; + setPC(pcValue: number): Promise; + writeUint8(address: number, value: number): Promise; + writeUint16(address: number, value: number): Promise; + writeUint32(address: number, value: number): Promise; + setRegisters(registers: Partial): Promise; + singleStep(): Promise; + readRegisters(): Promise; + readUint8(address: number): Promise; + readUint16(address: number): Promise; + readUint32(address: number): Promise; + tearDown(): Promise; +}