From fb0b489d1734fc2eeff19705bdab8e6f193efc89 Mon Sep 17 00:00:00 2001 From: John Hardy Date: Mon, 9 Feb 2026 18:16:33 +1100 Subject: [PATCH] Stabilize SD SPI write response and runtime test --- src/platforms/tec1g/sd-spi.ts | 3 +- tests/platforms/tec1g/sd-spi-runtime.test.ts | 93 +++++++++++++++----- 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/src/platforms/tec1g/sd-spi.ts b/src/platforms/tec1g/sd-spi.ts index cb396b0..9196851 100644 --- a/src/platforms/tec1g/sd-spi.ts +++ b/src/platforms/tec1g/sd-spi.ts @@ -357,7 +357,8 @@ export class SdSpi { } } // Data response token: 0bxxx00101 = 0x05 (accepted). - this.enqueueResponse([0x05, 0xff]); + this.pendingResponse = [0x05, 0xff]; + this.delayBytes = 1; this.writeState = null; } } diff --git a/tests/platforms/tec1g/sd-spi-runtime.test.ts b/tests/platforms/tec1g/sd-spi-runtime.test.ts index 5d2a1a7..688640f 100644 --- a/tests/platforms/tec1g/sd-spi-runtime.test.ts +++ b/tests/platforms/tec1g/sd-spi-runtime.test.ts @@ -8,7 +8,12 @@ import type { Tec1gPlatformConfigNormalized } from '../../../src/platforms/types const MOSI_BIT = 0x01; const CLK_BIT = 0x02; -function makeRuntime(image?: Uint8Array) { +type Tec1gRuntimeHandle = { + rt: ReturnType; + sdImagePath?: string; +}; + +function makeRuntime(image?: Uint8Array): Tec1gRuntimeHandle { let sdImagePath: string | undefined; if (image) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'debug80-sd-')); @@ -34,31 +39,31 @@ function makeRuntime(image?: Uint8Array) { sdHighCapacity: true, ...(sdImagePath ? { sdImagePath } : {}), }; - return createTec1gRuntime(config, () => {}); + return { rt: createTec1gRuntime(config, () => {}), sdImagePath }; } -function writePort(rt: ReturnType, value: number): void { +function writePort(rt: ReturnType, value: number): void { rt.ioHandlers.write(0xfd, value & 0xff); } -function readPort(rt: ReturnType): number { +function readPort(rt: ReturnType): number { return rt.ioHandlers.read(0xfd) & 0xff; } -function pulse(rt: ReturnType, bit: number): void { +function pulse(rt: ReturnType, bit: number): void { const io = bit ? MOSI_BIT : 0; writePort(rt, io); writePort(rt, io | CLK_BIT); writePort(rt, io); } -function writeByte(rt: ReturnType, value: number): void { +function writeByte(rt: ReturnType, value: number): void { for (let i = 7; i >= 0; i -= 1) { pulse(rt, (value >> i) & 1); } } -function readByte(rt: ReturnType): number { +function readByte(rt: ReturnType): number { let value = 0; for (let i = 0; i < 8; i += 1) { writePort(rt, MOSI_BIT); @@ -70,7 +75,7 @@ function readByte(rt: ReturnType): number { return value & 0xff; } -function readResponse(rt: ReturnType): number { +function readResponse(rt: ReturnType): number { for (let i = 0; i < 8; i += 1) { const value = readByte(rt); if (value !== 0xff) { @@ -80,31 +85,75 @@ function readResponse(rt: ReturnType): number { return 0xff; } -function sendCommand(rt: ReturnType, bytes: number[]): void { +function sendCommand(rt: ReturnType, bytes: number[]): void { bytes.forEach((byte) => writeByte(rt, byte)); } +function initHighCapacity(rt: ReturnType): void { + writePort(rt, 0x00); + sendCommand(rt, [0x40, 0x00, 0x00, 0x00, 0x00, 0x95]); + expect(readResponse(rt)).toBe(0x01); + sendCommand(rt, [0x48, 0x00, 0x00, 0x01, 0xaa, 0x87]); + expect(readResponse(rt)).toBe(0x01); + expect(readByte(rt)).toBe(0x00); + expect(readByte(rt)).toBe(0x00); + expect(readByte(rt)).toBe(0x01); + expect(readByte(rt)).toBe(0xaa); + sendCommand(rt, [0x77, 0x00, 0x00, 0x00, 0x00, 0x65]); + expect(readResponse(rt)).toBe(0x01); + sendCommand(rt, [0x69, 0x40, 0x00, 0x00, 0x00, 0x77]); + expect(readResponse(rt)).toBe(0x01); + sendCommand(rt, [0x77, 0x00, 0x00, 0x00, 0x00, 0x65]); + expect(readResponse(rt)).toBe(0x01); + sendCommand(rt, [0x69, 0x40, 0x00, 0x00, 0x00, 0x77]); + expect(readResponse(rt)).toBe(0x00); + sendCommand(rt, [0x7a, 0x00, 0x00, 0x00, 0x00, 0xfd]); + expect(readResponse(rt)).toBe(0x00); + expect(readByte(rt)).toBe(0x40); + expect(readByte(rt)).toBe(0x00); + expect(readByte(rt)).toBe(0x00); + expect(readByte(rt)).toBe(0x00); +} + +function writeDataBlock(rt: ReturnType, payload: Uint8Array): void { + writeByte(rt, 0xfe); + for (let i = 0; i < payload.length; i += 1) { + writeByte(rt, payload[i] ?? 0x00); + } + writeByte(rt, 0xff); + writeByte(rt, 0xff); +} + describe('TEC-1G SD SPI runtime', () => { it('initializes and reads a block through port 0xFD', () => { const image = new Uint8Array(1024); image[0x0201] = 0xab; - const rt = makeRuntime(image); - writePort(rt, 0x00); - sendCommand(rt, [0x40, 0x00, 0x00, 0x00, 0x00, 0x95]); - expect(readResponse(rt)).toBe(0x01); - sendCommand(rt, [0x48, 0x00, 0x00, 0x01, 0xaa, 0x87]); - expect(readResponse(rt)).toBe(0x01); - sendCommand(rt, [0x77, 0x00, 0x00, 0x00, 0x00, 0x65]); - readResponse(rt); - sendCommand(rt, [0x69, 0x40, 0x00, 0x00, 0x00, 0x77]); - readResponse(rt); - sendCommand(rt, [0x7a, 0x00, 0x00, 0x00, 0x00, 0xfd]); - expect(readResponse(rt)).toBe(0x00); - expect(readByte(rt)).toBe(0x40); + const { rt } = makeRuntime(image); + initHighCapacity(rt); sendCommand(rt, [0x51, 0x00, 0x00, 0x00, 0x01, 0xff]); expect(readResponse(rt)).toBe(0x00); expect(readByte(rt)).toBe(0xfe); expect(readByte(rt)).toBe(0x00); expect(readByte(rt)).toBe(0xab); }); + + it('writes a block through port 0xFD', () => { + const image = new Uint8Array(1024); + const payload = new Uint8Array(512); + payload[0] = 0xde; + payload[1] = 0xad; + payload[2] = 0xbe; + const { rt } = makeRuntime(image); + initHighCapacity(rt); + sendCommand(rt, [0x58, 0x00, 0x00, 0x00, 0x01, 0xff]); + expect(readResponse(rt)).toBe(0x00); + writeDataBlock(rt, payload); + expect(readResponse(rt)).toBe(0x05); + sendCommand(rt, [0x51, 0x00, 0x00, 0x00, 0x01, 0xff]); + expect(readResponse(rt)).toBe(0x00); + expect(readByte(rt)).toBe(0xfe); + expect(readByte(rt)).toBe(0xde); + expect(readByte(rt)).toBe(0xad); + expect(readByte(rt)).toBe(0xbe); + }); });