Skip to content

Commit 5bc85ec

Browse files
committed
fix(gpio): CBI/SBI handling in writes to PIN register #103
fix #103
1 parent 60d024f commit 5bc85ec

File tree

4 files changed

+50
-8
lines changed

4 files changed

+50
-8
lines changed

src/cpu/cpu.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ export interface ICPU {
3434
cycles: number;
3535

3636
readData(addr: u16): u8;
37-
writeData(addr: u16, value: u8): void;
37+
writeData(addr: u16, value: u8, mask?: u8): void;
3838
}
3939

40-
export type CPUMemoryHook = (value: u8, oldValue: u8, addr: u16) => boolean | void;
40+
export type CPUMemoryHook = (value: u8, oldValue: u8, addr: u16, mask: u8) => boolean | void;
4141
export interface CPUMemoryHooks {
4242
[key: number]: CPUMemoryHook;
4343
}
@@ -102,10 +102,10 @@ export class CPU implements ICPU {
102102
return this.data[addr];
103103
}
104104

105-
writeData(addr: number, value: number) {
105+
writeData(addr: number, value: number, mask = 0xff) {
106106
const hook = this.writeHooks[addr];
107107
if (hook) {
108-
if (hook(value, this.data[addr], addr)) {
108+
if (hook(value, this.data[addr], addr, mask)) {
109109
return;
110110
}
111111
}

src/cpu/instruction.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ export function avrInstruction(cpu: ICPU) {
149149
const A = opcode & 0xf8;
150150
const b = opcode & 7;
151151
const R = cpu.readData((A >> 3) + 32);
152-
cpu.writeData((A >> 3) + 32, R & ~(1 << b));
152+
const mask = 1 << b;
153+
cpu.writeData((A >> 3) + 32, R & ~mask, mask);
153154
} else if ((opcode & 0xfe0f) === 0x9400) {
154155
/* COM, 1001 010d dddd 0000 */
155156
const d = (opcode & 0x1f0) >> 4;
@@ -603,7 +604,8 @@ export function avrInstruction(cpu: ICPU) {
603604
} else if ((opcode & 0xff00) === 0x9a00) {
604605
/* SBI, 1001 1010 AAAA Abbb */
605606
const target = ((opcode & 0xf8) >> 3) + 32;
606-
cpu.writeData(target, cpu.readData(target) | (1 << (opcode & 7)));
607+
const mask = 1 << (opcode & 7);
608+
cpu.writeData(target, cpu.readData(target) | mask, mask);
607609
cpu.cycles++;
608610
} else if ((opcode & 0xff00) === 0x9900) {
609611
/* SBIC, 1001 1001 AAAA Abbb */

src/peripherals/gpio.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { CPU } from '../cpu/cpu';
2+
import { asmProgram, TestProgramRunner } from '../utils/test-utils';
23
import { AVRIOPort, portBConfig, PinState, portDConfig, PinOverrideMode } from './gpio';
34

45
// CPU registers
@@ -8,6 +9,9 @@ const SREG = 95;
89
const PINB = 0x23;
910
const DDRB = 0x24;
1011
const PORTB = 0x25;
12+
const PIND = 0x29;
13+
const DDRD = 0x2a;
14+
const PORTD = 0x2b;
1115
const EIFR = 0x3c;
1216
const EIMSK = 0x3d;
1317
const PCICR = 0x68;
@@ -76,6 +80,42 @@ describe('GPIO', () => {
7680
expect(cpu.data[PINB]).toEqual(0x4); // PINB should return port value
7781
});
7882

83+
it('should only affect one pin when writing to PIN using SBI (issue #103)', () => {
84+
const { program } = asmProgram(`
85+
; register addresses
86+
_REPLACE DDRD, ${DDRD - 0x20}
87+
_REPLACE PIND, ${PIND - 0x20}
88+
_REPLACE PORTD, ${PORTD - 0x20}
89+
90+
; Setup
91+
ldi r24, 0x48
92+
out DDRD, r24
93+
out PORTD, r24
94+
95+
; Now toggle pin 6 with SBI
96+
sbi PIND, 6
97+
98+
break
99+
`);
100+
const cpu = new CPU(program);
101+
const portD = new AVRIOPort(cpu, portDConfig);
102+
const runner = new TestProgramRunner(cpu);
103+
104+
const listener = jest.fn();
105+
portD.addListener(listener);
106+
107+
// Setup: pins 6, 3 are output, set to HIGH
108+
runner.runInstructions(3);
109+
expect(listener).toHaveBeenCalledWith(0x48, 0x0);
110+
expect(cpu.data[PORTD]).toEqual(0x48);
111+
listener.mockReset();
112+
113+
// Now we toggle pin 6
114+
runner.runInstructions(1);
115+
expect(listener).toHaveBeenCalledWith(0x08, 0x48);
116+
expect(cpu.data[PORTD]).toEqual(0x8);
117+
});
118+
79119
it('should update the PIN register on output compare (OCR) match (issue #102)', () => {
80120
const cpu = new CPU(new Uint16Array(1024));
81121
const port = new AVRIOPort(cpu, portBConfig);

src/peripherals/gpio.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,11 @@ export class AVRIOPort {
229229
this.updatePinRegister(ddrMask);
230230
return true;
231231
};
232-
cpu.writeHooks[portConfig.PIN] = (value: u8) => {
232+
cpu.writeHooks[portConfig.PIN] = (value: u8, oldValue, addr, mask) => {
233233
// Writing to 1 PIN toggles PORT bits
234234
const oldPortValue = cpu.data[portConfig.PORT];
235235
const ddrMask = cpu.data[portConfig.DDR];
236-
const portValue = oldPortValue ^ value;
236+
const portValue = oldPortValue ^ (value & mask);
237237
cpu.data[portConfig.PORT] = portValue;
238238
this.writeGpio(portValue, ddrMask);
239239
this.updatePinRegister(ddrMask);

0 commit comments

Comments
 (0)