aboutsummaryrefslogtreecommitdiff
path: root/src/cpu
diff options
context:
space:
mode:
authorUri Shaked2020-04-09 22:54:32 +0300
committerUri Shaked2020-04-09 22:54:32 +0300
commitd705922dfe12863e1ab4b9f9979916b224f09028 (patch)
tree6b751a631974f5958a3a805ea6df7c15d52eae00 /src/cpu
parentstyle(demo): formatting, lint issue (diff)
downloadavr8js-d705922dfe12863e1ab4b9f9979916b224f09028.tar.gz
avr8js-d705922dfe12863e1ab4b9f9979916b224f09028.tar.bz2
avr8js-d705922dfe12863e1ab4b9f9979916b224f09028.zip
feat(instruction): 22-bit PC support #31
adapt CALL, ICALL, RCALL, RET, and RETI for MCUs with 22-bit PC
Diffstat (limited to '')
-rw-r--r--src/cpu/cpu.ts18
-rw-r--r--src/cpu/instruction.spec.ts81
-rw-r--r--src/cpu/instruction.ts40
3 files changed, 119 insertions, 20 deletions
diff --git a/src/cpu/cpu.ts b/src/cpu/cpu.ts
index 93f79d0..0c60c9e 100644
--- a/src/cpu/cpu.ts
+++ b/src/cpu/cpu.ts
@@ -5,7 +5,7 @@
* Copyright (C) 2019, Uri Shaked
*/
-import { u16, u8 } from '../types';
+import { u32, u16, u8 } from '../types';
const registerSpace = 0x100;
@@ -15,7 +15,20 @@ export interface ICPU {
readonly dataView: DataView;
readonly progMem: Uint16Array;
readonly progBytes: Uint8Array;
- pc: u16;
+
+ /**
+ * Whether the program counter (PC) can address 22 bits (the default is 16)
+ */
+ readonly pc22Bits: boolean;
+
+ /**
+ * Program counter
+ */
+ pc: u32;
+
+ /**
+ * Clock cycle counter
+ */
cycles: number;
readData(addr: u16): u8;
@@ -33,6 +46,7 @@ export class CPU implements ICPU {
readonly dataView = new DataView(this.data.buffer);
readonly progBytes = new Uint8Array(this.progMem.buffer);
readonly writeHooks: CPUMemoryHooks = [];
+ readonly pc22Bits = this.progBytes.length > 0x20000;
pc = 0;
cycles = 0;
diff --git a/src/cpu/instruction.spec.ts b/src/cpu/instruction.spec.ts
index 4f6e045..5ad160d 100644
--- a/src/cpu/instruction.spec.ts
+++ b/src/cpu/instruction.spec.ts
@@ -117,11 +117,23 @@ describe('avrInstruction', () => {
cpu.data[93] = 150; // SP <- 50
avrInstruction(cpu);
expect(cpu.pc).toEqual(0x5c);
- expect(cpu.cycles).toEqual(5);
+ expect(cpu.cycles).toEqual(4);
expect(cpu.data[150]).toEqual(2); // return addr
expect(cpu.data[93]).toEqual(148); // SP should be decremented
});
+ it('should push 3-byte return address when executing `CALL` instruction on device with >128k flash', () => {
+ cpu = new CPU(new Uint16Array(0x20000));
+ loadProgram('CALL 0xb8');
+ cpu.data[94] = 0;
+ cpu.data[93] = 150; // SP <- 50
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(0x5c);
+ expect(cpu.cycles).toEqual(5);
+ expect(cpu.data[150]).toEqual(2); // return addr
+ expect(cpu.data[93]).toEqual(147); // SP should be decremented by 3
+ });
+
it('should execute `CPC r27, r18` instruction', () => {
loadProgram('CPC r27, r18');
cpu.data[18] = 0x1;
@@ -261,7 +273,20 @@ describe('avrInstruction', () => {
expect(cpu.cycles).toEqual(3);
expect(cpu.pc).toEqual(0x2020);
expect(cpu.data[0x80]).toEqual(1); // Return address
- expect(cpu.data[93]).toEqual(0x7e);
+ expect(cpu.data[93]).toEqual(0x7e); // SP Should decrement by 2
+ });
+
+ it('should push 3-byte return address when executing `ICALL` instruction on device with >128k flash', () => {
+ cpu = new CPU(new Uint16Array(0x20000));
+ loadProgram('ICALL');
+ cpu.data[94] = 0;
+ cpu.data[93] = 0x80;
+ cpu.dataView.setUint16(30, 0x2020, true); // Z <- 0x2020
+ avrInstruction(cpu);
+ expect(cpu.cycles).toEqual(4);
+ expect(cpu.pc).toEqual(0x2020);
+ expect(cpu.data[0x80]).toEqual(1); // Return address
+ expect(cpu.data[93]).toEqual(0x7d); // SP Should decrement by 3
});
it('should execute `IJMP` instruction', () => {
@@ -659,9 +684,9 @@ describe('avrInstruction', () => {
cpu.data[93] = 0x80; // SP <- 0x80
avrInstruction(cpu);
expect(cpu.pc).toEqual(4);
- expect(cpu.cycles).toEqual(4);
+ expect(cpu.cycles).toEqual(3);
expect(cpu.dataView.getUint16(0x80, true)).toEqual(1); // RET address
- expect(cpu.data[93]).toEqual(0x7e); // SP
+ expect(cpu.data[93]).toEqual(0x7e); // SP should decrement by 2
});
it('should execute `RCALL .-4` instruction', () => {
@@ -671,9 +696,22 @@ describe('avrInstruction', () => {
avrInstruction(cpu);
avrInstruction(cpu);
expect(cpu.pc).toEqual(0);
- expect(cpu.cycles).toEqual(5); // 1 for NOP, 4 for RCALL
+ expect(cpu.cycles).toEqual(4); // 1 for NOP, 3 for RCALL
expect(cpu.dataView.getUint16(0x80, true)).toEqual(2); // RET address
- expect(cpu.data[93]).toEqual(0x7e); // SP
+ expect(cpu.data[93]).toEqual(0x7e); // SP should decrement by 2
+ });
+
+ it('should push 3-byte return address when executing `RCALL` instruction on device with >128k flash', () => {
+ cpu = new CPU(new Uint16Array(0x20000));
+ loadProgram('RCALL 6');
+ cpu.data[94] = 0;
+ cpu.data[93] = 0x80;
+ cpu.dataView.setUint16(30, 0x2020, true); // Z <- 0x2020
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(4);
+ expect(cpu.cycles).toEqual(4);
+ expect(cpu.dataView.getUint16(0x80, true)).toEqual(1); // RET address
+ expect(cpu.data[93]).toEqual(0x7d); // SP Should decrement by 3
});
it('should execute `RET` instruction', () => {
@@ -683,8 +721,21 @@ describe('avrInstruction', () => {
cpu.data[0x92] = 16;
avrInstruction(cpu);
expect(cpu.pc).toEqual(16);
+ expect(cpu.cycles).toEqual(4);
+ expect(cpu.data[93]).toEqual(0x92); // SP should increment by 2
+ });
+
+ it('should execute `RET` instruction on device with >128k flash', () => {
+ cpu = new CPU(new Uint16Array(0x20000));
+ loadProgram('RET');
+ cpu.data[94] = 0;
+ cpu.data[93] = 0x90; // SP <- 0x90
+ cpu.data[0x91] = 0x1;
+ cpu.data[0x93] = 0x16;
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(0x10016);
expect(cpu.cycles).toEqual(5);
- expect(cpu.data[93]).toEqual(0x92); // SP should increment
+ expect(cpu.data[93]).toEqual(0x93); // SP should increment by 3
});
it('should execute `RETI` instruction', () => {
@@ -694,8 +745,22 @@ describe('avrInstruction', () => {
cpu.data[0xc2] = 200;
avrInstruction(cpu);
expect(cpu.pc).toEqual(200);
+ expect(cpu.cycles).toEqual(4);
+ expect(cpu.data[93]).toEqual(0xc2); // SP should increment by 2
+ expect(cpu.data[95]).toEqual(0b10000000); // SREG: I
+ });
+
+ it('should execute `RETI` instruction on device with >128k flash', () => {
+ cpu = new CPU(new Uint16Array(0x20000));
+ loadProgram('RETI');
+ cpu.data[94] = 0;
+ cpu.data[93] = 0xc0; // SP <- 0xc0
+ cpu.data[0xc1] = 0x1;
+ cpu.data[0xc3] = 0x30;
+ avrInstruction(cpu);
+ expect(cpu.pc).toEqual(0x10030);
expect(cpu.cycles).toEqual(5);
- expect(cpu.data[93]).toEqual(0xc2); // SP should increment
+ expect(cpu.data[93]).toEqual(0xc3); // SP should increment by 3
expect(cpu.data[95]).toEqual(0b10000000); // SREG: I
});
diff --git a/src/cpu/instruction.ts b/src/cpu/instruction.ts
index 07f1576..40ff71f 100644
--- a/src/cpu/instruction.ts
+++ b/src/cpu/instruction.ts
@@ -131,11 +131,15 @@ export function avrInstruction(cpu: ICPU) {
const k = cpu.progMem[cpu.pc + 1] | ((opcode & 1) << 16) | ((opcode & 0x1f0) << 13);
const ret = cpu.pc + 2;
const sp = cpu.dataView.getUint16(93, true);
+ const { pc22Bits } = cpu;
cpu.data[sp] = 255 & ret;
cpu.data[sp - 1] = (ret >> 8) & 255;
- cpu.dataView.setUint16(93, sp - 2, true);
+ if (pc22Bits) {
+ cpu.data[sp - 2] = (ret >> 16) & 255;
+ }
+ cpu.dataView.setUint16(93, sp - (pc22Bits ? 3 : 2), true);
cpu.pc = k - 1;
- cpu.cycles += 4;
+ cpu.cycles += pc22Bits ? 4 : 3;
} else if ((opcode & 0xff00) === 0x9800) {
/* CBI, 1001 1000 AAAA Abbb */
const A = opcode & 0xf8;
@@ -282,11 +286,15 @@ export function avrInstruction(cpu: ICPU) {
/* ICALL, 1001 0101 0000 1001 */
const retAddr = cpu.pc + 1;
const sp = cpu.dataView.getUint16(93, true);
+ const { pc22Bits } = cpu;
cpu.data[sp] = retAddr & 255;
cpu.data[sp - 1] = (retAddr >> 8) & 255;
- cpu.dataView.setUint16(93, sp - 2, true);
+ if (pc22Bits) {
+ cpu.data[sp - 2] = (retAddr >> 16) & 255;
+ }
+ cpu.dataView.setUint16(93, sp - (pc22Bits ? 3 : 2), true);
cpu.pc = cpu.dataView.getUint16(30, true) - 1;
- cpu.cycles += 2;
+ cpu.cycles += pc22Bits ? 3 : 2;
} else if (opcode === 0x9409) {
/* IJMP, 1001 0100 0000 1001 */
cpu.pc = cpu.dataView.getUint16(30, true) - 1;
@@ -512,23 +520,35 @@ export function avrInstruction(cpu: ICPU) {
const k = (opcode & 0x7ff) - (opcode & 0x800 ? 0x800 : 0);
const retAddr = cpu.pc + 1;
const sp = cpu.dataView.getUint16(93, true);
+ const { pc22Bits } = cpu;
cpu.data[sp] = 255 & retAddr;
cpu.data[sp - 1] = (retAddr >> 8) & 255;
- cpu.dataView.setUint16(93, sp - 2, true);
+ if (pc22Bits) {
+ cpu.data[sp - 2] = (retAddr >> 16) & 255;
+ }
+ cpu.dataView.setUint16(93, sp - (pc22Bits ? 3 : 2), true);
cpu.pc += k;
- cpu.cycles += 3;
+ cpu.cycles += pc22Bits ? 3 : 2;
} else if (opcode === 0x9508) {
/* RET, 1001 0101 0000 1000 */
- const i = cpu.dataView.getUint16(93, true) + 2;
+ const { pc22Bits } = cpu;
+ const i = cpu.dataView.getUint16(93, true) + (pc22Bits ? 3 : 2);
cpu.dataView.setUint16(93, i, true);
cpu.pc = (cpu.data[i - 1] << 8) + cpu.data[i] - 1;
- cpu.cycles += 4;
+ if (pc22Bits) {
+ cpu.pc |= cpu.data[i - 2] << 16;
+ }
+ cpu.cycles += pc22Bits ? 4 : 3;
} else if (opcode === 0x9518) {
/* RETI, 1001 0101 0001 1000 */
- const i = cpu.dataView.getUint16(93, true) + 2;
+ const { pc22Bits } = cpu;
+ const i = cpu.dataView.getUint16(93, true) + (pc22Bits ? 3 : 2);
cpu.dataView.setUint16(93, i, true);
cpu.pc = (cpu.data[i - 1] << 8) + cpu.data[i] - 1;
- cpu.cycles += 4;
+ if (pc22Bits) {
+ cpu.pc |= cpu.data[i - 2] << 16;
+ }
+ cpu.cycles += pc22Bits ? 4 : 3;
cpu.data[95] |= 0x80; // Enable interrupts
} else if ((opcode & 0xf000) === 0xc000) {
/* RJMP, 1100 kkkk kkkk kkkk */