velxio/test/test_intel/test_buses/8259-pic.test.js

158 lines
4.9 KiB
JavaScript

/**
* Intel 8259 PIC — unit tests.
*
* Verifies the CPU-facing register interface (init word sequence, OCW1
* mask, EOI), the IRQ-to-INT pipeline, and the INTA acknowledge cycle.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { BoardHarness } from '../src/BoardHarness.js';
import { chipWasmExists } from '../src/helpers.js';
const CHIP = '8259-pic';
const skip = !chipWasmExists(CHIP);
function pinMap() {
const m = {
A0: 'A0', CS: 'CS', RD: 'RD', WR: 'WR',
INT: 'INT', INTA: 'INTA',
CAS0: 'CAS0', CAS1: 'CAS1', CAS2: 'CAS2', SPEN: 'SPEN',
VCC: 'VCC', GND: 'GND',
};
for (let i = 0; i < 8; i++) m[`D${i}`] = `D${i}`;
for (let i = 0; i < 8; i++) m[`IRQ${i}`] = `IRQ${i}`;
return m;
}
function setData(board, byte) {
for (let i = 0; i < 8; i++) board.setNet(`D${i}`, ((byte >> i) & 1) === 1);
}
function readData(board) {
let v = 0;
for (let i = 0; i < 8; i++) if (board.getNet(`D${i}`)) v |= (1 << i);
return v;
}
function picWrite(board, a0, value) {
board.setNet('A0', a0 !== 0);
setData(board, value);
board.advanceNanos(20);
board.setNet('CS', false);
board.setNet('WR', false);
board.advanceNanos(20);
board.setNet('WR', true); // rising-edge latch
board.advanceNanos(20);
board.setNet('CS', true);
}
function picRead(board, a0) {
board.setNet('A0', a0 !== 0);
board.setNet('CS', false);
board.setNet('RD', false);
board.advanceNanos(20);
const v = readData(board);
board.setNet('RD', true);
board.setNet('CS', true);
return v;
}
async function setup(board) {
await board.addChip(CHIP, pinMap());
board.setNet('CS', true);
board.setNet('RD', true);
board.setNet('WR', true);
board.setNet('INTA', true);
for (let i = 0; i < 8; i++) board.setNet(`IRQ${i}`, false);
// Init: ICW1 (single mode + ICW4 needed), ICW2 (vector base 0x40),
// ICW4 (8086 mode bit 0 = 1). After this, IRQ vectors are
// 0x40..0x47.
picWrite(board, 0, 0x13); // ICW1: single, edge-trig, ICW4-needed
picWrite(board, 1, 0x40); // ICW2: vector base = 0x40
picWrite(board, 1, 0x01); // ICW4: 8086 mode
picWrite(board, 1, 0x00); // OCW1: unmask all (IMR = 0)
}
describe(`${CHIP} chip`, () => {
let board;
beforeEach(() => { board = new BoardHarness(); });
afterEach(() => { board.dispose(); });
it.skipIf(skip)('registers all 28 logical pins', async () => {
await expect(board.addChip(CHIP, pinMap())).resolves.toBeDefined();
});
it.skipIf(skip)('IRQ0 rising drives INT high after init', async () => {
await setup(board);
expect(board.getNet('INT')).toBe(false);
board.setNet('IRQ0', true);
board.advanceNanos(20);
expect(board.getNet('INT')).toBe(true);
});
it.skipIf(skip)('INTA falling drives the IRQ0 vector (0x40) on D bus', async () => {
await setup(board);
board.setNet('IRQ0', true);
board.advanceNanos(20);
// CPU asserts INTA̅
board.setNet('INTA', false);
board.advanceNanos(20);
expect(readData(board)).toBe(0x40);
expect(board.getNet('INT')).toBe(false); // INT deasserted on ack
board.setNet('INTA', true);
});
it.skipIf(skip)('IRQ3 produces vector base+3 = 0x43', async () => {
await setup(board);
board.setNet('IRQ3', true);
board.advanceNanos(20);
board.setNet('INTA', false);
board.advanceNanos(20);
expect(readData(board)).toBe(0x43);
});
it.skipIf(skip)('IMR mask suppresses INT', async () => {
await setup(board);
// Mask IRQ0 by writing OCW1 with bit 0 set.
picWrite(board, 1, 0x01);
board.setNet('IRQ0', true);
board.advanceNanos(20);
expect(board.getNet('INT')).toBe(false);
});
it.skipIf(skip)('non-specific EOI clears the in-service bit', async () => {
await setup(board);
board.setNet('IRQ0', true);
board.advanceNanos(20);
// Acknowledge to lock ISR
board.setNet('INTA', false);
board.advanceNanos(20);
board.setNet('INTA', true);
board.advanceNanos(20);
// ISR bit 0 should be set; reading ISR via OCW3 + RD A0=0 confirms.
picWrite(board, 0, 0x0B); // OCW3: read ISR next
expect(picRead(board, 0)).toBe(0x01);
// Send non-specific EOI
picWrite(board, 0, 0x20);
expect(picRead(board, 0)).toBe(0x00);
});
it.skipIf(skip)('higher-priority IRQ pre-empts lower-priority pending', async () => {
await setup(board);
// IRQ7 lower priority than IRQ1.
board.setNet('IRQ7', true);
board.advanceNanos(20);
expect(board.getNet('INT')).toBe(true);
// ACK IRQ7
board.setNet('INTA', false);
board.advanceNanos(20);
expect(readData(board)).toBe(0x47);
board.setNet('INTA', true);
board.advanceNanos(20);
// Now IRQ1 fires while IRQ7 is in-service. Should pre-empt INT.
board.setNet('IRQ1', true);
board.advanceNanos(20);
expect(board.getNet('INT')).toBe(true);
board.setNet('INTA', false);
board.advanceNanos(20);
expect(readData(board)).toBe(0x41);
});
});