Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/tec1g-emulation-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ Disco LEDs (Fullisik under mechanical keys) — **N/A**
| Matrix keyboard | Med | **Complete** | 100% |
| CONFIG DIP switch | Low | **Complete** | 100% |
| RTC (DS1302) | Low | **Complete** | 100% |
| SD card SPI | Low | **Missing** | 0% |
| SD card SPI | Low | **Complete** | 95% |
| Cartridge | Low | **Partial** | 40% |
| Joystick | Low | **Missing** | 0% |
| Status LED bar | Low | **Missing** | 0% |
Expand Down Expand Up @@ -767,7 +767,6 @@ External add-on emulation. Fully independent of other stages.
- [x] Add `sdEnabled: boolean` and optional `sdImagePath: string` config options
- [x] Instantiate `SdSpi` in runtime when enabled
- [x] Route port 0xFD writes to `sdSpi.write(value)`, reads to `sdSpi.read()`
- [ ] If `sdImagePath` provided, load file as virtual disk image for block reads
- [x] If `sdImagePath` provided, load file as virtual disk image for block reads
- [x] Add SD presence indicator to SYS_INPUT (not applicable — no dedicated bit in schematic)
- [x] Unit tests: end-to-end port 0xFD initialization sequence
Expand Down
4 changes: 2 additions & 2 deletions src/platforms/tec1g/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ The TEC-1G panel can switch speed modes; the serial timing assumes FAST mode.
- Bit 4: FF-D4 (reserved — not yet decoded).
- Bit 5: CAPS (caps lock — not yet decoded).
- Bits 6-7: FF-D5/FF-D6 (reserved — not yet decoded).
- `OUT 0xFC`: RTC DS1302 (stub — not yet emulated, reads return 0xFF).
- `OUT 0xFD`: SD card SPI (stub — not yet emulated, reads return 0xFF).
- `OUT 0xFC`: RTC DS1302 (bit-banged emulation).
- `OUT 0xFD`: SD card SPI (bit-banged emulation, SPI mode).

## Serial (bitbang)
- TX uses bit 6 on `OUT 0x01`; RX uses bit 7 on `IN 0x00` (mirrored on `IN 0x03`).
Expand Down
9 changes: 7 additions & 2 deletions src/platforms/tec1g/sd-spi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type SdCommand = {
export type SdSpiOptions = {
csMask?: number;
csActiveLow?: boolean;
highCapacity?: boolean;
image?: Uint8Array;
};

Expand All @@ -24,6 +25,7 @@ const DEFAULT_CS_MASK = 0x04;
export class SdSpi {
private csMask: number;
private csActiveLow: boolean;
private highCapacity: boolean;
private csActive = false;
private clk = false;
private inShift = 0;
Expand All @@ -47,6 +49,7 @@ export class SdSpi {
public constructor(options: SdSpiOptions = {}) {
this.csMask = options.csMask ?? DEFAULT_CS_MASK;
this.csActiveLow = options.csActiveLow !== false;
this.highCapacity = options.highCapacity !== false;
this.image = options.image ?? null;
}

Expand Down Expand Up @@ -229,7 +232,8 @@ export class SdSpi {
break;
}
case 58: {
this.pendingResponse = [this.ready ? 0x00 : 0x01, 0x40, 0x00, 0x00, 0x00];
const ocr = this.highCapacity ? 0x40 : 0x00;
this.pendingResponse = [this.ready ? 0x00 : 0x01, ocr, 0x00, 0x00, 0x00];
this.delayBytes = 1;
break;
}
Expand All @@ -253,7 +257,8 @@ export class SdSpi {
}

private readBlock(arg: number): number[] {
const start = arg >>> 0;
// SDHC uses block (LBA) addressing; SDSC uses byte addressing.
const start = this.highCapacity ? ((arg >>> 0) << 9) >>> 0 : (arg >>> 0);
const payload = new Array<number>(512).fill(0x00);
if (!this.image || this.image.length === 0) {
return payload;
Expand Down
21 changes: 21 additions & 0 deletions tests/platforms/tec1g/sd-spi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,25 @@ describe('SdSpi', () => {
expect(readByte(spi)).toBe(0x00);
expect(readByte(spi)).toBe(0x5a);
});

it('treats CMD17 argument as block address for high capacity cards', () => {
const image = new Uint8Array(2048);
image[0x0202] = 0xa5;
const spi = new SdSpi({ csMask: CS_BIT, image, highCapacity: true });
writeSpi(spi, 0x00);
sendCommand(spi, [0x77, 0x00, 0x00, 0x00, 0x00, 0x65]);
readResponseByte(spi);
sendCommand(spi, [0x69, 0x40, 0x00, 0x00, 0x00, 0x77]);
readResponseByte(spi);
sendCommand(spi, [0x77, 0x00, 0x00, 0x00, 0x00, 0x65]);
readResponseByte(spi);
sendCommand(spi, [0x69, 0x40, 0x00, 0x00, 0x00, 0x77]);
readResponseByte(spi);
sendCommand(spi, [0x51, 0x00, 0x00, 0x00, 0x01, 0xff]);
expect(readResponseByte(spi)).toBe(0x00);
expect(readByte(spi)).toBe(0xfe);
expect(readByte(spi)).toBe(0x00);
expect(readByte(spi)).toBe(0x00);
expect(readByte(spi)).toBe(0xa5);
});
});
Loading