Skip to content

Commit ad16db1

Browse files
committed
fix(timer): Reading TCNT in 2-cycle instructions
close #40
1 parent f37d350 commit ad16db1

File tree

3 files changed

+35
-16
lines changed

3 files changed

+35
-16
lines changed

src/cpu/instruction.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -347,78 +347,78 @@ export function avrInstruction(cpu: ICPU) {
347347
cpu.data[((opcode & 0xf0) >> 4) + 16] = (opcode & 0xf) | ((opcode & 0xf00) >> 4);
348348
} else if ((opcode & 0xfe0f) === 0x9000) {
349349
/* LDS, 1001 000d dddd 0000 kkkk kkkk kkkk kkkk */
350+
cpu.cycles++;
350351
const value = cpu.readData(cpu.progMem[cpu.pc + 1]);
351352
cpu.data[(opcode & 0x1f0) >> 4] = value;
352353
cpu.pc++;
353-
cpu.cycles++;
354354
} else if ((opcode & 0xfe0f) === 0x900c) {
355355
/* LDX, 1001 000d dddd 1100 */
356-
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(26, true));
357356
cpu.cycles++;
357+
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(26, true));
358358
} else if ((opcode & 0xfe0f) === 0x900d) {
359359
/* LDX(INC), 1001 000d dddd 1101 */
360360
const x = cpu.dataView.getUint16(26, true);
361+
cpu.cycles++;
361362
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(x);
362363
cpu.dataView.setUint16(26, x + 1, true);
363-
cpu.cycles++;
364364
} else if ((opcode & 0xfe0f) === 0x900e) {
365365
/* LDX(DEC), 1001 000d dddd 1110 */
366366
const x = cpu.dataView.getUint16(26, true) - 1;
367367
cpu.dataView.setUint16(26, x, true);
368-
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(x);
369368
cpu.cycles++;
369+
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(x);
370370
} else if ((opcode & 0xfe0f) === 0x8008) {
371371
/* LDY, 1000 000d dddd 1000 */
372-
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(28, true));
373372
cpu.cycles++;
373+
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(28, true));
374374
} else if ((opcode & 0xfe0f) === 0x9009) {
375375
/* LDY(INC), 1001 000d dddd 1001 */
376376
const y = cpu.dataView.getUint16(28, true);
377+
cpu.cycles++;
377378
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(y);
378379
cpu.dataView.setUint16(28, y + 1, true);
379-
cpu.cycles++;
380380
} else if ((opcode & 0xfe0f) === 0x900a) {
381381
/* LDY(DEC), 1001 000d dddd 1010 */
382382
const y = cpu.dataView.getUint16(28, true) - 1;
383383
cpu.dataView.setUint16(28, y, true);
384-
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(y);
385384
cpu.cycles++;
385+
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(y);
386386
} else if (
387387
(opcode & 0xd208) === 0x8008 &&
388388
(opcode & 7) | ((opcode & 0xc00) >> 7) | ((opcode & 0x2000) >> 8)
389389
) {
390390
/* LDDY, 10q0 qq0d dddd 1qqq */
391+
cpu.cycles++;
391392
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(
392393
cpu.dataView.getUint16(28, true) +
393394
((opcode & 7) | ((opcode & 0xc00) >> 7) | ((opcode & 0x2000) >> 8))
394395
);
395-
cpu.cycles++;
396396
} else if ((opcode & 0xfe0f) === 0x8000) {
397397
/* LDZ, 1000 000d dddd 0000 */
398-
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(30, true));
399398
cpu.cycles++;
399+
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(cpu.dataView.getUint16(30, true));
400400
} else if ((opcode & 0xfe0f) === 0x9001) {
401401
/* LDZ(INC), 1001 000d dddd 0001 */
402402
const z = cpu.dataView.getUint16(30, true);
403+
cpu.cycles++;
403404
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(z);
404405
cpu.dataView.setUint16(30, z + 1, true);
405-
cpu.cycles++;
406406
} else if ((opcode & 0xfe0f) === 0x9002) {
407407
/* LDZ(DEC), 1001 000d dddd 0010 */
408408
const z = cpu.dataView.getUint16(30, true) - 1;
409409
cpu.dataView.setUint16(30, z, true);
410-
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(z);
411410
cpu.cycles++;
411+
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(z);
412412
} else if (
413413
(opcode & 0xd208) === 0x8000 &&
414414
(opcode & 7) | ((opcode & 0xc00) >> 7) | ((opcode & 0x2000) >> 8)
415415
) {
416416
/* LDDZ, 10q0 qq0d dddd 0qqq */
417+
cpu.cycles++;
417418
cpu.data[(opcode & 0x1f0) >> 4] = cpu.readData(
418419
cpu.dataView.getUint16(30, true) +
419420
((opcode & 7) | ((opcode & 0xc00) >> 7) | ((opcode & 0x2000) >> 8))
420421
);
421-
cpu.cycles++;
422422
} else if (opcode === 0x95c8) {
423423
/* LPM, 1001 0101 1100 1000 */
424424
cpu.data[0] = cpu.progBytes[cpu.dataView.getUint16(30, true)];

src/peripherals/timer.spec.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ describe('timer', () => {
9595
cpu.cycles = 1;
9696
timer.tick();
9797
const tcnt = cpu.readData(0x46);
98-
expect(tcnt).toEqual(0); // TCNT should be 0
98+
expect(tcnt).toEqual(2); // TCNT should be 2 (one tick above + 2 cycles for interrupt)
9999
expect(cpu.data[0x35]).toEqual(0); // TOV bit in TIFR should be clear
100100
expect(cpu.pc).toEqual(0x20);
101101
expect(cpu.cycles).toEqual(3);
@@ -183,7 +183,7 @@ describe('timer', () => {
183183
cpu.cycles = 1;
184184
timer.tick();
185185
const tcnt = cpu.readData(0x46);
186-
expect(tcnt).toEqual(0x21); // TCNT should be 0x21
186+
expect(tcnt).toEqual(0x23); // TCNT should be 0x23 (one tick above + 2 cycles for interrupt)
187187
expect(cpu.data[0x35]).toEqual(0); // OCFA bit in TIFR should be clear
188188
expect(cpu.pc).toEqual(0x1c);
189189
expect(cpu.cycles).toEqual(3);
@@ -216,7 +216,7 @@ describe('timer', () => {
216216
cpu.cycles = 1;
217217
timer.tick();
218218
const tcnt = cpu.readData(0x46);
219-
expect(tcnt).toEqual(0x21); // TCNT should be 0x21
219+
expect(tcnt).toEqual(0x23); // TCNT should be 0x23 (one tick above + 2 cycles for interrupt)
220220
expect(cpu.data[0x35]).toEqual(0); // OCFB bit in TIFR should be clear
221221
expect(cpu.pc).toEqual(0x1e);
222222
expect(cpu.cycles).toEqual(3);
@@ -255,6 +255,24 @@ describe('timer', () => {
255255
expect(cpu.readData(0xb2)).toEqual(2); // TCNT2 should be 2
256256
});
257257

258+
it('should update TCNT as it is being read by a 2-cycle instruction (issue #40)', () => {
259+
const program = [
260+
'LDI r16, 0x1', // TCCR0B = 1 << CS00;
261+
'OUT 0x25, r16',
262+
'LDI r16, 0x0', // TCNT0 <- 0x30
263+
'OUT 0x26, r16',
264+
'NOP',
265+
'LDS r1, 0x46', // r17 <- TCNT0 (2 cycles)
266+
];
267+
loadProgram(...program);
268+
const timer = new AVRTimer(cpu, timer0Config);
269+
for (let i = 0; i < program.length; i++) {
270+
avrInstruction(cpu);
271+
timer.tick();
272+
}
273+
expect(cpu.data[1]).toEqual(2); // r1 should equal 2
274+
});
275+
258276
describe('16 bit timers', () => {
259277
it('should increment 16-bit TCNT by 1', () => {
260278
const timer = new AVRTimer(cpu, timer1Config);
@@ -301,7 +319,7 @@ describe('timer', () => {
301319
cpu.cycles = 1;
302320
timer.tick();
303321
cpu.readData(0x84); // Refresh TCNT1
304-
expect(cpu.dataView.getUint16(0x84, true)).toEqual(0); // TCNT1 should be 0
322+
expect(cpu.dataView.getUint16(0x84, true)).toEqual(2); // TCNT1 should be 0 (one tick above + 2 cycles for interrupt)
305323
expect(cpu.data[0x36]).toEqual(0); // TOV bit in TIFR should be clear
306324
expect(cpu.pc).toEqual(0x1a);
307325
expect(cpu.cycles).toEqual(3);

src/peripherals/timer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ export class AVRTimer {
197197
constructor(private cpu: CPU, private config: AVRTimerConfig) {
198198
this.updateWGMConfig();
199199
this.cpu.readHooks[config.TCNT] = (addr: u8) => {
200+
this.tick();
200201
if (this.config.bits === 16) {
201202
this.cpu.data[addr + 1] = this.tcnt >> 8;
202203
}

0 commit comments

Comments
 (0)