aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUri Shaked2020-04-29 23:07:27 +0300
committerUri Shaked2020-04-29 23:07:27 +0300
commitad16db1e84ee22ce794e9dd4ee60dfaabca248e2 (patch)
treeab03195497a353d4cb2987638e3f0ffbbd6957de
parentchore: release 0.8.3 (diff)
downloadavr8js-ad16db1e84ee22ce794e9dd4ee60dfaabca248e2.tar.gz
avr8js-ad16db1e84ee22ce794e9dd4ee60dfaabca248e2.tar.bz2
avr8js-ad16db1e84ee22ce794e9dd4ee60dfaabca248e2.zip
fix(timer): Reading TCNT in 2-cycle instructions
close #40
-rw-r--r--src/cpu/instruction.ts24
-rw-r--r--src/peripherals/timer.spec.ts26
-rw-r--r--src/peripherals/timer.ts1
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;
}