diff options
| author | Apexo | 2026-03-28 14:00:56 +0100 |
|---|---|---|
| committer | Apexo | 2026-03-28 14:00:56 +0100 |
| commit | 6336d478f7e046e45379c6996451b399e90b5bf1 (patch) | |
| tree | 04992f39d7e6c0fca1deb84c365d7e01666e2d69 /src | |
| parent | 0.21.0 (diff) | |
| download | avr8js-6336d478f7e046e45379c6996451b399e90b5bf1.tar.gz avr8js-6336d478f7e046e45379c6996451b399e90b5bf1.tar.bz2 avr8js-6336d478f7e046e45379c6996451b399e90b5bf1.zip | |
Diffstat (limited to '')
| -rw-r--r-- | src/cpu/cpu.ts | 30 | ||||
| -rw-r--r-- | src/cpu/instruction.ts | 53 | ||||
| -rw-r--r-- | src/cpu/interrupt.ts | 7 |
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 |
