OXYGENHUB
# ea9efff4e540 x16-emulator / src / memory.c
15.5 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
03a3459e Andy McCall 1
// Commander X16 Emulator
2
// Copyright (c) 2019 Michael Steil
3
// All rights reserved. License: 2-clause BSD
4
5
#include <sys/types.h>
6
#include <string.h>
7
#include <stdlib.h>
8
#include <unistd.h>
9
#include <time.h>
10
#include <inttypes.h>
11
#include "glue.h"
12
#include "via.h"
13
#include "memory.h"
14
#include "video.h"
15
#include "ymglue.h"
16
#include "cpu/fake6502.h"
17
#include "wav_recorder.h"
18
#include "audio.h"
19
#include "cartridge.h"
20
#include "iso_8859_15.h"
21
#include "midi.h"
22
#include "netcard.h"
23
24
uint8_t ram_bank;
25
uint8_t rom_bank;
26
27
uint8_t *RAM, *BRAM;
28
uint8_t ROM[ROM_SIZE];
29
extern uint8_t *CART;
30
31
static uint8_t addr_ym = 0;
32
33
bool randomizeRAM = false;
34
bool reportUninitializedAccess = false;
35
const char *reportUsageStatisticsFilename = NULL;
36
bool *RAM_access_flags, *BRAM_access_flags;
37
uint64_t *RAM_system_reads;
38
uint64_t *RAM_system_writes;
39
uint64_t *RAM_banked_reads[256];
40
uint64_t *RAM_banked_writes[256];
41
uint64_t *ROM_banked_reads[256];
42
uint64_t *ROM_banked_writes[256];	// shouldn't occur for obvious reasons unless Bonk RAM is installed in a cart
43
44
45
static uint32_t clock_snap = 0UL;
46
static uint32_t clock_base = 0UL;
47
48
#define DEVICE_EMULATOR (0x9fb0)
49
50
void cpuio_write(uint8_t reg, uint8_t value);
51
52
void
53
memory_init()
54
{
55
	// Initialize RAM array
56
	RAM = calloc(RAM_SIZE, sizeof(uint8_t));
57
	BRAM = calloc(BRAM_SIZE, sizeof(uint8_t));
58
59
	if(reportUsageStatisticsFilename!=NULL) {
60
		RAM_system_reads = calloc(num_banks * BANK_SIZE, sizeof(uint64_t));
61
		RAM_system_writes = calloc(num_banks * BANK_SIZE, sizeof(uint64_t));
62
		for(int bank=0; bank<256; ++bank) {
63
			RAM_banked_reads[bank] = calloc(8192, sizeof(uint64_t));
64
			RAM_banked_writes[bank] = calloc(8192, sizeof(uint64_t));
65
			ROM_banked_reads[bank] = calloc(16384, sizeof(uint64_t));
66
			ROM_banked_writes[bank] = calloc(16384, sizeof(uint64_t));
67
		}
68
	}
69
70
71
	// Randomize all RAM (if option selected)
72
	if (randomizeRAM) {
73
		time_t t;
74
		srand((unsigned)time(&t));
75
		for (int i = 0; i < RAM_SIZE; i++) {
76
			if (i >= 0x9f00 && i < 0x10000) {
77
				// Leave the unused hole in the address space at 0
78
				// Memory dumps will likely confuse people less often
79
				RAM[i] = 0;
80
			} else {
81
				RAM[i] = rand();
82
			}
83
		}
84
		for (int i = 0; i < BRAM_SIZE; i++) {
85
			BRAM[i] = rand();
86
		}
87
	}
88
89
	// Initialize RAM access flag array (if option selected)
90
	if (reportUninitializedAccess) {
91
		RAM_access_flags = (bool*) malloc(RAM_SIZE * sizeof(bool));
92
		for (int i = 0; i < RAM_SIZE; i++) {
93
			RAM_access_flags[i] = false;
94
		}
95
		BRAM_access_flags = (bool*) malloc(BRAM_SIZE * sizeof(bool));
96
		for (int i = 0; i < BRAM_SIZE; i++) {
97
			BRAM_access_flags[i] = false;
98
		}
99
	}
100
101
	memory_reset();
102
}
103
104
void
105
memory_reset()
106
{
107
	// default banks are 0
108
	memory_set_ram_bank(0);
109
	memory_set_rom_bank(0);
110
}
111
112
void
113
memory_report_uninitialized_access(bool value)
114
{
115
	reportUninitializedAccess = value;
116
}
117
118
void
119
memory_report_usage_statistics(const char *filename) {
120
	reportUsageStatisticsFilename = filename;
121
}
122
123
void
124
memory_randomize_ram(bool value)
125
{
126
	randomizeRAM = value;
127
}
128
129
void
130
memory_initialize_cart(uint8_t *mem)
131
{
132
	if(randomizeRAM) {
133
		for(int i=0; i<0x4000; ++i) {
134
			mem[i] = rand();
135
		}
136
	} else {
137
		memset(mem, 0, 0x4000);
138
	}
139
}
140
141
//
142
// interface for fake6502
143
//
144
// if debugOn then reads memory only for debugger; no I/O, no side effects whatsoever
145
146
static const char *format_addr(uint16_t address, uint8_t bank, uint8_t x16Bank) {
147
	static char buffer[2 + 1 + 4 + 1];
148
149
	if (bank == 0 && address >= 0xA000) {
150
		if (address >= 0xA000) {
151
			snprintf(buffer, sizeof(buffer), "%02X:%04X", x16Bank, address);
152
		}
153
	}
154
	else if (is_gen2) {
155
		snprintf(buffer, sizeof(buffer), "%02X %04X", bank, address);
156
	} else {
157
		snprintf(buffer, sizeof(buffer), "%04X", address);
158
	}
159
160
	return buffer;
161
}
162
163
uint8_t
164
read6502(uint16_t address, uint8_t bank) {
165
	if (!is_gen2) bank = 0;
166
	// Report access to uninitialized RAM (if option selected)
167
	if (reportUninitializedAccess) {
168
		if (bank == 0) {
169
			uint8_t pc_x16_bank;
170
171
			if (opcode_addr < 0xa000) {
172
				pc_x16_bank = 0;
173
			} else if (opcode_addr < 0xc000) {
174
				pc_x16_bank = memory_get_ram_bank();
175
			} else {
176
				pc_x16_bank = memory_get_rom_bank();
177
			}
178
179
			if (address >= 0xa000 && address < 0xc000) {
180
				if (memory_get_ram_bank() < num_ram_banks && BRAM_access_flags[(memory_get_ram_bank() << 13) + address - 0xa000] == false){
181
					printf("Warning: %02X:%04X accessed uninitialized RAM address %02X:%04X\n", pc_x16_bank, opcode_addr, memory_get_ram_bank(), address);
182
				}
183
			}
184
		}
185
186
		if (bank != 0 || address < 0x9f00) {
187
			if (RAM_access_flags[bank * BANK_SIZE + address] == false) {
188
				printf("Warning: %s accessed uninitialized RAM address %02X %04X\n", format_addr(opcode_addr, regs.k, 0), bank, address);
189
			}
190
		}
191
	}
192
193
    if (reportUsageStatisticsFilename!=NULL) {
194
      if (bank != 0 || address < 0xa000) {
195
        RAM_system_reads[bank * BANK_SIZE + address]++;
196
      } else if (address < 0xc000) {
197
        RAM_banked_reads[memory_get_ram_bank()][address-0xa000]++;
198
      } else {
199
		ROM_banked_reads[rom_bank][address-0xc000]++;
200
      }
201
    }
202
203
	return real_read6502(address, bank, false, USE_CURRENT_X16_BANK);
204
}
205
206
uint8_t
207
real_read6502(uint16_t address, uint8_t bank, bool debugOn, int16_t x16Bank)
208
{
209
	if (is_gen2 && bank != 0) { // RAM
210
		if (bank < num_banks) {
211
			return RAM[bank * BANK_SIZE + address];
212
		} else {
213
			return (address >> 8) & 0xff; // open bus read
214
		}
215
	}
216
217
	if (address < 0x9f00) { // RAM
218
		return RAM[address];
219
	} else if (address < 0xa000) { // I/O
220
		if (!debugOn && address >= 0x9fa0) {
221
			// slow IO5-7 range
222
			clockticks6502 += 3;
223
		}
224
		if (address >= 0x9f00 && address < 0x9f10) {
225
			return via1_read(address & 0xf, debugOn);
226
		} else if (has_via2 && (address >= 0x9f10 && address < 0x9f20)) {
227
			return via2_read(address & 0xf, debugOn);
228
		} else if (address >= 0x9f20 && address < 0x9f40) {
229
			return video_read(address & 0x1f, debugOn);
230
		} else if (address >= 0x9f40 && address < 0x9f60) {
231
			// slow IO2 range
232
			if (!debugOn) {
233
				clockticks6502 += 3;
234
			}
235
			if ((address & 0x01) != 0) { // partial decoding in this range
236
				audio_render();
237
				return YM_read_status();
238
			}
239
			return 0x9f; // open bus read
240
		} else if (address >= 0x9fb0 && address < 0x9fc0) {
241
			// emulator state
242
			return emu_read(address & 0xf, debugOn);
243
		} else if (has_netcard && (address & 0xfff0) == netcard_addr) {
244
			// virtual serial/network card
245
			return netcard_read(address & 0xf, debugOn);
246
		} else if (has_midi_card && (address & 0xfff0) == midi_card_addr) {
247
			// midi card
248
			return midi_serial_read(address & 0xf, debugOn);
249
		} else {
250
			// future expansion
251
			return 0x9f; // open bus read
252
		}
253
	} else if (address < 0xc000) { // banked RAM
254
		int ramBank = x16Bank >= 0 ? (uint8_t)x16Bank : memory_get_ram_bank();
255
		if (ramBank < num_ram_banks) {
256
			return BRAM[(ramBank << 13) + address - 0xa000];
257
		} else {
258
			return (address >> 8) & 0xff; // open bus read
259
		}
260
	} else { // banked ROM
261
		int romBank = x16Bank >= 0 ? (uint8_t)x16Bank : rom_bank;
262
		if (romBank < 32) {
263
			return ROM[(romBank << 14) + address - 0xc000];
264
		} else {
265
			if (!CART) {
266
				return (address >> 8) & 0xff; // open bus read
267
			}
268
			return cartridge_read(address, romBank);
269
		}
270
	}
271
}
272
273
void
274
write6502(uint16_t address, uint8_t bank, uint8_t value)
275
{
276
	if (!is_gen2) bank = 0;
277
278
	if(reportUsageStatisticsFilename!=NULL) {
279
		if (bank != 0 || address < 0xa000) {
280
			RAM_system_writes[bank * BANK_SIZE + address]++;
281
		} else if (address < 0xc000) {
282
			RAM_banked_writes[memory_get_ram_bank()][address-0xa000]++;
283
		} else {
284
			// this is weird, but it does occur. And cartridges can install "Bonk RAM" in place of ROM.
285
			ROM_banked_writes[rom_bank][address-0xc000]++;
286
		}
287
	}
288
289
	// Update RAM access flag
290
	if (reportUninitializedAccess) {
291
		if (bank != 0 || address < 0xa000) {
292
			RAM_access_flags[bank * BANK_SIZE + address] = true;
293
		} else if (address < 0xc000) {
294
			if (memory_get_ram_bank() < num_ram_banks)
295
				BRAM_access_flags[(memory_get_ram_bank() << 13) + address - 0xa000] = true;
296
		}
297
	}
298
299
	// Write to memory
300
	if (is_gen2 && bank != 0) {
301
		if (bank < num_banks) {
302
			RAM[bank * BANK_SIZE + address] = value;
303
		}
304
		return;
305
	}
306
307
	// Write to CPU I/O ports
308
	if (address < 2) {
309
		cpuio_write(address, value);
310
	}
311
	// Write to memory
312
	if (address < 0x9f00) { // RAM
313
		RAM[address] = value;
314
	} else if (address < 0xa000) { // I/O
315
		if (address >= 0x9fa0) {
316
			// slow IO5-7 range
317
			clockticks6502 += 3;
318
		}
319
		if (address >= 0x9f00 && address < 0x9f10) {
320
			via1_write(address & 0xf, value);
321
		} else if (has_via2 && (address >= 0x9f10 && address < 0x9f20)) {
322
			via2_write(address & 0xf, value);
323
		} else if (address >= 0x9f20 && address < 0x9f40) {
324
			video_write(address & 0x1f, value);
325
		} else if (address >= 0x9f40 && address < 0x9f60) {
326
			// slow IO2 range
327
			clockticks6502 += 3;
328
			if ((address & 0x01) == 0) {   // YM reg (partially decoded)
329
				addr_ym = value;
330
			} else {                       // YM data (partially decoded)
331
				audio_render();
332
				YM_write_reg(addr_ym, value);
333
			}
334
		} else if (address >= 0x9fb0 && address < 0x9fc0) {
335
			// emulator state
336
			emu_write(address & 0xf, value);
337
		} else if (has_netcard && (address & 0xfff0) == netcard_addr) {
338
			netcard_write(address & 0xf, value);
339
		} else if (has_midi_card && (address & 0xfff0) == midi_card_addr) {
340
			midi_serial_write(address & 0xf, value);
341
		} else {
342
			// future expansion
343
		}
344
	} else if (address < 0xc000) { // banked RAM
345
		if (memory_get_ram_bank() < num_ram_banks) {
346
			BRAM[(memory_get_ram_bank() << 13) + address - 0xa000] = value;
347
		}
348
	} else { // ROM
349
		if (rom_bank >= 32) { // Cartridge ROM/RAM
350
			cartridge_write(address, rom_bank, value);
351
		}
352
		// ignore if base ROM (banks 0-31)
353
	}
354
}
355
356
void
357
vp6502()
358
{
359
	memory_set_rom_bank(0);
360
}
361
362
//
363
// saves the memory content into a file
364
//
365
366
void
367
memory_save(SDL_RWops *f, bool dump_ram, bool dump_bank)
368
{
369
	if (dump_ram) {
370
		SDL_RWwrite(f, &RAM[0], sizeof(uint8_t), is_gen2 ? num_banks * BANK_SIZE : 0xa000);
371
	}
372
	if (dump_bank) {
373
		SDL_RWwrite(f, &BRAM[0], sizeof(uint8_t), (num_ram_banks * 8192));
374
	}
375
}
376
377
378
void writestring(SDL_RWops *f, const char *string) {
379
	SDL_RWwrite(f, string, strlen(string), 1);
380
}
381
382
void memory_dump_usage_counts() {
383
	if(reportUsageStatisticsFilename==NULL)
384
		return;
385
386
	SDL_RWops *f = SDL_RWFromFile(reportUsageStatisticsFilename, "w");
387
	if (!f) {
388
		printf("Cannot write to %s!\n", reportUsageStatisticsFilename);
389
		return;
390
	}
391
392
	writestring(f, "Usage counts of all memory locations. Locations not printed have count zero.\n");
393
	writestring(f, "Tip: use 'sort -r -n -k 3' to sort it so it shows the most used at the top.\n");
394
	writestring(f, "\nsystem RAM reads:\n");
395
	int addr;
396
	char buf[100];
397
	for(addr=0; addr<num_banks * BANK_SIZE; ++addr) {
398
		if(RAM_system_reads[addr]>0) {
399
			SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "r %04x %" PRIu64 "\n", addr, RAM_system_reads[addr]), 1);
400
		}
401
	}
402
	writestring(f, "\nsystem RAM writes:\n");
403
	for(addr=0; addr<65536; ++addr) {
404
		if(RAM_system_writes[addr]>0) {
405
			SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "w %04x %" PRIu64 "\n", addr, RAM_system_writes[addr]), 1);
406
		}
407
	}
408
	writestring(f, "\nbanked RAM reads:\n");
409
	int bank;
410
	for(bank=0; bank<256; ++bank) {
411
		for(addr=0; addr<8192; ++addr) {
412
			if(RAM_banked_reads[bank][addr]>0) {
413
				SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "r %02x:%04x %" PRIu64 "\n", bank, addr+0xa000, RAM_banked_reads[bank][addr]), 1);
414
			}
415
		}
416
	}
417
	writestring(f, "\nbanked RAM writes:\n");
418
	for(bank=0; bank<256; ++bank) {
419
		for(addr=0; addr<8192; ++addr) {
420
			if(RAM_banked_writes[bank][addr]>0) {
421
				SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "w %02x:%04x %" PRIu64 "\n", bank, addr+0xa000, RAM_banked_writes[bank][addr]), 1);
422
			}
423
		}
424
	}
425
	writestring(f, "\nbanked ROM reads:\n");
426
	for(bank=0; bank<256; ++bank) {
427
		for(addr=0; addr<16384; ++addr) {
428
			if(ROM_banked_reads[bank][addr]>0) {
429
				SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "r %02x:%04x %" PRIu64 "\n", bank, addr+0xc000, ROM_banked_reads[bank][addr]), 1);
430
			}
431
		}
432
	}
433
	writestring(f, "\nbanked ROM / 'Bonk RAM' writes:\n");
434
	for(bank=0; bank<256; ++bank) {
435
		for(addr=0; addr<16384; ++addr) {
436
			if(ROM_banked_writes[bank][addr]>0) {
437
				SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "w %02x:%04x %" PRIu64 "\n", bank, addr+0xc000, ROM_banked_writes[bank][addr]), 1);
438
			}
439
		}
440
	}
441
442
	SDL_RWclose(f);
443
}
444
445
446
///
447
///
448
///
449
450
inline void
451
memory_set_ram_bank(uint8_t bank)
452
{
453
	ram_bank = bank;
454
}
455
456
inline uint8_t
457
memory_get_ram_bank()
458
{
459
	return ram_bank;
460
}
461
462
inline void
463
memory_set_rom_bank(uint8_t bank)
464
{
465
	rom_bank = bank;
466
}
467
468
inline uint8_t
469
memory_get_rom_bank()
470
{
471
	return rom_bank;
472
}
473
474
void
475
cpuio_write(uint8_t reg, uint8_t value)
476
{
477
	switch (reg) {
478
		case 0:
479
			memory_set_ram_bank(value);
480
			break;
481
		case 1:
482
			memory_set_rom_bank(value);
483
			break;
484
	}
485
}
486
487
// Control the GIF recorder
488
void
489
emu_recorder_set(gif_recorder_command_t command)
490
{
491
	// turning off while recording is enabled
492
	if (command == RECORD_GIF_PAUSE && record_gif != RECORD_GIF_DISABLED) {
493
		record_gif = RECORD_GIF_PAUSED; // need to save
494
	}
495
	// turning on continuous recording
496
	if (command == RECORD_GIF_RESUME && record_gif != RECORD_GIF_DISABLED) {
497
		record_gif = RECORD_GIF_ACTIVE;		// activate recording
498
	}
499
	// capture one frame
500
	if (command == RECORD_GIF_SNAP && record_gif != RECORD_GIF_DISABLED) {
501
		record_gif = RECORD_GIF_SINGLE;		// single-shot
502
	}
503
}
504
505
//
506
// read/write emulator state (feature flags)
507
//
508
// 0: debugger_enabled
509
// 1: log_video
510
// 2: log_keyboard
511
// 3: echo_mode
512
// 4: save_on_exit
513
// 5: record_gif
514
// 6: record_wav
515
// 7: cmd key toggle
516
// 8: write: reset cpu clock counter
517
// 8: read: snapshots cpu clock counter and reads the LSB bits 0-7
518
// 9: write: output debug byte 1
519
// 9: read: cpu clock bits 8-15
520
// 10: write: output debug byte 2
521
// 10: read: cpu clock bits 16-23
522
// 11: write: write character to STDOUT of console
523
// 11: read: cpu clock MSB bits 24-31
524
// POKE $9FB3,1:PRINT"ECHO MODE IS ON":POKE $9FB3,0
525
void
526
emu_write(uint8_t reg, uint8_t value)
527
{
528
	bool v = value != 0;
529
	switch (reg) {
530
		case 0: debugger_enabled = v; break;
531
		case 1: log_video = v; break;
532
		case 2: log_keyboard = v; break;
533
		case 3: echo_mode = value; break;
534
		case 4: save_on_exit = v; break;
535
		case 5: emu_recorder_set((gif_recorder_command_t) value); break;
536
		case 6: wav_recorder_set((wav_recorder_command_t) value); break;
537
		case 7: disable_emu_cmd_keys = v; break;
538
		case 8: clock_base = clockticks6502; break;
539
		case 9: printf("User debug 1: $%02x\n", value); fflush(stdout); break;
540
		case 10: printf("User debug 2: $%02x\n", value); fflush(stdout); break;
541
		case 11: {
542
			if (value == 0x09 || value == 0x0a || value == 0x0d || (value >= 0x20 && value < 0x7f)) {
543
				printf("%c", value);
544
			} else if (value >= 0xa1) {
545
				print_iso8859_15_char((char) value);
546
			} else {
547
				printf("\xef\xbf\xbd"); // �
548
			}
549
			fflush(stdout);
550
			break;
551
		}
552
		default: printf("WARN: Invalid register %x\n", DEVICE_EMULATOR + reg);
553
	}
554
}
555
556
uint8_t
557
emu_read(uint8_t reg, bool debugOn)
558
{
559
	if (reg == 0) {
560
		return debugger_enabled ? 1 : 0;
561
	} else if (reg == 1) {
562
		return log_video ? 1 : 0;
563
	} else if (reg == 2) {
564
		return log_keyboard ? 1 : 0;
565
	} else if (reg == 3) {
566
		return echo_mode;
567
	} else if (reg == 4) {
568
		return save_on_exit ? 1 : 0;
569
	} else if (reg == 5) {
570
		return record_gif;
571
	} else if (reg == 6) {
572
		return wav_recorder_get_state();
573
	} else if (reg == 7) {
574
		return disable_emu_cmd_keys ? 1 : 0;
575
576
	} else if (reg == 8) {
577
		if (!debugOn)
578
			clock_snap = clockticks6502 - clock_base;
579
		return (clock_snap >> 0) & 0xff;
580
	} else if (reg == 9) {
581
		return (clock_snap >> 8) & 0xff;
582
	} else if (reg == 10) {
583
		return (clock_snap >> 16) & 0xff;
584
	} else if (reg == 11) {
585
		return (clock_snap >> 24) & 0xff;
586
587
	} else if (reg == 13) {
588
		return keymap;
589
	} else if (reg == 14) {
590
		return '1'; // emulator detection
591
	} else if (reg == 15) {
592
		return '6'; // emulator detection
593
	}
594
	if (!debugOn) printf("WARN: Invalid register %x\n", DEVICE_EMULATOR + reg);
595
	return -1;
596
}