aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/cpu/cpu.ts30
-rw-r--r--src/cpu/instruction.ts53
-rw-r--r--src/cpu/interrupt.ts7
3 files changed, 60 insertions, 30 deletions
diff --git a/src/cpu/cpu.ts b/src/cpu/cpu.ts
index 4065b0a..6da0fee 100644
--- a/src/cpu/cpu.ts
+++ b/src/cpu/cpu.ts
@@ -59,6 +59,21 @@ export class CPU {
*/
readonly pc22Bits = this.progBytes.length > 0x20000;
+ /**
+ * Offset applied to data-space addresses in readData/writeData.
+ * Classic AVR (ATmega): 0 — data addresses map 1:1 to the data array.
+ * Modern AVR (AVR-Dx, avrxmega3): 32 — data addresses are shifted by 32
+ * to avoid collision with R0-R31 which occupy data[0..31].
+ */
+ readonly dataMemoryOffset: number;
+
+ /**
+ * Offset added to I/O addresses for IN/OUT/SBI/CBI/SBIS/SBIC instructions.
+ * Classic AVR (ATmega): 32 — I/O space starts at data address 0x20.
+ * Modern AVR (AVR-Dx): 0 — readData applies dataMemoryOffset instead.
+ */
+ readonly ioOffset: number;
+
readonly gpioPorts = new Set<AVRIOPort>();
readonly gpioByPort: AVRIOPort[] = [];
@@ -71,6 +86,14 @@ export class CPU {
};
/**
+ * This function is called by the SLEEP instruction. The sleep controller peripheral
+ * attaches to it to handle sleep modes (e.g., fast-forwarding to the next clock event).
+ */
+ onSleep = () => {
+ /* empty by default */
+ };
+
+ /**
* Program counter
*/
pc: u32 = 0;
@@ -86,7 +109,10 @@ export class CPU {
constructor(
public progMem: Uint16Array,
private sramBytes = 8192,
+ { dataMemoryOffset = 0, ioOffset = 32 }: { dataMemoryOffset?: number; ioOffset?: number } = {},
) {
+ this.dataMemoryOffset = dataMemoryOffset;
+ this.ioOffset = ioOffset;
this.reset();
}
@@ -99,13 +125,15 @@ export class CPU {
}
readData(addr: number) {
- if (addr >= 32 && this.readHooks[addr]) {
+ addr += this.dataMemoryOffset;
+ if (this.readHooks[addr]) {
return this.readHooks[addr](addr);
}
return this.data[addr];
}
writeData(addr: number, value: number, mask = 0xff) {
+ addr += this.dataMemoryOffset;
const hook = this.writeHooks[addr];
if (hook) {
if (hook(value, this.data[addr], addr, mask)) {
diff --git a/src/cpu/instruction.ts b/src/cpu/instruction.ts
index 77f27ba..b9b7f8b 100644
--- a/src/cpu/instruction.ts
+++ b/src/cpu/instruction.ts
@@ -31,6 +31,7 @@ function isTwoWordInstruction(opcode: u16) {
export function avrInstruction(cpu: CPU) {
const opcode = cpu.progMem[cpu.pc];
+ const dmo = cpu.dataMemoryOffset; // data memory offset for stack accesses
if ((opcode & 0xfc00) === 0x1c00) {
/* ADC, 0001 11rd dddd rrrr */
@@ -139,10 +140,10 @@ export function avrInstruction(cpu: CPU) {
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.data[sp + dmo] = 255 & ret;
+ cpu.data[sp - 1 + dmo] = (ret >> 8) & 255;
if (pc22Bits) {
- cpu.data[sp - 2] = (ret >> 16) & 255;
+ cpu.data[sp - 2 + dmo] = (ret >> 16) & 255;
}
cpu.dataView.setUint16(93, sp - (pc22Bits ? 3 : 2), true);
cpu.pc = k - 1;
@@ -151,9 +152,9 @@ export function avrInstruction(cpu: CPU) {
/* CBI, 1001 1000 AAAA Abbb */
const A = opcode & 0xf8;
const b = opcode & 7;
- const R = cpu.readData((A >> 3) + 32);
+ const R = cpu.readData((A >> 3) + cpu.ioOffset);
const mask = 1 << b;
- cpu.writeData((A >> 3) + 32, R & ~mask, mask);
+ cpu.writeData((A >> 3) + cpu.ioOffset, R & ~mask, mask);
} else if ((opcode & 0xfe0f) === 0x9400) {
/* COM, 1001 010d dddd 0000 */
const d = (opcode & 0x1f0) >> 4;
@@ -226,9 +227,9 @@ export function avrInstruction(cpu: CPU) {
const retAddr = cpu.pc + 1;
const sp = cpu.dataView.getUint16(93, true);
const eind = cpu.data[0x5c];
- cpu.data[sp] = retAddr & 255;
- cpu.data[sp - 1] = (retAddr >> 8) & 255;
- cpu.data[sp - 2] = (retAddr >> 16) & 255;
+ cpu.data[sp + dmo] = retAddr & 255;
+ cpu.data[sp - 1 + dmo] = (retAddr >> 8) & 255;
+ cpu.data[sp - 2 + dmo] = (retAddr >> 16) & 255;
cpu.dataView.setUint16(93, sp - 3, true);
cpu.pc = ((eind << 16) | cpu.dataView.getUint16(30, true)) - 1;
cpu.cycles += 3;
@@ -296,10 +297,10 @@ export function avrInstruction(cpu: CPU) {
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.data[sp + dmo] = retAddr & 255;
+ cpu.data[sp - 1 + dmo] = (retAddr >> 8) & 255;
if (pc22Bits) {
- cpu.data[sp - 2] = (retAddr >> 16) & 255;
+ cpu.data[sp - 2 + dmo] = (retAddr >> 16) & 255;
}
cpu.dataView.setUint16(93, sp - (pc22Bits ? 3 : 2), true);
cpu.pc = cpu.dataView.getUint16(30, true) - 1;
@@ -310,7 +311,7 @@ export function avrInstruction(cpu: CPU) {
cpu.cycles++;
} else if ((opcode & 0xf800) === 0xb000) {
/* IN, 1011 0AAd dddd AAAA */
- const i = cpu.readData(((opcode & 0xf) | ((opcode & 0x600) >> 5)) + 32);
+ const i = cpu.readData(((opcode & 0xf) | ((opcode & 0x600) >> 5)) + cpu.ioOffset);
cpu.data[(opcode & 0x1f0) >> 4] = i;
} else if ((opcode & 0xfe0f) === 0x9403) {
/* INC, 1001 010d dddd 0011 */
@@ -514,17 +515,17 @@ export function avrInstruction(cpu: CPU) {
cpu.data[95] = sreg;
} else if ((opcode & 0xf800) === 0xb800) {
/* OUT, 1011 1AAr rrrr AAAA */
- cpu.writeData(((opcode & 0xf) | ((opcode & 0x600) >> 5)) + 32, cpu.data[(opcode & 0x1f0) >> 4]);
+ cpu.writeData(((opcode & 0xf) | ((opcode & 0x600) >> 5)) + cpu.ioOffset, cpu.data[(opcode & 0x1f0) >> 4]);
} else if ((opcode & 0xfe0f) === 0x900f) {
/* POP, 1001 000d dddd 1111 */
const value = cpu.dataView.getUint16(93, true) + 1;
cpu.dataView.setUint16(93, value, true);
- cpu.data[(opcode & 0x1f0) >> 4] = cpu.data[value];
+ cpu.data[(opcode & 0x1f0) >> 4] = cpu.data[value + dmo];
cpu.cycles++;
} else if ((opcode & 0xfe0f) === 0x920f) {
/* PUSH, 1001 001d dddd 1111 */
const value = cpu.dataView.getUint16(93, true);
- cpu.data[value] = cpu.data[(opcode & 0x1f0) >> 4];
+ cpu.data[value + dmo] = cpu.data[(opcode & 0x1f0) >> 4];
cpu.dataView.setUint16(93, value - 1, true);
cpu.cycles++;
} else if ((opcode & 0xf000) === 0xd000) {
@@ -533,10 +534,10 @@ export function avrInstruction(cpu: CPU) {
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.data[sp + dmo] = 255 & retAddr;
+ cpu.data[sp - 1 + dmo] = (retAddr >> 8) & 255;
if (pc22Bits) {
- cpu.data[sp - 2] = (retAddr >> 16) & 255;
+ cpu.data[sp - 2 + dmo] = (retAddr >> 16) & 255;
}
cpu.dataView.setUint16(93, sp - (pc22Bits ? 3 : 2), true);
cpu.pc += k;
@@ -546,9 +547,9 @@ export function avrInstruction(cpu: CPU) {
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.pc = (cpu.data[i - 1 + dmo] << 8) + cpu.data[i + dmo] - 1;
if (pc22Bits) {
- cpu.pc |= cpu.data[i - 2] << 16;
+ cpu.pc |= cpu.data[i - 2 + dmo] << 16;
}
cpu.cycles += pc22Bits ? 4 : 3;
} else if (opcode === 0x9518) {
@@ -556,9 +557,9 @@ export function avrInstruction(cpu: CPU) {
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.pc = (cpu.data[i - 1 + dmo] << 8) + cpu.data[i + dmo] - 1;
if (pc22Bits) {
- cpu.pc |= cpu.data[i - 2] << 16;
+ cpu.pc |= cpu.data[i - 2 + dmo] << 16;
}
cpu.cycles += pc22Bits ? 4 : 3;
cpu.data[95] |= 0x80; // Enable interrupts
@@ -606,13 +607,13 @@ export function avrInstruction(cpu: CPU) {
cpu.data[95] = sreg;
} else if ((opcode & 0xff00) === 0x9a00) {
/* SBI, 1001 1010 AAAA Abbb */
- const target = ((opcode & 0xf8) >> 3) + 32;
+ const target = ((opcode & 0xf8) >> 3) + cpu.ioOffset;
const mask = 1 << (opcode & 7);
cpu.writeData(target, cpu.readData(target) | mask, mask);
cpu.cycles++;
} else if ((opcode & 0xff00) === 0x9900) {
/* SBIC, 1001 1001 AAAA Abbb */
- const value = cpu.readData(((opcode & 0xf8) >> 3) + 32);
+ const value = cpu.readData(((opcode & 0xf8) >> 3) + cpu.ioOffset);
if (!(value & (1 << (opcode & 7)))) {
const nextOpcode = cpu.progMem[cpu.pc + 1];
const skipSize = isTwoWordInstruction(nextOpcode) ? 2 : 1;
@@ -621,7 +622,7 @@ export function avrInstruction(cpu: CPU) {
}
} else if ((opcode & 0xff00) === 0x9b00) {
/* SBIS, 1001 1011 AAAA Abbb */
- const value = cpu.readData(((opcode & 0xf8) >> 3) + 32);
+ const value = cpu.readData(((opcode & 0xf8) >> 3) + cpu.ioOffset);
if (value & (1 << (opcode & 7))) {
const nextOpcode = cpu.progMem[cpu.pc + 1];
const skipSize = isTwoWordInstruction(nextOpcode) ? 2 : 1;
@@ -662,7 +663,7 @@ export function avrInstruction(cpu: CPU) {
}
} else if (opcode === 0x9588) {
/* SLEEP, 1001 0101 1000 1000 */
- /* not implemented */
+ cpu.onSleep();
} else if (opcode === 0x95e8) {
/* SPM, 1001 0101 1110 1000 */
/* not implemented */
diff --git a/src/cpu/interrupt.ts b/src/cpu/interrupt.ts
index 73b56ee..df54ea3 100644
--- a/src/cpu/interrupt.ts
+++ b/src/cpu/interrupt.ts
@@ -13,10 +13,11 @@ import { CPU } from './cpu';
export function avrInterrupt(cpu: CPU, addr: number) {
const sp = cpu.dataView.getUint16(93, true);
- cpu.data[sp] = cpu.pc & 0xff;
- cpu.data[sp - 1] = (cpu.pc >> 8) & 0xff;
+ const dmo = cpu.dataMemoryOffset;
+ cpu.data[sp + dmo] = cpu.pc & 0xff;
+ cpu.data[sp - 1 + dmo] = (cpu.pc >> 8) & 0xff;
if (cpu.pc22Bits) {
- cpu.data[sp - 2] = (cpu.pc >> 16) & 0xff;
+ cpu.data[sp - 2 + dmo] = (cpu.pc >> 16) & 0xff;
}
cpu.dataView.setUint16(93, sp - (cpu.pc22Bits ? 3 : 2), true);
cpu.data[95] &= 0x7f; // clear global interrupt flag