diff --git a/demo/emulator-run.ts b/demo/emulator-run.ts index e18cc9f..b368ed4 100644 --- a/demo/emulator-run.ts +++ b/demo/emulator-run.ts @@ -14,9 +14,9 @@ loadHex(hex, mcu.flash, 0x10000000); const gdbServer = new GDBTCPServer(mcu, 3333); console.log(`RP2040 GDB Server ready! Listening on port ${gdbServer.port}`); -mcu.uart[0].onByte = (value) => { +mcu.uart[0].on('byteSent', (value) => { process.stdout.write(new Uint8Array([value])); -}; +}); mcu.core.PC = 0x10000000; mcu.execute(); diff --git a/package-lock.json b/package-lock.json index 5765ab1..3f5a6d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "rp2040js", "version": "0.17.15", "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + }, "devDependencies": { "@types/jest": "^27.4.1", "@types/minimist": "^1.2.2", @@ -2668,6 +2671,11 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -8058,6 +8066,11 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", diff --git a/package.json b/package.json index 0554328..979b39e 100644 --- a/package.json +++ b/package.json @@ -65,5 +65,8 @@ "**/*.js": [ "prettier --write" ] + }, + "dependencies": { + "eventemitter3": "^5.0.1" } } diff --git a/src/peripherals/peripheral.ts b/src/peripherals/peripheral.ts index fac3eb9..bf69707 100644 --- a/src/peripherals/peripheral.ts +++ b/src/peripherals/peripheral.ts @@ -1,3 +1,4 @@ +import EventEmitter from 'eventemitter3'; import { RP2040 } from '../rp2040'; const ATOMIC_NORMAL = 0; @@ -25,10 +26,15 @@ export interface Peripheral { writeUint32Atomic(offset: number, value: number, atomicType: number): void; } -export class BasePeripheral implements Peripheral { +// eslint-disable-next-line @typescript-eslint/ban-types -- EventEmitter3 defines the allowed types as `object`, so copy that here +export class BasePeripheral> + extends EventEmitter + implements Peripheral { protected rawWriteValue = 0; - constructor(protected rp2040: RP2040, readonly name: string) {} + constructor(protected rp2040: RP2040, readonly name: string) { + super(); + } readUint32(offset: number) { this.warn(`Unimplemented peripheral read from ${offset.toString(16)}`); diff --git a/src/peripherals/uart.ts b/src/peripherals/uart.ts index 11a4866..a692e58 100644 --- a/src/peripherals/uart.ts +++ b/src/peripherals/uart.ts @@ -27,13 +27,25 @@ const UARTEN = 1 << 0; // Interrupt bits const UARTRXINTR = 1 << 4; -export class RPUART extends BasePeripheral implements Peripheral { +type UartEvents = { + /** the MCU has written a byte to the UART */ + byteSent: (value: number) => void; + /** readFhe MCU's read FIFO is full, and any further bytes fed to the UART will be dropped */ + readFifoFull: () => void; + /** the MCU's read FIFO is empty */ + readFifoEmpty: () => void; + /** the MCU has read a byte from the UART */ + byteConsumed: () => void; +}; + +export class RPUART extends BasePeripheral implements Peripheral { private ctrlRegister = RXE | TXE; private lineCtrlRegister = 0; private rxFIFO = new FIFO(32); private interruptMask = 0; private interruptStatus = 0; + /** @deprecated prefer `on("byteSent", callback)` */ public onByte?: (value: number) => void; constructor(rp2040: RP2040, name: string, readonly irq: number) { @@ -95,6 +107,13 @@ export class RPUART extends BasePeripheral implements Peripheral { this.interruptStatus |= UARTRXINTR; this.checkInterrupts(); } + if (this.rxFIFO.empty) { + this.emit('readFifoEmpty'); + } + if (this.rxFIFO.full) { + this.emit('readFifoFull'); + } + this.emit('byteConsumed'); return value; } case UARTFR: @@ -115,9 +134,12 @@ export class RPUART extends BasePeripheral implements Peripheral { writeUint32(offset: number, value: number) { switch (offset) { - case UARTDR: - this.onByte?.(value & 0xff); + case UARTDR: { + const byte = value & 0xff; + this.onByte?.(byte); + this.emit('byteSent', byte); break; + } case UARTLCR_H: this.lineCtrlRegister = value;