| name | engine-parity-check |
| description | Auto-activates when modifying CPU instructions or engine code. Ensures JS and WASM engines produce identical behavior. |
Engine Parity Check Skill
This skill activates when working on CPU instruction implementations to ensure both JS and WASM engines behave identically.
When This Skill Applies
- Adding new 6502 instructions
- Modifying instruction behavior
- Fixing CPU emulation bugs
- Working on addressing modes
- Changing flag calculations
Parity Requirements
Both engines MUST produce identical:
- Register values (A, X, Y, SP, PC)
- Status flags (N, V, B, D, I, Z, C)
- Memory writes (same address, same value)
- Cycle counts (for timing accuracy)
Verification Process
Step 1: Identify Affected Instructions
When modifying code, list all affected opcodes:
Instruction: ADC (Add with Carry)
Opcodes: $69 (imm), $65 (zp), $75 (zpx), $6D (abs), $7D (abx), $79 (aby), $61 (izx), $71 (izy)
Step 2: Check Both Implementations
JavaScript (src/core/cpu6502/instructions.ts):
private ADC(value: number): void {
}
Rust (wasm-cpu/src/instructions_bus_impl.rs — the bus-aware path that runs in-app;
instructions.rs is the non-bus reference):
fn adc(&mut self, value: u8) {
}
Step 3: Create Test Cases
For each instruction, test:
describe('ADC instruction parity', () => {
it('should match JS and WASM for immediate mode', async () => {
jsEngine.setRegister('A', 0x50);
wasmEngine.setRegister('A', 0x50);
jsEngine.execute(0x69, 0x50);
wasmEngine.execute(0x69, 0x50);
expect(jsEngine.getState()).toEqual(wasmEngine.getState());
});
});
Step 4: Edge Cases to Test
Always verify these scenarios:
| Test Case | Description |
|---|
| Zero result | Result is exactly 0x00 |
| Negative result | Result has bit 7 set |
| Carry out | Result > 0xFF |
| Overflow | Signed overflow occurred |
| Page crossing | Address crosses page boundary |
| Wrap-around | Address wraps from $FFFF to $0000 |
Common Parity Issues
1. Flag Calculation Differences
let n_flag = (result & 0x80) != 0;
let z_flag = result == 0;
let z_flag = (result & 0xFF) == 0;
let n_flag = (result & 0x80) != 0;
2. Cycle Count Mismatches
cycles += if page_crossed { 1 } else { 0 };
self.cycles += if self.page_crossed() { 1 } else { 0 };
3. Memory Access Order
self.write(addr, low_byte);
self.write(addr + 1, high_byte);
Quick Parity Test
Run this to verify parity for common operations (vitest uses -t to filter by test name):
yarn test -t parity
The dedicated suite is src/core/cpu-engines/__tests__/engine-parity.vitest.test.ts. Note it
skips when the WASM module can't be fetched under the vitest runner; the headless benchmark's
file:// fetch shim is the working pattern to make it load. Or use the /engine-test command.
Debugging Parity Failures
- Enable tracing in both engines
- Step through instruction by instruction
- Compare state after each step
- Find first divergence point
- Fix root cause in the divergent engine
File References
| Component | JS Location | Rust Location (in-app = bus-aware) |
|---|
| CPU Core | src/core/cpu6502/core.ts | wasm-cpu/src/cpu.rs, system.rs |
| Instructions | src/core/cpu6502/instructions.ts | instructions_with_bus.rs + instructions_bus_impl.rs (ref: instructions.rs) |
| Opcodes | src/core/cpu6502/opcodes.ts | opcodes_with_bus.rs (ref: opcodes.rs) |
| Tests | src/core/__tests__/CPU6502-*.vitest.test.ts | wasm-cpu/tests/ |