@typecad/hal 1.0.0-alpha.3 → 1.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/emit.d.ts CHANGED
@@ -53,6 +53,15 @@ export declare function i2cRequestFrom(bus: string, address: number, quantity: n
53
53
  export declare function i2cAvailable(bus: string): number;
54
54
  /** Read a byte from I2C. */
55
55
  export declare function i2cRead(bus: string): number;
56
+ /**
57
+ * Drain `count` bytes requested from the I2C bus into a caller-provided buffer.
58
+ * Semantic primitive: lowers to the `i2c.read_buffer` HAL op. The `buffer`
59
+ * argument is emitted as a placeholder (`__HAL_READ_BUF__`) that the var-init
60
+ * transformer rewrites to the caller's own buffer variable, so bytes land in
61
+ * the `uint8_t data[N]` declared in user scope — NOT an internal temp that
62
+ * decays to a pointer on return. Keeps `data.length` / `data[i]` valid.
63
+ */
64
+ export declare function i2cReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void;
56
65
  /** Initialize SPI bus. */
57
66
  export declare function spiBegin(bus: string): void;
58
67
  /** Disable SPI bus. */
@@ -67,6 +76,14 @@ export declare function spiEndTx(bus: string): void;
67
76
  export declare function spiCsLow(pin: number | string): void;
68
77
  /** Set SPI chip-select pin HIGH. */
69
78
  export declare function spiCsHigh(pin: number | string): void;
79
+ /**
80
+ * Read `count` bytes from the SPI bus into a caller-provided buffer by clocking
81
+ * dummy (0x00) transfers. Semantic primitive: lowers to the `spi.read_buffer`
82
+ * HAL op (per-byte `bus.transfer(0)` read loop). The `buffer` placeholder is
83
+ * rewritten to the caller's variable. Mirrors i2cReadBuffer. The caller is
84
+ * responsible for asserting/de-asserting chip-select around it.
85
+ */
86
+ export declare function spiReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void;
70
87
  /** Set SPI data mode. */
71
88
  export declare function spiSetMode(bus: string, mode: number): void;
72
89
  /** Set SPI bit order. */
package/dist/emit.js CHANGED
@@ -85,6 +85,15 @@ export function i2cRequestFrom(bus, address, quantity, stop) { return 0; }
85
85
  export function i2cAvailable(bus) { return 0; }
86
86
  /** Read a byte from I2C. */
87
87
  export function i2cRead(bus) { return 0; }
88
+ /**
89
+ * Drain `count` bytes requested from the I2C bus into a caller-provided buffer.
90
+ * Semantic primitive: lowers to the `i2c.read_buffer` HAL op. The `buffer`
91
+ * argument is emitted as a placeholder (`__HAL_READ_BUF__`) that the var-init
92
+ * transformer rewrites to the caller's own buffer variable, so bytes land in
93
+ * the `uint8_t data[N]` declared in user scope — NOT an internal temp that
94
+ * decays to a pointer on return. Keeps `data.length` / `data[i]` valid.
95
+ */
96
+ export function i2cReadBuffer(bus, count, buffer) { }
88
97
  // ---------------------------------------------------------------------------
89
98
  // SPI — serial peripheral interface
90
99
  // ---------------------------------------------------------------------------
@@ -102,6 +111,14 @@ export function spiEndTx(bus) { }
102
111
  export function spiCsLow(pin) { }
103
112
  /** Set SPI chip-select pin HIGH. */
104
113
  export function spiCsHigh(pin) { }
114
+ /**
115
+ * Read `count` bytes from the SPI bus into a caller-provided buffer by clocking
116
+ * dummy (0x00) transfers. Semantic primitive: lowers to the `spi.read_buffer`
117
+ * HAL op (per-byte `bus.transfer(0)` read loop). The `buffer` placeholder is
118
+ * rewritten to the caller's variable. Mirrors i2cReadBuffer. The caller is
119
+ * responsible for asserting/de-asserting chip-select around it.
120
+ */
121
+ export function spiReadBuffer(bus, count, buffer) { }
105
122
  /** Set SPI data mode. */
106
123
  export function spiSetMode(bus, mode) { }
107
124
  /** Set SPI bit order. */
package/dist/i2c.d.ts CHANGED
@@ -2,6 +2,13 @@ export declare class I2CDevice {
2
2
  private _bus;
3
3
  private _address;
4
4
  constructor(bus: string, address: number);
5
+ /** The 7-bit I2C address this accessor targets. Exposed so I2CDevice
6
+ * structurally satisfies the @typecad/simulator II2CDeviceAccessor contract
7
+ * (which declares `readonly address`), letting the same driver function be
8
+ * typed against the contract and accept either a real board device or a
9
+ * simulated one. The transpiler strips HAL class bodies to IR, so this
10
+ * getter carries no runtime cost in the generated C++. */
11
+ get address(): number;
5
12
  writeByte(register: number, value: number): void;
6
13
  readByte(register: number): number;
7
14
  writeBytes(register: number, data: number[] | Uint8Array): void;
package/dist/i2c.js CHANGED
@@ -1,10 +1,19 @@
1
- import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, rawCpp } from './emit.js';
1
+ import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, i2cReadBuffer, rawCpp } from './emit.js';
2
2
  import { include } from './include.js';
3
3
  export class I2CDevice {
4
4
  constructor(bus, address) {
5
5
  this._bus = bus;
6
6
  this._address = address;
7
7
  }
8
+ /** The 7-bit I2C address this accessor targets. Exposed so I2CDevice
9
+ * structurally satisfies the @typecad/simulator II2CDeviceAccessor contract
10
+ * (which declares `readonly address`), letting the same driver function be
11
+ * typed against the contract and accept either a real board device or a
12
+ * simulated one. The transpiler strips HAL class bodies to IR, so this
13
+ * getter carries no runtime cost in the generated C++. */
14
+ get address() {
15
+ return this._address;
16
+ }
8
17
  writeByte(register, value) {
9
18
  include("<Wire.h>");
10
19
  i2cBeginTx(this._bus, this._address);
@@ -33,9 +42,11 @@ export class I2CDevice {
33
42
  i2cWrite(this._bus, register);
34
43
  i2cEndTx(this._bus, false);
35
44
  i2cRequestFrom(this._bus, this._address, count, true);
36
- rawCpp(`static uint8_t __buf[${count}];`);
37
- rawCpp(`for (int __i = 0; __i < ${count}; __i++) __buf[__i] = ${this._bus}.read();`);
38
- rawCpp(`return __buf;`);
45
+ // Drain the requested bytes into the caller's buffer (declared by the
46
+ // Uint8Array return marker as `uint8_t data[count]`). Using the semantic
47
+ // primitive — NOT rawCpp — keeps the buffer in user scope so it survives
48
+ // the return (no decayed pointer) and `data.length` / `data[i]` work.
49
+ i2cReadBuffer(this._bus, count, new Uint8Array(count));
39
50
  return new Uint8Array(count);
40
51
  }
41
52
  }
@@ -4,8 +4,16 @@ export type Bit = 0 | 1;
4
4
  * the bit width for documentation only; the value is the raw field contents. */
5
5
  export type Bits<N extends number = number> = number;
6
6
  /** Class decorator marking a struct as a memory-mapped register at `address`.
7
- * Erased at transpile time — the class becomes a `volatile uint32_t*`. */
8
- export declare function register(address: number): ClassDecorator;
7
+ * Erased at transpile time — the class becomes a `volatile uint32_t*`.
8
+ *
9
+ * These carry real (inert) runtime bodies rather than `declare`, so the
10
+ * `export { register, bits }` re-export in index.ts resolves under Node's ESM
11
+ * loader, which validates that re-exported bindings exist at runtime. They are
12
+ * never invoked: the cuttlefish transpiler detects them by name and lowers the
13
+ * decorated struct away, so these stubs are only reached when the decorator
14
+ * source is imported without transpilation (e.g. host-side tests). */
15
+ export declare function register(_address: number): ClassDecorator;
9
16
  /** Property decorator carrying the bit range [lo, hi] (inclusive) of a field
10
- * within its register. Erased at transpile time. */
11
- export declare function bits(hi: number, lo: number): PropertyDecorator;
17
+ * within its register. Erased at transpile time. See `register` for why these
18
+ * have runtime bodies. */
19
+ export declare function bits(_hi: number, _lo: number): PropertyDecorator;
package/dist/register.js CHANGED
@@ -18,4 +18,21 @@
18
18
  // USART1.UE = 1; // (*USART1 & ~1UL) | ((1 & 1UL) << 0)
19
19
  // const parity = USART1.PS; // ((*USART1 >> 8) & ((1UL << 2) - 1))
20
20
  // ---------------------------------------------------------------------------
21
- export {};
21
+ /** Class decorator marking a struct as a memory-mapped register at `address`.
22
+ * Erased at transpile time — the class becomes a `volatile uint32_t*`.
23
+ *
24
+ * These carry real (inert) runtime bodies rather than `declare`, so the
25
+ * `export { register, bits }` re-export in index.ts resolves under Node's ESM
26
+ * loader, which validates that re-exported bindings exist at runtime. They are
27
+ * never invoked: the cuttlefish transpiler detects them by name and lowers the
28
+ * decorated struct away, so these stubs are only reached when the decorator
29
+ * source is imported without transpilation (e.g. host-side tests). */
30
+ export function register(_address) {
31
+ return () => { };
32
+ }
33
+ /** Property decorator carrying the bit range [lo, hi] (inclusive) of a field
34
+ * within its register. Erased at transpile time. See `register` for why these
35
+ * have runtime bodies. */
36
+ export function bits(_hi, _lo) {
37
+ return () => { };
38
+ }
package/dist/spi.js CHANGED
@@ -1,4 +1,4 @@
1
- import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, rawCpp } from './emit.js';
1
+ import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, spiReadBuffer, rawCpp } from './emit.js';
2
2
  import { include } from './include.js';
3
3
  export class SPIDevice {
4
4
  constructor(bus, chipSelect) {
@@ -21,12 +21,15 @@ export class SPIDevice {
21
21
  }
22
22
  readRegister(register, count) {
23
23
  include("<SPI.h>");
24
- rawCpp(`digitalWrite(${this._cs}, LOW);`);
25
- rawCpp(`${this._bus}.transfer(${register});`);
26
- rawCpp(`static uint8_t __spi_buf[${count}];`);
27
- rawCpp(`for (int i=0; i<${count}; i++) __spi_buf[i] = ${this._bus}.transfer(0x00);`);
28
- rawCpp(`digitalWrite(${this._cs}, HIGH);`);
29
- rawCpp(`return __spi_buf;`);
24
+ spiCsLow(this._cs);
25
+ spiTransfer(this._bus, register);
26
+ // Clock `count` dummy bytes and drain them into the caller's buffer
27
+ // (declared by the Uint8Array return marker as `uint8_t data[count]`).
28
+ // Using the semantic primitive — NOT rawCpp — keeps the buffer in user
29
+ // scope so it survives the return (no decayed pointer) and `data.length`
30
+ // / `data[i]` work, mirroring I2CDevice.readBytes.
31
+ spiReadBuffer(this._bus, count, new Uint8Array(count));
32
+ spiCsHigh(this._cs);
30
33
  return new Uint8Array(count);
31
34
  }
32
35
  writeRegister(register, value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typecad/hal",
3
- "version": "1.0.0-alpha.3",
3
+ "version": "1.0.0-alpha.6",
4
4
  "description": "TypeCAD hardware abstraction layer — GPIO, I2C, SPI, UART as regular TypeScript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -49,9 +49,9 @@
49
49
  },
50
50
  "author": "typecad0",
51
51
  "devDependencies": {
52
- "@typecad/expect": "1.0.0-alpha.3",
53
- "@typecad/board-arduino-uno": "1.0.0-alpha.3",
54
- "@typecad/mcu-atmega328p": "1.0.0-alpha.3"
52
+ "@typecad/expect": "1.0.0-alpha.6",
53
+ "@typecad/board-arduino-uno": "1.0.0-alpha.6",
54
+ "@typecad/mcu-atmega328p": "1.0.0-alpha.6"
55
55
  },
56
56
  "sideEffects": false,
57
57
  "exports": {
package/src/emit.ts CHANGED
@@ -99,6 +99,15 @@ export function i2cRequestFrom(bus: string, address: number, quantity: number, s
99
99
  export function i2cAvailable(bus: string): number { return 0; }
100
100
  /** Read a byte from I2C. */
101
101
  export function i2cRead(bus: string): number { return 0; }
102
+ /**
103
+ * Drain `count` bytes requested from the I2C bus into a caller-provided buffer.
104
+ * Semantic primitive: lowers to the `i2c.read_buffer` HAL op. The `buffer`
105
+ * argument is emitted as a placeholder (`__HAL_READ_BUF__`) that the var-init
106
+ * transformer rewrites to the caller's own buffer variable, so bytes land in
107
+ * the `uint8_t data[N]` declared in user scope — NOT an internal temp that
108
+ * decays to a pointer on return. Keeps `data.length` / `data[i]` valid.
109
+ */
110
+ export function i2cReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void {}
102
111
 
103
112
  // ---------------------------------------------------------------------------
104
113
  // SPI — serial peripheral interface
@@ -118,6 +127,14 @@ export function spiEndTx(bus: string): void {}
118
127
  export function spiCsLow(pin: number | string): void {}
119
128
  /** Set SPI chip-select pin HIGH. */
120
129
  export function spiCsHigh(pin: number | string): void {}
130
+ /**
131
+ * Read `count` bytes from the SPI bus into a caller-provided buffer by clocking
132
+ * dummy (0x00) transfers. Semantic primitive: lowers to the `spi.read_buffer`
133
+ * HAL op (per-byte `bus.transfer(0)` read loop). The `buffer` placeholder is
134
+ * rewritten to the caller's variable. Mirrors i2cReadBuffer. The caller is
135
+ * responsible for asserting/de-asserting chip-select around it.
136
+ */
137
+ export function spiReadBuffer(bus: string, count: number, buffer: number[] | Uint8Array): void {}
121
138
  /** Set SPI data mode. */
122
139
  export function spiSetMode(bus: string, mode: number): void {}
123
140
  /** Set SPI bit order. */
package/src/i2c.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, rawCpp } from './emit.js';
1
+ import { i2cBegin, i2cEnd, i2cSetClock, i2cBeginTx, i2cWrite, i2cWriteBuffer, i2cEndTx, i2cRequestFrom, i2cAvailable, i2cRead, i2cReadBuffer, rawCpp } from './emit.js';
2
2
  import { include } from './include.js';
3
3
 
4
4
  export class I2CDevice {
@@ -10,6 +10,16 @@ export class I2CDevice {
10
10
  this._address = address;
11
11
  }
12
12
 
13
+ /** The 7-bit I2C address this accessor targets. Exposed so I2CDevice
14
+ * structurally satisfies the @typecad/simulator II2CDeviceAccessor contract
15
+ * (which declares `readonly address`), letting the same driver function be
16
+ * typed against the contract and accept either a real board device or a
17
+ * simulated one. The transpiler strips HAL class bodies to IR, so this
18
+ * getter carries no runtime cost in the generated C++. */
19
+ get address(): number {
20
+ return this._address;
21
+ }
22
+
13
23
  writeByte(register: number, value: number): void {
14
24
  include("<Wire.h>");
15
25
  i2cBeginTx(this._bus, this._address);
@@ -41,9 +51,11 @@ export class I2CDevice {
41
51
  i2cWrite(this._bus, register);
42
52
  i2cEndTx(this._bus, false);
43
53
  i2cRequestFrom(this._bus, this._address, count, true);
44
- rawCpp(`static uint8_t __buf[${count}];`);
45
- rawCpp(`for (int __i = 0; __i < ${count}; __i++) __buf[__i] = ${this._bus}.read();`);
46
- rawCpp(`return __buf;`);
54
+ // Drain the requested bytes into the caller's buffer (declared by the
55
+ // Uint8Array return marker as `uint8_t data[count]`). Using the semantic
56
+ // primitive — NOT rawCpp — keeps the buffer in user scope so it survives
57
+ // the return (no decayed pointer) and `data.length` / `data[i]` work.
58
+ i2cReadBuffer(this._bus, count, new Uint8Array(count));
47
59
  return new Uint8Array(count);
48
60
  }
49
61
  }
package/src/register.ts CHANGED
@@ -27,9 +27,21 @@ export type Bit = 0 | 1;
27
27
  export type Bits<N extends number = number> = number;
28
28
 
29
29
  /** Class decorator marking a struct as a memory-mapped register at `address`.
30
- * Erased at transpile time — the class becomes a `volatile uint32_t*`. */
31
- export declare function register(address: number): ClassDecorator;
30
+ * Erased at transpile time — the class becomes a `volatile uint32_t*`.
31
+ *
32
+ * These carry real (inert) runtime bodies rather than `declare`, so the
33
+ * `export { register, bits }` re-export in index.ts resolves under Node's ESM
34
+ * loader, which validates that re-exported bindings exist at runtime. They are
35
+ * never invoked: the cuttlefish transpiler detects them by name and lowers the
36
+ * decorated struct away, so these stubs are only reached when the decorator
37
+ * source is imported without transpilation (e.g. host-side tests). */
38
+ export function register(_address: number): ClassDecorator {
39
+ return () => {};
40
+ }
32
41
 
33
42
  /** Property decorator carrying the bit range [lo, hi] (inclusive) of a field
34
- * within its register. Erased at transpile time. */
35
- export declare function bits(hi: number, lo: number): PropertyDecorator;
43
+ * within its register. Erased at transpile time. See `register` for why these
44
+ * have runtime bodies. */
45
+ export function bits(_hi: number, _lo: number): PropertyDecorator {
46
+ return () => {};
47
+ }
package/src/spi.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, rawCpp } from './emit.js';
1
+ import { spiBegin, spiEnd, spiTransfer, spiBeginTx, spiEndTx, spiCsLow, spiCsHigh, spiSetMode, spiSetBitOrder, spiReadBuffer, rawCpp } from './emit.js';
2
2
  import { include } from './include.js';
3
3
  import type { Pin } from './gpio.js';
4
4
  import type { SPIMode, SPISettings } from './types.js';
@@ -30,12 +30,15 @@ export class SPIDevice {
30
30
 
31
31
  readRegister(register: number, count: number): Uint8Array {
32
32
  include("<SPI.h>");
33
- rawCpp(`digitalWrite(${this._cs}, LOW);`);
34
- rawCpp(`${this._bus}.transfer(${register});`);
35
- rawCpp(`static uint8_t __spi_buf[${count}];`);
36
- rawCpp(`for (int i=0; i<${count}; i++) __spi_buf[i] = ${this._bus}.transfer(0x00);`);
37
- rawCpp(`digitalWrite(${this._cs}, HIGH);`);
38
- rawCpp(`return __spi_buf;`);
33
+ spiCsLow(this._cs);
34
+ spiTransfer(this._bus, register);
35
+ // Clock `count` dummy bytes and drain them into the caller's buffer
36
+ // (declared by the Uint8Array return marker as `uint8_t data[count]`).
37
+ // Using the semantic primitive — NOT rawCpp — keeps the buffer in user
38
+ // scope so it survives the return (no decayed pointer) and `data.length`
39
+ // / `data[i]` work, mirroring I2CDevice.readBytes.
40
+ spiReadBuffer(this._bus, count, new Uint8Array(count));
41
+ spiCsHigh(this._cs);
39
42
  return new Uint8Array(count);
40
43
  }
41
44