OXYGENHUB
◆ main enhanced-x16-emulator / src / memory.c
20.9 KiB · C · last changed chore(): initial commit 25 days ago source blame history raw
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 "compat.h"
9
#include <time.h>
10
#include <inttypes.h>
11
#include "glue.h"
12
#include "dbg_load.h"
13
#include "via.h"
14
#include "memory.h"
15
#include "video.h"
16
#include "ymglue.h"
17
#include "cpu/fake6502.h"
18
#include "wav_recorder.h"
19
#include "audio.h"
20
#include "cartridge.h"
21
#include "iso_8859_15.h"
22
#include "midi.h"
23
#include "debug_core.h"
24
#include "debugger.h"
25
#include "io_trace.h"
26
#include "perf_budget.h"
27
28
uint8_t ram_bank;
29
uint8_t rom_bank;
30
31
uint8_t *RAM, *BRAM;
32
uint8_t ROM[ROM_SIZE];
33
extern uint8_t *CART;
34
35
static uint8_t addr_ym = 0;
36
37
// Defined below, next to the I/O dispatch it belongs to; declared here because
38
// memory_init() installs it.
39
static void io_trace_cpu_context(uint16_t *pc, uint8_t *pc_bank, uint32_t *cycles);
40
41
bool randomizeRAM = false;
42
bool reportUninitializedAccess = false;
43
const char *reportUsageStatisticsFilename = NULL;
44
bool *RAM_access_flags, *BRAM_access_flags;
45
uint64_t *RAM_system_reads;
46
uint64_t *RAM_system_writes;
47
uint64_t *RAM_banked_reads[256];
48
uint64_t *RAM_banked_writes[256];
49
uint64_t *ROM_banked_reads[256];
50
uint64_t *ROM_banked_writes[256];	// shouldn't occur for obvious reasons unless Bonk RAM is installed in a cart
51
52
53
static uint32_t clock_snap = 0UL;
54
static uint32_t clock_base = 0UL;
55
56
#define DEVICE_EMULATOR (0x9fb0)
57
58
void cpuio_write(uint8_t reg, uint8_t value);
59
60
void
61
memory_init()
62
{
63
	// The trace stamps each event with the PC and cycle count, which io_trace.c
64
	// cannot reach for itself without pulling the CPU into every target that
65
	// links memory.c -- including the two unit tests that pair it with fake
66
	// devices. Handing it a provider keeps that module dependency-free.
67
	io_trace_set_context_provider(io_trace_cpu_context);
68
69
	// Initialize RAM array
70
	RAM = calloc(RAM_SIZE, sizeof(uint8_t));
71
	BRAM = calloc(BRAM_SIZE, sizeof(uint8_t));
72
73
	if(reportUsageStatisticsFilename!=NULL) {
74
		RAM_system_reads = calloc(num_banks * BANK_SIZE, sizeof(uint64_t));
75
		RAM_system_writes = calloc(num_banks * BANK_SIZE, sizeof(uint64_t));
76
		for(int bank=0; bank<256; ++bank) {
77
			RAM_banked_reads[bank] = calloc(8192, sizeof(uint64_t));
78
			RAM_banked_writes[bank] = calloc(8192, sizeof(uint64_t));
79
			ROM_banked_reads[bank] = calloc(16384, sizeof(uint64_t));
80
			ROM_banked_writes[bank] = calloc(16384, sizeof(uint64_t));
81
		}
82
	}
83
84
85
	// Randomize all RAM (if option selected)
86
	if (randomizeRAM) {
87
		time_t t;
88
		srand((unsigned)time(&t));
89
		for (int i = 0; i < RAM_SIZE; i++) {
90
			if (i >= 0x9f00 && i < 0x10000) {
91
				// Leave the unused hole in the address space at 0
92
				// Memory dumps will likely confuse people less often
93
				RAM[i] = 0;
94
			} else {
95
				RAM[i] = rand();
96
			}
97
		}
98
		for (int i = 0; i < BRAM_SIZE; i++) {
99
			BRAM[i] = rand();
100
		}
101
	}
102
103
	// Initialize RAM access flag array (if option selected)
104
	if (reportUninitializedAccess) {
105
		RAM_access_flags = (bool*) malloc(RAM_SIZE * sizeof(bool));
106
		for (int i = 0; i < RAM_SIZE; i++) {
107
			RAM_access_flags[i] = false;
108
		}
109
		BRAM_access_flags = (bool*) malloc(BRAM_SIZE * sizeof(bool));
110
		for (int i = 0; i < BRAM_SIZE; i++) {
111
			BRAM_access_flags[i] = false;
112
		}
113
	}
114
115
	memory_reset();
116
}
117
118
void
119
memory_reset()
120
{
121
	// default banks are 0
122
	memory_set_ram_bank(0);
123
	memory_set_rom_bank(0);
124
}
125
126
void
127
memory_report_uninitialized_access(bool value)
128
{
129
	reportUninitializedAccess = value;
130
}
131
132
void
133
memory_report_usage_statistics(const char *filename) {
134
	reportUsageStatisticsFilename = filename;
135
}
136
137
void
138
memory_randomize_ram(bool value)
139
{
140
	randomizeRAM = value;
141
}
142
143
void
144
memory_initialize_cart(uint8_t *mem)
145
{
146
	if(randomizeRAM) {
147
		for(int i=0; i<0x4000; ++i) {
148
			mem[i] = rand();
149
		}
150
	} else {
151
		memset(mem, 0, 0x4000);
152
	}
153
}
154
155
//
156
// interface for fake6502
157
//
158
// if debugOn then reads memory only for debugger; no I/O, no side effects whatsoever
159
160
static const char *format_addr(uint16_t address, uint8_t bank, uint8_t x16Bank) {
161
	static char buffer[2 + 1 + 4 + 1];
162
163
	if (bank == 0 && address >= 0xA000) {
164
		if (address >= 0xA000) {
165
			snprintf(buffer, sizeof(buffer), "%02X:%04X", x16Bank, address);
166
		}
167
	}
168
	else if (is_gen2) {
169
		snprintf(buffer, sizeof(buffer), "%02X %04X", bank, address);
170
	} else {
171
		snprintf(buffer, sizeof(buffer), "%04X", address);
172
	}
173
174
	return buffer;
175
}
176
177
uint8_t
178
read6502(uint16_t address, uint8_t bank) {
179
	if (!is_gen2) bank = 0;
180
	// Report access to uninitialized RAM (if option selected)
181
	if (reportUninitializedAccess) {
182
		if (bank == 0) {
183
			uint8_t pc_x16_bank;
184
185
			if (opcode_addr < 0xa000) {
186
				pc_x16_bank = 0;
187
			} else if (opcode_addr < 0xc000) {
188
				pc_x16_bank = memory_get_ram_bank();
189
			} else {
190
				pc_x16_bank = memory_get_rom_bank();
191
			}
192
193
			if (address >= 0xa000 && address < 0xc000) {
194
				if (memory_get_ram_bank() < num_ram_banks && BRAM_access_flags[(memory_get_ram_bank() << 13) + address - 0xa000] == false){
195
					printf("Warning: %02X:%04X accessed uninitialized RAM address %02X:%04X\n", pc_x16_bank, opcode_addr, memory_get_ram_bank(), address);
196
				}
197
			}
198
		}
199
200
		if (bank != 0 || address < 0x9f00) {
201
			if (RAM_access_flags[bank * BANK_SIZE + address] == false) {
202
				printf("Warning: %s accessed uninitialized RAM address %02X %04X\n", format_addr(opcode_addr, regs.k, 0), bank, address);
203
			}
204
		}
205
	}
206
207
    if (reportUsageStatisticsFilename!=NULL) {
208
      if (bank != 0 || address < 0xa000) {
209
        RAM_system_reads[bank * BANK_SIZE + address]++;
210
      } else if (address < 0xc000) {
211
        RAM_banked_reads[memory_get_ram_bank()][address-0xa000]++;
212
      } else {
213
		ROM_banked_reads[rom_bank][address-0xc000]++;
214
      }
215
    }
216
217
	return real_read6502(address, bank, false, USE_CURRENT_X16_BANK);
218
}
219
220
// Which device an I/O-page address belongs to, for the trace. Kept here rather
221
// than in io_trace.c because the MIDI card's base address is configurable, so
222
// only the dispatch below actually knows the map.
223
//
224
// $9F3E/$9F3F are reported as IO_DEV_SPI, not IO_DEV_VERA: they are VERA
225
// registers, but they are also the sole path to the SD card, and VERA capture
226
// is off by default because its data ports would swamp the ring.
227
static io_device_t
228
io_trace_device_for(uint16_t address)
229
{
230
	if (address >= 0x9f00 && address < 0x9f10) {
231
		return IO_DEV_VIA1;
232
	} else if (has_via2 && address >= 0x9f10 && address < 0x9f20) {
233
		return IO_DEV_VIA2;
234
	} else if (address >= 0x9f3e && address < 0x9f40) {
235
		return IO_DEV_SPI;
236
	} else if (address >= 0x9f20 && address < 0x9f40) {
237
		return IO_DEV_VERA;
238
	} else if (address >= 0x9f40 && address < 0x9f60) {
239
		return (address & 0x01) != 0 ? IO_DEV_YM : IO_DEV_OPENBUS;
240
	} else if (address >= 0x9fb0 && address < 0x9fc0) {
241
		return IO_DEV_EMU;
242
	} else if (has_midi_card && (address & 0xfff0) == midi_card_addr) {
243
		return IO_DEV_MIDI;
244
	}
245
	return IO_DEV_OPENBUS;
246
}
247
248
// Stamps trace events with where the machine was when they happened. Installed
249
// from memory_init() so io_trace.c itself need not know about the CPU.
250
static void
251
io_trace_cpu_context(uint16_t *pc, uint8_t *pc_bank, uint32_t *cycles)
252
{
253
	*pc      = regs.pc;
254
	*pc_bank = regs.k;
255
	*cycles  = clockticks6502;
256
}
257
258
// The I/O page dispatch, lifted out of real_read6502() so the value it produces
259
// can be traced. Reads here have side effects -- VERA's data ports
260
// auto-increment, the SPI port consumes a byte -- so the caller records what
261
// this returned and never re-reads to find out.
262
static uint8_t
263
io_read(uint16_t address, bool debugOn)
264
{
265
	if (!debugOn && address >= 0x9fa0) {
266
		// slow IO5-7 range
267
		clockticks6502 += 3;
268
	}
269
	if (address >= 0x9f00 && address < 0x9f10) {
270
		return via1_read(address & 0xf, debugOn);
271
	} else if (has_via2 && (address >= 0x9f10 && address < 0x9f20)) {
272
		return via2_read(address & 0xf, debugOn);
273
	} else if (address >= 0x9f20 && address < 0x9f40) {
274
		return video_read(address & 0x1f, debugOn);
275
	} else if (address >= 0x9f40 && address < 0x9f60) {
276
		// slow IO2 range
277
		if (!debugOn) {
278
			clockticks6502 += 3;
279
		}
280
		if ((address & 0x01) != 0) { // partial decoding in this range
281
			// A debug read must not flush audio: audio_render() advances
282
			// the render write position, and the "no side effects"
283
			// guarantee is what lets the debugger sweep the I/O page.
284
			if (!debugOn) {
285
				audio_render();
286
			}
287
			return YM_read_status();
288
		}
289
		return 0x9f; // open bus read
290
	} else if (address >= 0x9fb0 && address < 0x9fc0) {
291
		// emulator state
292
		return emu_read(address & 0xf, debugOn);
293
	} else if (has_midi_card && (address & 0xfff0) == midi_card_addr) {
294
		// midi card
295
		return midi_serial_read(address & 0xf, debugOn);
296
	} else {
297
		// future expansion
298
		return 0x9f; // open bus read
299
	}
300
}
301
302
uint8_t
303
real_read6502(uint16_t address, uint8_t bank, bool debugOn, int16_t x16Bank)
304
{
305
	if (is_gen2 && bank != 0) { // RAM
306
		if (bank < num_banks) {
307
			return RAM[bank * BANK_SIZE + address];
308
		} else {
309
			return (address >> 8) & 0xff; // open bus read
310
		}
311
	}
312
313
	if (address < 0x9f00) { // RAM
314
		return RAM[address];
315
	} else if (address < 0xa000) { // I/O
316
		const uint8_t value = io_read(address, debugOn);
317
		// A debug read stays out of the trace entirely. The trace records what
318
		// the machine did; the debugger sweeping the I/O page to draw itself is
319
		// not the machine, and letting it in would mean the act of watching
320
		// filled the ring with its own observations.
321
		if (!debugOn) {
322
			const io_device_t dev = io_trace_device_for(address);
323
			if (io_trace_wants(dev)) {
324
				io_trace_access(dev, address, value, false);
325
			}
326
		}
327
		return value;
328
	} else if (address < 0xc000) { // banked RAM
329
		int ramBank = x16Bank >= 0 ? (uint8_t)x16Bank : memory_get_ram_bank();
330
		if (ramBank < num_ram_banks) {
331
			return BRAM[(ramBank << 13) + address - 0xa000];
332
		} else {
333
			return (address >> 8) & 0xff; // open bus read
334
		}
335
	} else { // banked ROM
336
		int romBank = x16Bank >= 0 ? (uint8_t)x16Bank : rom_bank;
337
		if (romBank < 32) {
338
			return ROM[(romBank << 14) + address - 0xc000];
339
		} else {
340
			if (!CART) {
341
				return (address >> 8) & 0xff; // open bus read
342
			}
343
			return cartridge_read(address, romBank);
344
		}
345
	}
346
}
347
348
void
349
write6502(uint16_t address, uint8_t bank, uint8_t value)
350
{
351
	real_write6502(address, bank, value, false);
352
}
353
354
void
355
real_write6502(uint16_t address, uint8_t bank, uint8_t value, bool debugOn)
356
{
357
	if (!is_gen2) bank = 0;
358
359
	if(reportUsageStatisticsFilename!=NULL) {
360
		if (bank != 0 || address < 0xa000) {
361
			RAM_system_writes[bank * BANK_SIZE + address]++;
362
		} else if (address < 0xc000) {
363
			RAM_banked_writes[memory_get_ram_bank()][address-0xa000]++;
364
		} else {
365
			// this is weird, but it does occur. And cartridges can install "Bonk RAM" in place of ROM.
366
			ROM_banked_writes[rom_bank][address-0xc000]++;
367
		}
368
	}
369
370
	// Update RAM access flag
371
	if (reportUninitializedAccess) {
372
		if (bank != 0 || address < 0xa000) {
373
			RAM_access_flags[bank * BANK_SIZE + address] = true;
374
		} else if (address < 0xc000) {
375
			if (memory_get_ram_bank() < num_ram_banks)
376
				BRAM_access_flags[(memory_get_ram_bank() << 13) + address - 0xa000] = true;
377
		}
378
	}
379
380
	// Memory write watchpoints. The scan is skipped entirely while none are
381
	// set, so this costs one comparison per store outside a debugging session.
382
	// Only bank 0 is watched: a non-zero program bank on Gen2 is flat RAM in a
383
	// separate address space, which a 16-bit watch address cannot name.
384
	//
385
	// A debug write is not a store the program made, so it reports nothing: a
386
	// watchpoint answers "when does the program touch this?", and breaking on
387
	// the debugger's own edit answers a question nobody asked.
388
	if (!debugOn && debug_wp_count() > 0 && bank == 0 && debug_wp_check_write(address, value)) {
389
		DEBUGBreakOnWatchpoint();
390
	}
391
392
	// A store is the signal the budget's spin detector runs on: a loop that
393
	// writes nothing is waiting, and a loop that writes is computing. A debug
394
	// write is excluded for the same reason it is excluded from watchpoints and
395
	// the I/O trace -- the debugger poking memory is not the program running.
396
	if (!debugOn) {
397
		perf_budget_note_store();
398
	}
399
400
	// Write to memory
401
	if (is_gen2 && bank != 0) {
402
		if (bank < num_banks) {
403
			RAM[bank * BANK_SIZE + address] = value;
404
		}
405
		return;
406
	}
407
408
	// Write to CPU I/O ports
409
	if (address < 2) {
410
		cpuio_write(address, value);
411
	}
412
	// Write to memory
413
	if (address < 0x9f00) { // RAM
414
		RAM[address] = value;
415
	} else if (address < 0xa000) { // I/O
416
		// Traced before dispatch, so that a decoded event the device emits in
417
		// response -- an SD command completing, a file being opened -- is
418
		// ordered after the write that caused it.
419
		//
420
		// A debug write stays out of the trace for the same reason a debug read
421
		// does: the trace records what the machine did, and the debugger poking
422
		// a register on the user's behalf is not the machine.
423
		if (!debugOn) {
424
			const io_device_t dev = io_trace_device_for(address);
425
			if (io_trace_wants(dev)) {
426
				io_trace_access(dev, address, value, true);
427
			}
428
		}
429
		if (!debugOn && address >= 0x9fa0) {
430
			// slow IO5-7 range
431
			clockticks6502 += 3;
432
		}
433
		if (address >= 0x9f00 && address < 0x9f10) {
434
			via1_write(address & 0xf, value);
435
		} else if (has_via2 && (address >= 0x9f10 && address < 0x9f20)) {
436
			via2_write(address & 0xf, value);
437
		} else if (address >= 0x9f20 && address < 0x9f40) {
438
			// video_write() has no debugOn parameter -- eighty-odd call sites
439
			// would have to change to give it one -- so the debugger announces
440
			// itself instead. VERA's bandwidth accounting must not charge the
441
			// guest for a poke the developer made from a memory view.
442
			video_set_debug_write(debugOn);
443
			video_write(address & 0x1f, value);
444
			video_set_debug_write(false);
445
		} else if (address >= 0x9f40 && address < 0x9f60) {
446
			// slow IO2 range
447
			if (!debugOn) {
448
				clockticks6502 += 3;
449
			}
450
			if ((address & 0x01) == 0) {   // YM reg (partially decoded)
451
				addr_ym = value;
452
			} else {                       // YM data (partially decoded)
453
				audio_render();
454
				YM_write_reg(addr_ym, value);
455
			}
456
		} else if (address >= 0x9fb0 && address < 0x9fc0) {
457
			// emulator state
458
			emu_write(address & 0xf, value);
459
		} else if (has_midi_card && (address & 0xfff0) == midi_card_addr) {
460
			midi_serial_write(address & 0xf, value);
461
		} else {
462
			// future expansion
463
		}
464
	} else if (address < 0xc000) { // banked RAM
465
		if (memory_get_ram_bank() < num_ram_banks) {
466
			BRAM[(memory_get_ram_bank() << 13) + address - 0xa000] = value;
467
		}
468
	} else { // ROM
469
		if (rom_bank >= 32) { // Cartridge ROM/RAM
470
			cartridge_write(address, rom_bank, value);
471
		}
472
		// ignore if base ROM (banks 0-31)
473
	}
474
}
475
476
void
477
vp6502()
478
{
479
	memory_set_rom_bank(0);
480
}
481
482
//
483
// saves the memory content into a file
484
//
485
486
void
487
memory_save(SDL_RWops *f, bool dump_ram, bool dump_bank)
488
{
489
	if (dump_ram) {
490
		SDL_RWwrite(f, &RAM[0], sizeof(uint8_t), is_gen2 ? num_banks * BANK_SIZE : 0xa000);
491
	}
492
	if (dump_bank) {
493
		SDL_RWwrite(f, &BRAM[0], sizeof(uint8_t), (num_ram_banks * 8192));
494
	}
495
}
496
497
498
void writestring(SDL_RWops *f, const char *string) {
499
	SDL_RWwrite(f, string, strlen(string), 1);
500
}
501
502
void memory_dump_usage_counts() {
503
	if(reportUsageStatisticsFilename==NULL)
504
		return;
505
506
	SDL_RWops *f = SDL_RWFromFile(reportUsageStatisticsFilename, "w");
507
	if (!f) {
508
		printf("Cannot write to %s!\n", reportUsageStatisticsFilename);
509
		return;
510
	}
511
512
	writestring(f, "Usage counts of all memory locations. Locations not printed have count zero.\n");
513
	writestring(f, "Tip: use 'sort -r -n -k 3' to sort it so it shows the most used at the top.\n");
514
	writestring(f, "\nsystem RAM reads:\n");
515
	int addr;
516
	char buf[100];
517
	for(addr=0; addr<num_banks * BANK_SIZE; ++addr) {
518
		if(RAM_system_reads[addr]>0) {
519
			SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "r %04x %" PRIu64 "\n", addr, RAM_system_reads[addr]), 1);
520
		}
521
	}
522
	writestring(f, "\nsystem RAM writes:\n");
523
	for(addr=0; addr<65536; ++addr) {
524
		if(RAM_system_writes[addr]>0) {
525
			SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "w %04x %" PRIu64 "\n", addr, RAM_system_writes[addr]), 1);
526
		}
527
	}
528
	writestring(f, "\nbanked RAM reads:\n");
529
	int bank;
530
	for(bank=0; bank<256; ++bank) {
531
		for(addr=0; addr<8192; ++addr) {
532
			if(RAM_banked_reads[bank][addr]>0) {
533
				SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "r %02x:%04x %" PRIu64 "\n", bank, addr+0xa000, RAM_banked_reads[bank][addr]), 1);
534
			}
535
		}
536
	}
537
	writestring(f, "\nbanked RAM writes:\n");
538
	for(bank=0; bank<256; ++bank) {
539
		for(addr=0; addr<8192; ++addr) {
540
			if(RAM_banked_writes[bank][addr]>0) {
541
				SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "w %02x:%04x %" PRIu64 "\n", bank, addr+0xa000, RAM_banked_writes[bank][addr]), 1);
542
			}
543
		}
544
	}
545
	writestring(f, "\nbanked ROM reads:\n");
546
	for(bank=0; bank<256; ++bank) {
547
		for(addr=0; addr<16384; ++addr) {
548
			if(ROM_banked_reads[bank][addr]>0) {
549
				SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "r %02x:%04x %" PRIu64 "\n", bank, addr+0xc000, ROM_banked_reads[bank][addr]), 1);
550
			}
551
		}
552
	}
553
	writestring(f, "\nbanked ROM / 'Bonk RAM' writes:\n");
554
	for(bank=0; bank<256; ++bank) {
555
		for(addr=0; addr<16384; ++addr) {
556
			if(ROM_banked_writes[bank][addr]>0) {
557
				SDL_RWwrite(f, buf, snprintf(buf, sizeof(buf), "w %02x:%04x %" PRIu64 "\n", bank, addr+0xc000, ROM_banked_writes[bank][addr]), 1);
558
			}
559
		}
560
	}
561
562
	SDL_RWclose(f);
563
}
564
565
566
///
567
///
568
///
569
570
inline void
571
memory_set_ram_bank(uint8_t bank)
572
{
573
	ram_bank = bank;
574
}
575
576
inline uint8_t
577
memory_get_ram_bank()
578
{
579
	return ram_bank;
580
}
581
582
inline void
583
memory_set_rom_bank(uint8_t bank)
584
{
585
	rom_bank = bank;
586
}
587
588
inline uint8_t
589
memory_get_rom_bank()
590
{
591
	return rom_bank;
592
}
593
594
void
595
cpuio_write(uint8_t reg, uint8_t value)
596
{
597
	switch (reg) {
598
		case 0:
599
			memory_set_ram_bank(value);
600
			break;
601
		case 1:
602
			memory_set_rom_bank(value);
603
			break;
604
	}
605
}
606
607
// Control the GIF recorder
608
void
609
emu_recorder_set(gif_recorder_command_t command)
610
{
611
	// turning off while recording is enabled
612
	if (command == RECORD_GIF_PAUSE && record_gif != RECORD_GIF_DISABLED) {
613
		record_gif = RECORD_GIF_PAUSED; // need to save
614
	}
615
	// turning on continuous recording
616
	if (command == RECORD_GIF_RESUME && record_gif != RECORD_GIF_DISABLED) {
617
		record_gif = RECORD_GIF_ACTIVE;		// activate recording
618
	}
619
	// capture one frame
620
	if (command == RECORD_GIF_SNAP && record_gif != RECORD_GIF_DISABLED) {
621
		record_gif = RECORD_GIF_SINGLE;		// single-shot
622
	}
623
}
624
625
//
626
// read/write emulator state (feature flags)
627
//
628
// 0: debugger_enabled
629
// 1: log_video
630
// 2: log_keyboard
631
// 3: echo_mode
632
// 4: save_on_exit
633
// 5: record_gif
634
// 6: record_wav
635
// 7: cmd key toggle
636
// 8: write: reset cpu clock counter
637
// 8: read: snapshots cpu clock counter and reads the LSB bits 0-7
638
// 9: write: output debug byte 1
639
// 9: read: cpu clock bits 8-15
640
// 10: write: output debug byte 2
641
// 10: read: cpu clock bits 16-23
642
// 11: write: write character to STDOUT of console
643
// 11: read: cpu clock MSB bits 24-31
644
// 12: write: performance budget marker (see perf_budget.h for the protocol)
645
// 12: read: marker protocol version, so guest code can feature-detect
646
// POKE $9FB3,1:PRINT"ECHO MODE IS ON":POKE $9FB3,0
647
void
648
emu_write(uint8_t reg, uint8_t value)
649
{
650
	bool v = value != 0;
651
	switch (reg) {
652
		// Also told to dbg_load: it follows the debugger by default, and the
653
		// machine can switch the debugger on here long after startup. The
654
		// graphical debugger counts as "a debugger is on" too, so a guest
655
		// clearing this register must not switch .dbg loading off underneath
656
		// a running -imgui session.
657
		case 0: debugger_enabled = v; dbg_load_note_debugger(v || imgui_debugger_enabled); break;
658
		case 1: log_video = v; break;
659
		case 2: log_keyboard = v; break;
660
		case 3: echo_mode = value; break;
661
		case 4: save_on_exit = v; break;
662
		case 5: emu_recorder_set((gif_recorder_command_t) value); break;
663
		case 6: wav_recorder_set((wav_recorder_command_t) value); break;
664
		case 7: disable_emu_cmd_keys = v; break;
665
		case 8: clock_base = clockticks6502; break;
666
		case 9: printf("User debug 1: $%02x\n", value); fflush(stdout); break;
667
		case 10: printf("User debug 2: $%02x\n", value); fflush(stdout); break;
668
		case 11: {
669
			if (value == 0x09 || value == 0x0a || value == 0x0d || (value >= 0x20 && value < 0x7f)) {
670
				printf("%c", value);
671
			} else if (value >= 0xa1) {
672
				print_iso8859_15_char((char) value);
673
			} else {
674
				printf("\xef\xbf\xbd"); // �
675
			}
676
			fflush(stdout);
677
			break;
678
		}
679
		case 12: perf_budget_marker_write(value); break;
680
		default: printf("WARN: Invalid register %x\n", DEVICE_EMULATOR + reg);
681
	}
682
}
683
684
uint8_t
685
emu_read(uint8_t reg, bool debugOn)
686
{
687
	if (reg == 0) {
688
		return debugger_enabled ? 1 : 0;
689
	} else if (reg == 1) {
690
		return log_video ? 1 : 0;
691
	} else if (reg == 2) {
692
		return log_keyboard ? 1 : 0;
693
	} else if (reg == 3) {
694
		return echo_mode;
695
	} else if (reg == 4) {
696
		return save_on_exit ? 1 : 0;
697
	} else if (reg == 5) {
698
		return record_gif;
699
	} else if (reg == 6) {
700
		return wav_recorder_get_state();
701
	} else if (reg == 7) {
702
		return disable_emu_cmd_keys ? 1 : 0;
703
704
	} else if (reg == 8) {
705
		if (!debugOn)
706
			clock_snap = clockticks6502 - clock_base;
707
		return (clock_snap >> 0) & 0xff;
708
	} else if (reg == 9) {
709
		return (clock_snap >> 8) & 0xff;
710
	} else if (reg == 10) {
711
		return (clock_snap >> 16) & 0xff;
712
	} else if (reg == 11) {
713
		return (clock_snap >> 24) & 0xff;
714
715
	} else if (reg == 12) {
716
		return perf_budget_marker_read();
717
	} else if (reg == 13) {
718
		return keymap;
719
	} else if (reg == 14) {
720
		return '1'; // emulator detection
721
	} else if (reg == 15) {
722
		return '6'; // emulator detection
723
	}
724
	if (!debugOn) printf("WARN: Invalid register %x\n", DEVICE_EMULATOR + reg);
725
	return -1;
726
}