112 lines
3.5 KiB
JavaScript
112 lines
3.5 KiB
JavaScript
/**
|
|
* Intel 8251 USART — async-mode unit tests.
|
|
*
|
|
* Verifies CPU-side register interface (mode word + command word
|
|
* loading, status read, data write/read). Does NOT exercise the
|
|
* actual TxD/RxD bit timing — that's handled by the runtime's UART
|
|
* abstraction and proven by the test_custom_chips/uart-rot13 tests.
|
|
*/
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { BoardHarness } from '../src/BoardHarness.js';
|
|
import { chipWasmExists } from '../src/helpers.js';
|
|
|
|
const CHIP = '8251-usart';
|
|
const skip = !chipWasmExists(CHIP);
|
|
|
|
function pinMap() {
|
|
const m = {
|
|
RD: 'RD', WR: 'WR', CS: 'CS', CD: 'CD', RESET: 'RESET', CLK: 'CLK',
|
|
TXD: 'TXD', RXD: 'RXD',
|
|
TXRDY: 'TXRDY', RXRDY: 'RXRDY', TXEMPTY: 'TXEMPTY',
|
|
DSR: 'DSR', DTR: 'DTR', CTS: 'CTS', RTS: 'RTS',
|
|
VCC: 'VCC', GND: 'GND',
|
|
};
|
|
for (let i = 0; i < 8; i++) m[`D${i}`] = `D${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 uartWrite(board, cd, value) {
|
|
board.setNet('CD', cd);
|
|
setData(board, value);
|
|
board.advanceNanos(20);
|
|
board.setNet('CS', false);
|
|
board.setNet('WR', false);
|
|
board.advanceNanos(20);
|
|
board.setNet('WR', true);
|
|
board.advanceNanos(20);
|
|
board.setNet('CS', true);
|
|
}
|
|
function uartRead(board, cd) {
|
|
board.setNet('CD', cd);
|
|
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('RESET', true);
|
|
board.advanceNanos(50);
|
|
board.setNet('RESET', false);
|
|
board.advanceNanos(50);
|
|
}
|
|
|
|
describe(`${CHIP} chip`, () => {
|
|
let board;
|
|
beforeEach(() => { board = new BoardHarness(); });
|
|
afterEach(() => { board.dispose(); });
|
|
|
|
it.skipIf(skip)('registers all logical pins', async () => {
|
|
await expect(board.addChip(CHIP, pinMap())).resolves.toBeDefined();
|
|
});
|
|
|
|
it.skipIf(skip)('after RESET status reads as TxEMPTY without TxRDY', async () => {
|
|
await setup(board);
|
|
const status = uartRead(board, true);
|
|
// bit 0 (TxRDY) = 0 (not enabled yet); bit 2 (TxEMPTY) = 1.
|
|
expect(status & 0x01).toBe(0);
|
|
expect(status & 0x04).toBe(0x04);
|
|
});
|
|
|
|
it.skipIf(skip)('mode + command init sequence enables Tx', async () => {
|
|
await setup(board);
|
|
// Mode word: 0x4E = 8N1, baud rate factor x16 (typical setup).
|
|
uartWrite(board, true, 0x4E);
|
|
// Command word: 0x05 = TxEnable + RxEnable.
|
|
uartWrite(board, true, 0x05);
|
|
const status = uartRead(board, true);
|
|
expect(status & 0x01, 'TxRDY set after Tx-enable').toBe(0x01);
|
|
});
|
|
|
|
it.skipIf(skip)('command write 0x40 internal-reset returns to expecting mode word', async () => {
|
|
await setup(board);
|
|
uartWrite(board, true, 0x4E); // mode
|
|
uartWrite(board, true, 0x05); // command — Tx + Rx enable
|
|
uartWrite(board, true, 0x40); // internal reset
|
|
|
|
// Now the next write to control should be interpreted as a NEW mode
|
|
// word (0x4E) rather than a command. After mode + new command, Tx
|
|
// should re-enable.
|
|
uartWrite(board, true, 0x4E);
|
|
uartWrite(board, true, 0x05);
|
|
const status = uartRead(board, true);
|
|
expect(status & 0x01).toBe(0x01);
|
|
});
|
|
});
|