aboutsummaryrefslogtreecommitdiff
path: root/src/cpu
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
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.