OXYGENHUB
# 8799a5e4565e x16-emulator / src / cartridge.c
11.9 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
1
#include "cartridge.h"
2
#include "files.h"
3
4
#include <ctype.h>
5
#include <string.h>
6
#include <stdlib.h>
7
#include <stdio.h>
8
9
struct x16cartridge_header Cartridge_info;
10
uint8_t *CART = NULL;
11
char *Cartridge_path = NULL;
12
char *Cartridge_nvram_path = NULL;
13
14
const char Cartridge_magic_number[CART_MAGIC_NUMBER_SIZE] = { 'C', 'X', '1', '6', ' ', 'C', 'A', 'R', 'T', 'R', 'I', 'D', 'G', 'E', '\r', '\n' };
15
const char Cartridge_current_version[CART_VERSION_SIZE]   = { '0', '1', '.', '0', '0', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
16
17
static void 
18
initialize_cart_bank(uint8_t *mem, bool randomize)
19
{
20
	if(randomize) {
21
		for(int i=0; i<CART_BANK_SIZE; ++i) {
22
			mem[i] = rand();
23
		}
24
	} else {
25
		memset(mem, 0, CART_BANK_SIZE);
26
	}
27
}
28
29
static int
30
x16_strnicmp(char const *s0, char const *s1, int len)
31
{
32
	for(int i=0; (i<len) && (*s0 && *s1); ++i) {
33
		if(tolower((unsigned char)*s0) != tolower((unsigned char)*s1)) {
34
			break;
35
		}
36
		++s0;
37
		++s1;
38
	}
39
40
	return *s0 - *s1;
41
}
42
43
bool 
44
cartridge_load(const char *path, bool randomize)
45
{
46
	const char *extension = file_find_extension(path, NULL);
47
	if(x16_strnicmp(extension, ".crt", 4) != 0) {
48
		printf("Path \"%s\" does not appear to be a cartridge (.crt) file.\n", path);
49
		return false;
50
	}
51
52
	const size_t path_len = strlen(path);
53
	Cartridge_path = malloc(path_len + 1);
54
	strcpy(Cartridge_path, path);
55
56
	Cartridge_nvram_path = malloc(path_len + 3);
57
	strcpy(Cartridge_nvram_path, Cartridge_path);
58
	strcpy(Cartridge_nvram_path + path_len - 3, "nvram");
59
60
	struct x16file *cart = x16open(Cartridge_path, "rb");
61
	struct x16file *nvram = x16open(Cartridge_nvram_path, "rb");
62
63
	bool ok = !!x16read(cart, (void *)(&Cartridge_info), sizeof(struct x16cartridge_header), 1);
64
	if(!ok) {
65
		printf("Could not load cartridge \"%s\": Could not read header.\n", path);\
66
		goto load_end;
67
	}
68
69
	if(memcmp(Cartridge_info.magic_number, Cartridge_magic_number, CART_MAGIC_NUMBER_SIZE) != 0) {
70
		printf("Could not load cartridge \"%s\": Not a cartridge file.\n", path);
71
		goto load_end;
72
	}
73
74
	if(strncmp(Cartridge_info.cart_version, Cartridge_current_version, CART_VERSION_SIZE) != 0) {
75
		printf("Could not load cartridge \"%s\": Unsupported version.\n", path);
76
		goto load_end;
77
	}
78
79
	if(CART != NULL) {
80
		free(CART);
81
	}
82
	CART = malloc(CART_MAX_SIZE);
83
84
	uint8_t *mem = CART;
85
	for(uint8_t t=0; ok && t<CART_MAX_BANKS; ++t) {
86
		switch(Cartridge_info.bank_info[t]) {
87
			case CART_BANK_NONE: /* skip */ break;
88
			case CART_BANK_ROM: ok = !!x16read(cart, mem, CART_BANK_SIZE, 1); break;
89
			case CART_BANK_UNINITIALIZED_RAM: initialize_cart_bank(mem, randomize); break;
90
			case CART_BANK_INITIALIZED_RAM: ok = !!x16read(cart, mem, CART_BANK_SIZE, 1); break;
91
			case CART_BANK_UNINITIALIZED_NVRAM: initialize_cart_bank(mem, randomize); break;
92
			case CART_BANK_INITIALIZED_NVRAM:
93
				if(nvram) {
94
					ok = !!x16read(nvram, mem, CART_BANK_SIZE, 1);
95
					x16seek(cart, CART_BANK_SIZE, XSEEK_CUR);
96
				} else {
97
					ok = !!x16read(cart, mem, CART_BANK_SIZE, 1);
98
				}
99
				break;
100
			default: printf("Warning: Unknown cartridge bank type at %d\n", t); break;
101
		}
102
		mem += CART_BANK_SIZE;
103
	}
104
105
load_end:
106
	if(!ok) {
107
		free(CART);
108
		free(Cartridge_path);
109
		free(Cartridge_nvram_path);
110
111
		CART = NULL;
112
		Cartridge_path = NULL;
113
		Cartridge_nvram_path = NULL;
114
	}
115
116
	x16close(cart);
117
	x16close(nvram);
118
119
	return ok;
120
}
121
122
void cartridge_unload()
123
{
124
	if(CART != NULL) {
125
		free(CART);
126
		CART = NULL;
127
	}
128
	if(Cartridge_path != NULL) {
129
		free(Cartridge_path);
130
		Cartridge_path = NULL;
131
	}
132
	if(Cartridge_nvram_path != NULL) {
133
		free(Cartridge_nvram_path);
134
		Cartridge_nvram_path = NULL;
135
	}
136
}
137
138
void 
139
cartridge_new()
140
{
141
	cartridge_unload();
142
143
	memset(&Cartridge_info, 0, sizeof(struct x16cartridge_header));
144
	memcpy(Cartridge_info.magic_number, Cartridge_magic_number, CART_MAGIC_NUMBER_SIZE);
145
	memcpy(Cartridge_info.cart_version, Cartridge_current_version, CART_VERSION_SIZE);
146
147
	CART = malloc(CART_MAX_SIZE);
148
}
149
150
void 
151
cartridge_set_desc(const char *name)
152
{
153
	if(name == NULL) {
154
		return;
155
	}
156
157
	int namelen = strlen(name);
158
	if(namelen > CART_DESCRIPTION_SIZE) {
159
		namelen = CART_DESCRIPTION_SIZE;
160
	}
161
162
	memset(Cartridge_info.description, 0x20, CART_DESCRIPTION_SIZE);
163
	memcpy(Cartridge_info.description, name, namelen);
164
}
165
166
void
167
cartridge_set_author(const char *name)
168
{
169
	if(name == NULL) {
170
		return;
171
	}
172
173
	int namelen = strlen(name);
174
	if(namelen > CART_AUTHOR_SIZE) {
175
		namelen = CART_AUTHOR_SIZE;
176
	}
177
178
	memset(Cartridge_info.author, 0x20, CART_AUTHOR_SIZE);
179
	memcpy(Cartridge_info.author, name, namelen);
180
}
181
182
void
183
cartridge_set_copyright(const char *name)
184
{
185
	if(name == NULL) {
186
		return;
187
	}
188
189
	int namelen = strlen(name);
190
	if(namelen > CART_COPYRIGHT_SIZE) {
191
		namelen = CART_COPYRIGHT_SIZE;
192
	}
193
194
	memset(Cartridge_info.copyright, 0x20, CART_COPYRIGHT_SIZE);
195
	memcpy(Cartridge_info.copyright, name, namelen);
196
197
}
198
199
void
200
cartridge_set_program_version(const char *name)
201
{
202
	if(name == NULL) {
203
		return;
204
	}
205
206
	int namelen = strlen(name);
207
	if(namelen > CART_PROGRAM_VERSION_SIZE) {
208
		namelen = CART_PROGRAM_VERSION_SIZE;
209
	}
210
211
	memset(Cartridge_info.prg_version, 0x20, CART_PROGRAM_VERSION_SIZE);
212
	memcpy(Cartridge_info.prg_version, name, namelen);
213
}
214
215
static char *
216
rtrim(char *str)
217
{
218
    char *c = str + strlen(str) - 1;
219
    while(isspace((unsigned char)*c)) {
220
		--c;
221
	}
222
    *(c + 1) = '\0';
223
	return str;
224
}
225
226
void 
227
cartridge_get_desc(char *buffer, size_t buffer_size)
228
{
229
	if(buffer == NULL) {
230
		return;
231
	}
232
233
	if(buffer_size > CART_DESCRIPTION_SIZE + 1) {
234
		buffer_size = CART_DESCRIPTION_SIZE + 1;
235
	}
236
237
	memcpy(buffer, Cartridge_info.description, buffer_size - 1);
238
	buffer[buffer_size - 1] = '\0';
239
240
	rtrim(buffer);
241
}
242
243
void
244
cartridge_get_author(char *buffer, size_t buffer_size)
245
{
246
	if(buffer == NULL) {
247
		return;
248
	}
249
250
	if(buffer_size > CART_AUTHOR_SIZE + 1) {
251
		buffer_size = CART_AUTHOR_SIZE + 1;
252
	}
253
254
	memcpy(buffer, Cartridge_info.author, buffer_size - 1);
255
	buffer[buffer_size - 1] = '\0';
256
257
	rtrim(buffer);
258
}
259
260
void
261
cartridge_get_copyright(char *buffer, size_t buffer_size)
262
{
263
	if(buffer == NULL) {
264
		return;
265
	}
266
267
	if(buffer_size > CART_COPYRIGHT_SIZE + 1) {
268
		buffer_size = CART_COPYRIGHT_SIZE + 1;
269
	}
270
271
	memcpy(buffer, Cartridge_info.copyright, buffer_size - 1);
272
	buffer[buffer_size - 1] = '\0';
273
274
	rtrim(buffer);
275
}
276
277
void
278
cartridge_get_program_version(char *buffer, size_t buffer_size)
279
{
280
	if(buffer == NULL) {
281
		return;
282
	}
283
284
	if(buffer_size > CART_PROGRAM_VERSION_SIZE + 1) {
285
		buffer_size = CART_PROGRAM_VERSION_SIZE + 1;
286
	}
287
288
	memcpy(buffer, Cartridge_info.prg_version, buffer_size - 1);
289
	buffer[buffer_size - 1] = '\0';
290
291
	rtrim(buffer);
292
}
293
294
bool 
295
cartridge_define_bank_range(uint8_t start_bank, uint8_t end_bank, uint8_t bank_type)
296
{
297
	if(start_bank < 32) {
298
		return false;
299
	}
300
301
	start_bank -= 32;
302
303
	if(start_bank >= CART_MAX_BANKS) {
304
		return false;
305
	}
306
307
	if(end_bank < 32) {
308
		return false;
309
	}
310
311
	end_bank -= 32;
312
313
	if(end_bank >= CART_MAX_BANKS) {
314
		return false;
315
	}
316
317
	if(start_bank > end_bank) {
318
		return false;
319
	}
320
321
	if(bank_type > CART_NUM_BANK_TYPES) {
322
		printf("Warning: Attempting to define unknown cartridge bank type %d\n", bank_type);
323
	}
324
325
	for(int i = start_bank; i <= end_bank; ++i) {
326
		Cartridge_info.bank_info[i] = bank_type;
327
	}
328
329
	return true;
330
}
331
332
bool
333
cartridge_import_files(char **bin_files, int num_files, uint8_t start_bank, uint8_t bank_type, uint8_t fill_value)
334
{
335
	if(start_bank < 32) {
336
		return false;
337
	}
338
339
	start_bank -= 32;
340
341
	if(start_bank >= CART_MAX_BANKS) {
342
		return false;
343
	}
344
345
	uint32_t start_address = ((uint32_t)start_bank) << 14;
346
	uint32_t available_size = CART_MAX_SIZE - start_address;
347
348
	for(int i=0; i<num_files; ++i) {
349
		if(available_size == 0) {
350
			return false;
351
		}
352
353
		struct x16file *bin = x16open(bin_files[i], "rb");
354
		if(bin == NULL) {
355
			return false;
356
		}
357
358
		const uint32_t data_read = (uint32_t)x16read(bin, CART + start_address, 1, available_size);
359
		available_size -= data_read;
360
		start_address += data_read;
361
362
		x16close(bin);
363
	}
364
365
	uint32_t fill_end = (start_address + CART_BANK_SIZE - 1) & (0xffUL << 14);
366
	if(fill_end > CART_MAX_SIZE) {
367
		fill_end = CART_MAX_SIZE;
368
	}
369
	memset(CART + start_address, fill_value, fill_end - start_address);
370
	
371
	uint8_t last_bank = (start_address - 1) >> 14;
372
	for(uint8_t i = start_bank; i <= last_bank; ++i) {
373
		Cartridge_info.bank_info[i] = bank_type;
374
	}
375
	return true;
376
}
377
378
bool
379
cartridge_fill(uint8_t start_bank, uint8_t end_bank, uint8_t bank_type, uint8_t fill_value)
380
{
381
	if(start_bank < 32) {
382
		return false;
383
	}
384
385
	start_bank -= 32;
386
387
	if(start_bank >= CART_MAX_BANKS) {
388
		return false;
389
	}
390
391
	if(end_bank < 32) {
392
		return false;
393
	}
394
395
	end_bank -= 32;
396
397
	if(end_bank >= CART_MAX_BANKS) {
398
		return false;
399
	}
400
401
	if(start_bank > end_bank) {
402
		return false;
403
	}
404
405
	uint32_t fill_start = ((uint32_t)start_bank) << 14;
406
	uint32_t fill_end = ((uint32_t)end_bank + 1) << 14;
407
	memset(CART + fill_start, fill_value, fill_end - fill_start);
408
409
	for(uint8_t i = start_bank; i <= end_bank; ++i) {
410
		Cartridge_info.bank_info[i] = bank_type;
411
	}
412
	return true;
413
}
414
415
bool 
416
cartridge_save(const char *path)
417
{
418
	bool compressed = file_is_compressed_type(path);
419
	const char *extension = file_find_extension(path, NULL);
420
	if(strncmp(extension, ".crt", 4) != 0) {
421
		printf("Path \"%s\" does not appear to be a cartridge (.crt) file.\n", path);
422
		return false;
423
	}
424
425
	struct x16file *cart = x16open(path, compressed ? "wb6" : "wb");
426
427
	uint64_t header_wrote = x16write(cart, (void*)(&Cartridge_info), sizeof(struct x16cartridge_header), 1);
428
	if(!header_wrote) {
429
		printf("Could not save cartridge \"%s\": Could not write header.\n", path);
430
		return false;
431
	}
432
433
	uint8_t *mem = CART;
434
	bool ok = true;
435
	for(uint8_t t=0; ok && t<CART_MAX_BANKS; ++t) {
436
		switch(Cartridge_info.bank_info[t]) {
437
			case CART_BANK_NONE: /* skip */ break;
438
			case CART_BANK_ROM: ok = !!x16write(cart, mem, CART_BANK_SIZE, 1); break;
439
			case CART_BANK_UNINITIALIZED_RAM: /* skip */ break;
440
			case CART_BANK_INITIALIZED_RAM: ok = !!x16write(cart, mem, CART_BANK_SIZE, 1); break;
441
			case CART_BANK_UNINITIALIZED_NVRAM: /* skip */ break;
442
			case CART_BANK_INITIALIZED_NVRAM: ok = !!x16write(cart, mem, CART_BANK_SIZE, 1); break;
443
			default: printf("Warning: Unknown cartridge bank type at %d\n", t); break;
444
		}
445
		mem += CART_BANK_SIZE;
446
	}
447
448
	if(!ok) {
449
		printf("Warning: Failed to save some cartridge data. The cartridge may be corrupt.\n");
450
	}
451
452
	x16close(cart);
453
454
	return ok;
455
}
456
457
bool
458
cartridge_save_nvram()
459
{
460
	if(Cartridge_nvram_path == NULL) {
461
		return false;
462
	}
463
464
	struct x16file *nvram = x16open(Cartridge_nvram_path, "wb");
465
466
	uint8_t *mem = CART;
467
	bool ok = true;
468
	for(uint8_t t=0; ok && t<CART_MAX_BANKS; ++t) {
469
		switch(Cartridge_info.bank_info[t]) {
470
			case CART_BANK_NONE: /* skip */ break;
471
			case CART_BANK_ROM: /* skip */ break;
472
			case CART_BANK_UNINITIALIZED_RAM: /* skip */ break;
473
			case CART_BANK_INITIALIZED_RAM: /* skip */ break;
474
			case CART_BANK_UNINITIALIZED_NVRAM: ok = !!x16write(nvram, mem, CART_BANK_SIZE, 1); break;
475
			case CART_BANK_INITIALIZED_NVRAM: ok = !!x16write(nvram, mem, CART_BANK_SIZE, 1); break;
476
			default: printf("Warning: Unknown cartridge bank type at %d\n", t); break;
477
		}
478
		mem += CART_BANK_SIZE;
479
	}
480
481
	if(!ok) {
482
		printf("Warning: Failed to save some nvram data. The nvram may be corrupt.\n");
483
	}
484
485
	x16close(nvram);
486
487
	return ok;
488
}
489
490
void 
491
cartridge_write(uint16_t address, uint8_t bank, uint8_t value)
492
{
493
	if(!CART) {
494
		return;
495
	}
496
497
	if(bank < 32) {
498
		return;
499
	}
500
501
	bank -= 32;
502
503
	if(bank >= CART_MAX_BANKS) {
504
		return;
505
	}
506
507
	switch(Cartridge_info.bank_info[bank]) {
508
		case CART_BANK_NONE: // fall-through
509
		case CART_BANK_ROM:
510
		default:
511
			break;
512
513
		case CART_BANK_UNINITIALIZED_RAM: // fall-through
514
		case CART_BANK_INITIALIZED_RAM: // fall-through
515
		case CART_BANK_UNINITIALIZED_NVRAM: // fall-through
516
		case CART_BANK_INITIALIZED_NVRAM:
517
			CART[((uint32_t)bank << 14) + address - 0xc000] = value;
518
			break;
519
	}
520
}
521
522
uint8_t 
523
cartridge_read(uint16_t address, uint8_t bank)
524
{
525
	if(!CART) {
526
		return 0;
527
	}
528
529
	if(bank < 32) {
530
		return 0;
531
	}
532
533
	bank -= 32;
534
535
	if(bank >= CART_MAX_BANKS) {
536
		return 0;
537
	}
538
539
	return CART[((uint32_t)bank << 14) + address - 0xc000];
540
}
541
542
uint8_t
543
cartridge_get_bank_type(uint8_t bank)
544
{
545
	if(bank < 32) {
546
		return CART_BANK_NONE;
547
	}
548
549
	bank -= 32;
550
551
	if(bank >= CART_MAX_BANKS) {
552
		return CART_BANK_NONE;
553
	}
554
555
	return Cartridge_info.bank_info[bank];
556
}