diff options
| author | Uri Shaked | 2020-04-09 22:54:32 +0300 |
|---|---|---|
| committer | Uri Shaked | 2020-04-09 22:54:32 +0300 |
| commit | d705922dfe12863e1ab4b9f9979916b224f09028 (patch) | |
| tree | 6b751a631974f5958a3a805ea6df7c15d52eae00 /src | |
| parent | style(demo): formatting, lint issue (diff) | |
| download | avr8js-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.ts | 18 | ||||
| -rw-r--r-- | src/cpu/instruction.spec.ts | 81 | ||||
| -rw-r--r-- | src/cpu/instruction.ts | 40 | ||||
| -rw-r--r-- | src/types.ts | 1 |
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; |
