OXYGENHUB
◆ main x16-emulator / src / sdcard.c
9.2 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
1
// Commander X16 Emulator
2
// Copyright (c) 2019 Michael Steil
3
// Copyright (c) 2020 Frank van den Hoef
4
// All rights reserved. License: 2-clause BSD
5
#ifndef __APPLE__
6
#define _XOPEN_SOURCE   600
7
#define _POSIX_C_SOURCE 1
8
#endif
9
10
#include <limits.h>
11
#include <stdio.h>
12
#include <stdbool.h>
13
#include <string.h>
14
#include "sdcard.h"
15
#include "files.h"
16
17
//#define VERBOSE 1
18
19
// MMC/SD command (SPI mode)
20
enum {
21
	CMD0   = 0,         // GO_IDLE_STATE
22
	CMD1   = 1,         // SEND_OP_COND
23
	ACMD41 = 0x80 | 41, // SEND_OP_COND (SDC)
24
	CMD8   = 8,         // SEND_IF_COND
25
	CMD9   = 9,         // SEND_CSD
26
	CMD10  = 10,        // SEND_CID
27
	CMD12  = 12,        // STOP_TRANSMISSION
28
	CMD13  = 13,        // SEND_STATUS
29
	ACMD13 = 0x80 | 13, // SD_STATUS (SDC)
30
	CMD16  = 16,        // SET_BLOCKLEN
31
	CMD17  = 17,        // READ_SINGLE_BLOCK
32
	CMD18  = 18,        // READ_MULTIPLE_BLOCK
33
	CMD23  = 23,        // SET_BLOCK_COUNT
34
	ACMD23 = 0x80 | 23, // SET_WR_BLK_ERASE_COUNT (SDC)
35
	CMD24  = 24,        // WRITE_BLOCK
36
	CMD25  = 25,        // WRITE_MULTIPLE_BLOCK
37
	CMD32  = 32,        // ERASE_WR_BLK_START
38
	CMD33  = 33,        // ERASE_WR_BLK_END
39
	CMD38  = 38,        // ERASE
40
	CMD55  = 55,        // APP_CMD
41
	CMD58  = 58,        // READ_OCR
42
};
43
44
static char sdcard_path[PATH_MAX] = "";
45
static struct x16file *sdcard_file = NULL;
46
bool sdcard_attached = false;
47
48
static uint8_t rxbuf[3 + 512];
49
static int rxbuf_idx;
50
static uint32_t lba;
51
static uint8_t last_cmd;
52
static bool is_acmd = false;
53
static bool is_idle = true;
54
static bool is_initialized = false;
55
static bool ongoing_multiblock_read = false;
56
57
static const uint8_t *response = NULL;
58
static int response_length = 0;
59
static int response_counter = 0;
60
61
static bool selected = false;
62
63
void
64
sdcard_set_path(char const *path)
65
{
66
	sdcard_detach();
67
68
	strncpy(sdcard_path, path, PATH_MAX);
69
	sdcard_path[PATH_MAX-1] = '\0';
70
71
	sdcard_attach();
72
}
73
74
bool
75
sdcard_path_is_set()
76
{
77
	return strlen(sdcard_path) > 0;
78
}
79
80
void
81
sdcard_attach()
82
{
83
	if (!sdcard_attached && sdcard_path_is_set()) {
84
		sdcard_file = x16open(sdcard_path, "r+b");
85
		if(sdcard_file == NULL) {
86
			printf("Cannot open SDCard file %s!\n", sdcard_path);
87
			return;
88
		}
89
90
		printf("SD card attached.\n");
91
		sdcard_attached = true;
92
		is_initialized = false;
93
	}
94
}
95
96
void
97
sdcard_detach()
98
{
99
	if (sdcard_attached) {
100
		x16close(sdcard_file);
101
		sdcard_file = NULL;
102
103
		printf("SD card detached.\n");
104
		sdcard_attached = false;
105
	}
106
}
107
108
void
109
sdcard_select(bool select)
110
{
111
	selected = select;
112
	rxbuf_idx = 0;
113
#if defined(VERBOSE) && VERBOSE >= 2
114
	printf("*** SD card select: %u\n", select);
115
#endif
116
}
117
118
static void
119
set_response_csd(void)
120
{
121
	static uint8_t rr[] = {
122
		0xff, // dummy
123
		0xff, // dummy
124
		0x00, // R1 response
125
		0xff, // dummy
126
		0xfe, // begin block
127
		0x40, // CSD_STRUCTURE [7:6] = 1, RESERVED [5:0] = 0
128
		0x0e, // TAAC = 0x0e
129
		0x00, // NSAC = 0x00
130
		0x32, // TRAN_SPEED = 0x32
131
		0x5b, // CCC (11:4)
132
		0x59, // CCC (3:0) [7-4], READ_BL_LEN [3-0]
133
		0x00, // READ_BL_PARTIAL [7], WRITE_BLK_MISALIGN [6], READ_BLK_MISALIGN [5], DSR_IMP [4], RESERVED [3:0]
134
		0x00, // RESERVED [7:6], C_SIZE(21:16) [5:0]
135
		0x00, // C_SIZE(15:8)
136
		0x00, // C_SIZE(7:0)
137
		0x7f, // RESERVED [7], ERASE_BLK_EN[6], SECTOR_SIZE(6:1) [5:0]
138
		0x80, // SECTOR_SIZE(0) [7], WP_GRP_SIZE [6:0]
139
		0x0a, // WP_GRP_ENABLE [7], RESERVED [6:5], R2W_FACTOR [4:2], WRITE_BL_LEN (3:2) [1:0]
140
		0x40, // WRITE_BL_LEN (1:0) [7:6], WRITE_BL_PARTIAL [5], RESERVED [4:0]
141
		0x00, // FILE_FORMAT_GRP [7] = 0, COPY [6], PERM_WRITE_PROTECT [5], TMP_WRITE_PROTECT [4], RESERVED [3:0]
142
		0x01 // CRC[7:1], ALWAYS_1 [0]
143
		};
144
	uint64_t c_size = (x16size(sdcard_file) >> 19)-1;
145
	rr[12] |= (c_size >> 16) & 0x3f;
146
	rr[13] = (c_size >> 8) & 0xff;
147
	rr[14] = c_size & 0xff;
148
149
	response = rr;
150
	response_length = 21;
151
}
152
153
static void
154
set_response_r1(void)
155
{
156
	static uint8_t r1;
157
	r1 = is_idle ? 1 : 0;
158
	response = &r1;
159
	response_length = 1;
160
}
161
162
static void
163
set_response_r2(void)
164
{
165
	if (is_initialized) {
166
		static const uint8_t r2[] = {0x00, 0x00};
167
		response = r2;
168
		response_length = sizeof(r2);
169
	} else {
170
		static const uint8_t r2[] = {0x1F, 0xFF};
171
		response = r2;
172
		response_length = sizeof(r2);
173
	}
174
}
175
176
static void
177
set_response_r3(void)
178
{
179
	static const uint8_t r3[] = {0xC0, 0xFF, 0x80, 0x00};
180
	response = r3;
181
	response_length = sizeof(r3);
182
}
183
184
static void
185
set_response_r7(void)
186
{
187
	static const uint8_t r7[] = {1, 0x00, 0x00, 0x01, 0xAA};
188
	response = r7;
189
	response_length = sizeof(r7);
190
}
191
192
// Return length of reply
193
static int loadBlock(uint8_t *dest)
194
{
195
	int response_length = 0;
196
197
	dest[0] = 0xFE; // Data token for CMD17/18
198
#ifdef VERBOSE
199
	printf("*** SD Reading LBA %d\n", lba);
200
#endif
201
	if ((Sint64)lba * 512 >= x16size(sdcard_file)) {
202
		dest[0] = 0x08; // Error token: out of range
203
		response_length = 1;
204
	} else {
205
		x16seek(sdcard_file, (Sint64)lba * 512, XSEEK_SET);
206
		int bytes_read = x16read(sdcard_file, &dest[1], 1, 512);
207
		if (bytes_read != 512) {
208
			printf("Warning: short read!\n");
209
		}
210
		response_length = 1 + 512 + 2;
211
	}
212
	return response_length;
213
}
214
215
uint8_t
216
sdcard_handle(uint8_t inbyte)
217
{
218
	if (!selected || (sdcard_file == NULL)) {
219
		return 0xFF;
220
	}
221
	// printf("sdcard_handle: %02X\n", inbyte);
222
223
	uint8_t outbyte = 0xFF;
224
225
	if (rxbuf_idx == 0 && inbyte == 0xFF) {
226
		// send response data
227
		if (response) {
228
			outbyte = response[response_counter++];
229
			if (response_counter == response_length) {
230
				if (ongoing_multiblock_read) {
231
					static uint8_t read_multiblock_next_response[1 + 512 + 2];
232
					// Prepare next multiblock reply
233
					lba++;
234
					response_length = loadBlock(&read_multiblock_next_response[0]);
235
					// Stop multiblock read if error
236
					if (response_length == 1) {
237
						ongoing_multiblock_read = false;
238
					}
239
					response = read_multiblock_next_response;
240
					response_counter = 0;
241
				} else  {
242
					response = NULL;
243
					ongoing_multiblock_read = false;
244
				}
245
			}
246
		}
247
248
	} else {
249
		rxbuf[rxbuf_idx++] = inbyte;
250
251
		if ((rxbuf[0] & 0xC0) == 0x40 && rxbuf_idx == 6) {
252
			rxbuf_idx = 0;
253
254
			// Check for start-bit + transmission bit
255
			if ((rxbuf[0] & 0xC0) != 0x40) {
256
				response = NULL;
257
				return 0xFF;
258
			}
259
			rxbuf[0] &= 0x3F;
260
261
			// Use upper command bit to indicate this is an ACMD
262
			if (is_acmd) {
263
				rxbuf[0] |= 0x80;
264
				is_acmd = false;
265
			}
266
267
			last_cmd = rxbuf[0];
268
269
#if defined(VERBOSE) && VERBOSE >= 2
270
			printf("*** SD %sCMD%d -> Response:", (rxbuf[0] & 0x80) ? "A" : "", rxbuf[0] & 0x3F);
271
#endif
272
			switch (rxbuf[0]) {
273
				case CMD0: {
274
					// GO_IDLE_STATE: Resets the SD Memory Card
275
					is_idle = true;
276
					set_response_r1();
277
					break;
278
				}
279
280
				case CMD8: {
281
					// SEND_IF_COND: Sends SD Memory Card interface condition that includes host supply voltage
282
					set_response_r7();
283
					break;
284
				}
285
286
				case CMD9: {
287
					// SEND_CSD: Sends card-specific data
288
					set_response_csd();
289
					break;
290
				}
291
292
				case ACMD41: {
293
					// SD_SEND_OP_COND: Sends host capacity support information and activated the card's initialization process
294
					is_idle = false;
295
					is_initialized = true;
296
					set_response_r1();
297
					break;
298
				}
299
300
				case CMD12: {
301
					// STOP_TRANSMISSION: Abort ongoing multiple block read
302
					ongoing_multiblock_read = false;
303
					set_response_r1();
304
					break;
305
				}
306
307
				case CMD13: {
308
					// SEND_STATUS: Asks the selected card to send its status register
309
					set_response_r2();
310
					break;
311
				}
312
				case CMD16: {
313
					// SET_BLOCKLEN: In case of non-SDHC card, this sets the block length. Block length of SDHC/SDXC cards are fixed to 512 bytes.
314
					set_response_r1();
315
					break;
316
				}
317
				case CMD18: {
318
					// READ_MULTIPLE_BLOCK
319
					ongoing_multiblock_read = true;
320
					// Fallthrough
321
				}
322
				case CMD17: {
323
					// READ_SINGLE_BLOCK
324
					lba = (rxbuf[1] << 24) | (rxbuf[2] << 16) | (rxbuf[3] << 8) | rxbuf[4];
325
					static uint8_t read_block_response[2 + 512 + 2];
326
					read_block_response[0] = 0; // R1 response to command
327
					response_length = 1 + loadBlock(&read_block_response[1]);
328
					// Stop multiblock read if error
329
					if (response_length == 2) {
330
						ongoing_multiblock_read = false;
331
					}
332
					response = read_block_response;
333
					break;
334
				}
335
336
				case CMD24: {
337
					// WRITE_BLOCK
338
					lba = (rxbuf[1] << 24) | (rxbuf[2] << 16) | (rxbuf[3] << 8) | rxbuf[4];
339
					if (rxbuf_idx > 4 && (Sint64)lba * 512 >= x16size(sdcard_file)) {
340
						static uint8_t bad_lba[2] = {0x00, 0x08};
341
						response = bad_lba;
342
						response_length = 2;
343
					} else {
344
						set_response_r1();
345
					}
346
					break;
347
				}
348
349
				case CMD55: {
350
					// APP_CMD: Next command is an application specific command
351
					is_acmd = true;
352
					set_response_r1();
353
					break;
354
				}
355
356
				case CMD58: {
357
					// READ_OCR: Read the OCR register of the card
358
					set_response_r3();
359
					break;
360
				}
361
362
				default: {
363
					set_response_r1();
364
					break;
365
				}
366
			}
367
			response_counter = 0;
368
369
#if defined(VERBOSE) && VERBOSE >= 2
370
			for (int i = 0; i < (response_length < 16 ? response_length : 16); i++) {
371
				printf(" %02X", response[i]);
372
			}
373
			printf("\n");
374
#endif
375
376
		} else if (rxbuf_idx == 515) {
377
			rxbuf_idx = 0;
378
			// Check for 'start block' byte
379
			if (last_cmd == CMD24 && rxbuf[0] == 0xFE) {
380
#ifdef VERBOSE
381
				printf("*** SD Writing LBA %d\n", lba);
382
#endif
383
				if ((Sint64)lba * 512 >= x16size(sdcard_file)) {
384
					// do nothing?
385
				} else {
386
					x16seek(sdcard_file, (Sint64)lba * 512, XSEEK_SET);
387
					int bytes_written = x16write(sdcard_file, rxbuf + 1, 1, 512);
388
					if (bytes_written != 512) {
389
						printf("Warning: short write!\n");
390
					}
391
				}
392
			}
393
		}
394
	}
395
	return outbyte;
396
}