aboutsummaryrefslogtreecommitdiff
path: root/src
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
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 'src')
-rw-r--r--src/cpu/cpu.ts18
-rw-r--r--src/cpu/instruction.spec.ts81
-rw-r--r--src/cpu/instruction.ts40
-rw-r--r--src/types.ts1
4 files changed, 120 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 */
diff --git a/src/types.ts b/src/types.ts
index 8a13fe6..f87f298 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,2 +1,3 @@
export type u8 = number;
export type u16 = number;
+export type u32 = number;
If you find this content useful please consider a small donation via bitcoin: bitcoin:1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2?amount=0.001&message=Donation Scraping git contents via cgit is very resource intensive. The continued hosting of these git repositories is entirely financed by your bitcoin contributions. For donations of a least 1 mBTC you include your IP address in the message of your bitcoin donation. This will remove this message in the future for requests from your IP, which will significantly reduce token usage. In any case, any contributions to bitcoin address 1mxKyQsHHugqRxPKgwaA7wUwJEGCNthn2 are very welcome. Thanks in advance for you contribution to a self-sustaining ecosystem.