diff options
| author | Uri Shaked | 2020-04-29 23:07:27 +0300 |
|---|---|---|
| committer | Uri Shaked | 2020-04-29 23:07:27 +0300 |
| commit | ad16db1e84ee22ce794e9dd4ee60dfaabca248e2 (patch) | |
| tree | ab03195497a353d4cb2987638e3f0ffbbd6957de /src | |
| parent | chore: release 0.8.3 (diff) | |
| download | avr8js-ad16db1e84ee22ce794e9dd4ee60dfaabca248e2.tar.gz avr8js-ad16db1e84ee22ce794e9dd4ee60dfaabca248e2.tar.bz2 avr8js-ad16db1e84ee22ce794e9dd4ee60dfaabca248e2.zip | |
fix(timer): Reading TCNT in 2-cycle instructions
close #40
Diffstat (limited to '')
| -rw-r--r-- | src/cpu/instruction.ts | 24 | ||||
| -rw-r--r-- | src/peripherals/timer.spec.ts | 26 | ||||
| -rw-r--r-- | src/peripherals/timer.ts | 1 |
3 files changed, 35 insertions, 16 deletions
diff --git a/src/cpu/instruction.ts b/src/cpu/instruction.ts index 223a320..51bc3ab 100644 --- a/src/cpu/instruction.ts +++ b/src/cpu/instruction.ts @@ -347,78 +347,78 @@ export function avrInstruction(cpu: ICPU) { cpu.data[((opcode & 0xf0) >> 4) + 16] = (opcode & 0xf) | ((opcode & 0xf00) >> 4); } else if ((opcode & 0xfe0f) === 0x9000) { /* LDS, 1001 000d dddd 0000 kkkk kkkk kkkk kkkk */ + cpu.cycles++; const value = cpu.readData(cpu.progMem[cpu.pc + 1]); cpu.data[(opcode & 0x1f0) >> 4] = value; cpu.pc++; - cpu.cycles++; } else if ((opcode & 0xfe0f) === 0x900c) { /* LDX, 1001 000d dddd 1100 */ - cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(26, true)); cpu.cycles++; + cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(26, true)); } else if ((opcode & 0xfe0f) === 0x900d) { /* LDX(INC), 1001 000d dddd 1101 */ const x = cpu.dataView.getUint16(26, true); + cpu.cycles++; cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(x); cpu.dataView.setUint16(26, x + 1, true); - cpu.cycles++; } else if ((opcode & 0xfe0f) === 0x900e) { /* LDX(DEC), 1001 000d dddd 1110 */ const x = cpu.dataView.getUint16(26, true) - 1; cpu.dataView.setUint16(26, x, true); - cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(x); cpu.cycles++; + cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(x); } else if ((opcode & 0xfe0f) === 0x8008) { /* LDY, 1000 000d dddd 1000 */ - cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(28, true)); cpu.cycles++; + cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(28, true)); } else if ((opcode & 0xfe0f) === 0x9009) { /* LDY(INC), 1001 000d dddd 1001 */ const y = cpu.dataView.getUint16(28, true); + cpu.cycles++; cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(y); cpu.dataView.setUint16(28, y + 1, true); - cpu.cycles++; } else if ((opcode & 0xfe0f) === 0x900a) { /* LDY(DEC), 1001 000d dddd 1010 */ const y = cpu.dataView.getUint16(28, true) - 1; cpu.dataView.setUint16(28, y, true); - cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(y); cpu.cycles++; + cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(y); } else if ( (opcode & 0xd208) === 0x8008 && (opcode & 7) | ((opcode & 0xc00) >> 7) | ((opcode & 0x2000) >> 8) ) { /* LDDY, 10q0 qq0d dddd 1qqq */ + cpu.cycles++; cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData( cpu.dataView.getUint16(28, true) + ((opcode & 7) | ((opcode & 0xc00) >> 7) | ((opcode & 0x2000) >> 8)) ); - cpu.cycles++; } else if ((opcode & 0xfe0f) === 0x8000) { /* LDZ, 1000 000d dddd 0000 */ - cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(30, true)); cpu.cycles++; + cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(30, true)); } else if ((opcode & 0xfe0f) === 0x9001) { /* LDZ(INC), 1001 000d dddd 0001 */ const z = cpu.dataView.getUint16(30, true); + cpu.cycles++; cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(z); cpu.dataView.setUint16(30, z + 1, true); - cpu.cycles++; } else if ((opcode & 0xfe0f) === 0x9002) { /* LDZ(DEC), 1001 000d dddd 0010 */ const z = cpu.dataView.getUint16(30, true) - 1; cpu.dataView.setUint16(30, z, true); - cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(z); cpu.cycles++; + cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(z); } else if ( (opcode & 0xd208) === 0x8000 && (opcode & 7) | ((opcode & 0xc00) >> 7) | ((opcode & 0x2000) >> 8) ) { /* LDDZ, 10q0 qq0d dddd 0qqq */ + cpu.cycles++; cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData( cpu.dataView.getUint16(30, true) + ((opcode & 7) | ((opcode & 0xc00) >> 7) | ((opcode & 0x2000) >> 8)) ); - cpu.cycles++; } else if (opcode === 0x95c8) { /* LPM, 1001 0101 1100 1000 */ cpu.data[0] = cpu.progBytes[cpu.dataView.getUint16(30, true)]; diff --git a/src/peripherals/timer.spec.ts b/src/peripherals/timer.spec.ts index 7d280ed..8733f73 100644 --- a/src/peripherals/timer.spec.ts +++ b/src/peripherals/timer.spec.ts @@ -95,7 +95,7 @@ describe('timer', () => { cpu.cycles = 1; timer.tick(); const tcnt = cpu.readData(0x46); - expect(tcnt).toEqual(0); // TCNT should be 0 + expect(tcnt).toEqual(2); // TCNT should be 2 (one tick above + 2 cycles for interrupt) expect(cpu.data[0x35]).toEqual(0); // TOV bit in TIFR should be clear expect(cpu.pc).toEqual(0x20); expect(cpu.cycles).toEqual(3); @@ -183,7 +183,7 @@ describe('timer', () => { cpu.cycles = 1; timer.tick(); const tcnt = cpu.readData(0x46); - expect(tcnt).toEqual(0x21); // TCNT should be 0x21 + expect(tcnt).toEqual(0x23); // TCNT should be 0x23 (one tick above + 2 cycles for interrupt) expect(cpu.data[0x35]).toEqual(0); // OCFA bit in TIFR should be clear expect(cpu.pc).toEqual(0x1c); expect(cpu.cycles).toEqual(3); @@ -216,7 +216,7 @@ describe('timer', () => { cpu.cycles = 1; timer.tick(); const tcnt = cpu.readData(0x46); - expect(tcnt).toEqual(0x21); // TCNT should be 0x21 + expect(tcnt).toEqual(0x23); // TCNT should be 0x23 (one tick above + 2 cycles for interrupt) expect(cpu.data[0x35]).toEqual(0); // OCFB bit in TIFR should be clear expect(cpu.pc).toEqual(0x1e); expect(cpu.cycles).toEqual(3); @@ -255,6 +255,24 @@ describe('timer', () => { expect(cpu.readData(0xb2)).toEqual(2); // TCNT2 should be 2 }); + it('should update TCNT as it is being read by a 2-cycle instruction (issue #40)', () => { + const program = [ + 'LDI r16, 0x1', // TCCR0B = 1 << CS00; + 'OUT 0x25, r16', + 'LDI r16, 0x0', // TCNT0 <- 0x30 + 'OUT 0x26, r16', + 'NOP', + 'LDS r1, 0x46', // r17 <- TCNT0 (2 cycles) + ]; + loadProgram(...program); + const timer = new AVRTimer(cpu, timer0Config); + for (let i = 0; i < program.length; i++) { + avrInstruction(cpu); + timer.tick(); + } + expect(cpu.data[1]).toEqual(2); // r1 should equal 2 + }); + describe('16 bit timers', () => { it('should increment 16-bit TCNT by 1', () => { const timer = new AVRTimer(cpu, timer1Config); @@ -301,7 +319,7 @@ describe('timer', () => { cpu.cycles = 1; timer.tick(); cpu.readData(0x84); // Refresh TCNT1 - expect(cpu.dataView.getUint16(0x84, true)).toEqual(0); // TCNT1 should be 0 + expect(cpu.dataView.getUint16(0x84, true)).toEqual(2); // TCNT1 should be 0 (one tick above + 2 cycles for interrupt) expect(cpu.data[0x36]).toEqual(0); // TOV bit in TIFR should be clear expect(cpu.pc).toEqual(0x1a); expect(cpu.cycles).toEqual(3); diff --git a/src/peripherals/timer.ts b/src/peripherals/timer.ts index 04c09aa..7cae2ac 100644 --- a/src/peripherals/timer.ts +++ b/src/peripherals/timer.ts @@ -197,6 +197,7 @@ export class AVRTimer { constructor(private cpu: CPU, private config: AVRTimerConfig) { this.updateWGMConfig(); this.cpu.readHooks[config.TCNT] = (addr: u8) => { + this.tick(); if (this.config.bits === 16) { this.cpu.data[addr + 1] = this.tcnt >> 8; } |
