OXYGENHUB
◆ riscv-1 x16-emulator / src / main.c
50.8 KiB · C · last changed main: while the CPU waits in WAI, step the machine to its next event at once 15 days ago source blame history raw
03a3459e Andy McCall 1
// Commander X16 Emulator
2
// Copyright (c) 2019,2022 Michael Steil
3
// All rights reserved. License: 2-clause BSD
4
5
#ifndef __APPLE__
6
#define _XOPEN_SOURCE   600
7
#define _POSIX_C_SOURCE 1
8
#endif
9
#include <stdlib.h>
10
#include <stdbool.h>
11
#include <stdint.h>
12
#include <stdio.h>
13
#include <string.h>
14
#include <unistd.h>
15
#include <limits.h>
16
#ifdef __MINGW32__
17
#include <ctype.h>
18
#endif
19
#include "cpu/fake6502.h"
20
#include "timing.h"
21
#include "disasm.h"
22
#include "files.h"
23
#include "memory.h"
24
#include "video.h"
25
#include "via.h"
26
#include "serial.h"
27
#include "i2c.h"
28
#include "rtc.h"
29
#include "smc.h"
30
#include "vera_spi.h"
31
#include "sdcard.h"
32
#include "ieee.h"
33
#include "glue.h"
34
#include "debugger.h"
35
#include "utf8.h"
36
#include "iso_8859_15.h"
37
#include "joystick.h"
38
#include "rom_symbols.h"
39
#include "ymglue.h"
40
#include "audio.h"
41
#include "version.h"
42
#include "wav_recorder.h"
43
#include "testbench.h"
44
#include "coverage.h"
45
#include "cartridge.h"
46
#include "midi.h"
47
#include "netcard.h"
48
#include "retro_control.h"
49
50
int x16_control_start(int port);   // control_backend.c
51
52
#ifdef __EMSCRIPTEN__
53
#include <emscripten.h>
54
#include <pthread.h>
55
#endif
56
57
void *emulator_loop(void *param);
58
void emscripten_main_loop(void);
59
60
// This must match the KERNAL's set!
61
char *keymaps[] = {
62
	"en-us",
63
	"en-us-int",
64
	"en-gb",
65
	"sv",
66
	"de",
67
	"da",
68
	"it",
69
	"pl",
70
	"nb",
71
	"hu",
72
	"es",
73
	"fi",
74
	"pt-br",
75
	"cz",
76
	"jp",
77
	"fr",
78
	"de-ch",
79
	"en-us-dvo",
80
	"et",
81
	"fr-be",
82
	"fr-ca",
83
	"is",
84
	"pt",
85
	"hr",
86
	"sk",
87
	"sl",
88
	"lv",
89
	"lt",
90
};
91
92
#ifdef PERFSTAT
93
uint32_t stat[65536];
94
#endif
95
96
bool debugger_enabled = false;
97
char *paste_text = NULL;
98
char *clipboard_buffer = NULL;
99
char paste_text_data[65536];
100
bool pasting_bas = false;
101
102
uint16_t num_banks = 1;
103
uint16_t num_ram_banks = 64; // 512 KB default
104
105
bool log_video = false;
106
bool log_speed = false;
107
bool log_keyboard = false;
108
bool dump_cpu = false;
109
bool dump_ram = true;
110
bool dump_bank = true;
111
bool dump_vram = false;
112
int control_port = 0;   // retro-remote-debug-controller HTTP port (0 = off)
113
bool warp_mode = false;
114
bool warp_pastes = false;
115
bool grab_mouse = false;
116
echo_mode_t echo_mode;
117
bool save_on_exit = true;
118
bool disable_emu_cmd_keys = false;
119
bool set_system_time = false;
120
bool has_serial = false;
121
bool no_ieee_intercept = false;
122
bool has_via2 = false;
123
gif_recorder_state_t record_gif = RECORD_GIF_DISABLED;
124
char *gif_path = NULL;
125
char *wav_path = NULL;
126
uint8_t *fsroot_path = NULL;
127
uint8_t *startin_path = NULL;
128
uint8_t keymap = 0; // KERNAL's default
129
int window_scale = 1;
130
float screen_x_scale = 1.0;
131
float window_opacity = 1.0;
132
char *scale_quality = "best";
133
bool test_init_complete=false;
134
bool headless = false;
135
bool fullscreen = false;
136
bool testbench = false;
137
bool enable_midline = false;
138
bool ym2151_irq_support = false;
139
char *cartridge_path = NULL;
140
141
bool has_midi_card = false;
142
uint16_t midi_card_addr;
143
144
bool using_hostfs = true;
145
146
uint8_t MHZ = 8;
147
148
#ifdef TRACE
149
bool trace_mode = false;
150
uint16_t trace_address = 0;
151
#endif
152
153
int instruction_counter;
154
SDL_RWops *prg_file;
155
bool prg_finished_loading;
156
int prg_override_start = -1;
157
bool run_after_load = false;
158
159
char *nvram_path = NULL;
160
161
bool pwr_long_press=false;
162
bool is_gen2 = false;
163
164
#ifdef TRACE
165
#include "rom_labels.h"
166
#include "rom_lst.h"
167
char *
168
label_for_address(uint16_t address)
169
{
170
	uint16_t *addresses;
171
	char **labels;
172
	int count;
173
	switch (memory_get_rom_bank()) {
174
		case 0:
175
			addresses = addresses_bank0;
176
			labels = labels_bank0;
177
			count = sizeof(addresses_bank0) / sizeof(uint16_t);
178
			break;
179
		case 1:
180
			addresses = addresses_bank1;
181
			labels = labels_bank1;
182
			count = sizeof(addresses_bank1) / sizeof(uint16_t);
183
			break;
184
		case 2:
185
			addresses = addresses_bank2;
186
			labels = labels_bank2;
187
			count = sizeof(addresses_bank2) / sizeof(uint16_t);
188
			break;
189
		case 3:
190
			addresses = addresses_bank3;
191
			labels = labels_bank3;
192
			count = sizeof(addresses_bank3) / sizeof(uint16_t);
193
			break;
194
		case 4:
195
			addresses = addresses_bank4;
196
			labels = labels_bank4;
197
			count = sizeof(addresses_bank4) / sizeof(uint16_t);
198
			break;
199
		case 5:
200
			addresses = addresses_bank5;
201
			labels = labels_bank5;
202
			count = sizeof(addresses_bank5) / sizeof(uint16_t);
203
			break;
204
		case 6:
205
			addresses = addresses_bank6;
206
			labels = labels_bank6;
207
			count = sizeof(addresses_bank6) / sizeof(uint16_t);
208
			break;
209
		case 10:
210
			addresses = addresses_bankA;
211
			labels = labels_bankA;
212
			count = sizeof(addresses_bankA) / sizeof(uint16_t);
213
			break;
214
		case 11:
215
			addresses = addresses_bankB;
216
			labels = labels_bankB;
217
			count = sizeof(addresses_bankB) / sizeof(uint16_t);
218
			break;
219
		case 12:
220
			addresses = addresses_bankC;
221
			labels = labels_bankC;
222
			count = sizeof(addresses_bankC) / sizeof(uint16_t);
223
			break;
224
		case 13:
225
			addresses = addresses_bankD;
226
			labels = labels_bankD;
227
			count = sizeof(addresses_bankD) / sizeof(uint16_t);
228
			break;
229
		case 14:
230
			addresses = addresses_bankE;
231
			labels = labels_bankE;
232
			count = sizeof(addresses_bankE) / sizeof(uint16_t);
233
			break;
234
		case 15:
235
			addresses = addresses_bankF;
236
			labels = labels_bankF;
237
			count = sizeof(addresses_bankF) / sizeof(uint16_t);
238
			break;
239
		default:
240
			addresses = NULL;
241
			labels = NULL;
242
	}
243
244
	if (!addresses) {
245
		return NULL;
246
	}
247
248
	for (int i = 0; i < count; i++) {
249
		if (address == addresses[i]) {
250
			return labels[i];
251
		}
252
	}
253
	return NULL;
254
}
255
256
char *
257
lst_for_address(uint16_t address)
258
{
259
	if (address < 0xc000) {
260
		return NULL;
261
	}
262
263
	char **lst;
264
	switch (memory_get_rom_bank()) {
265
		case 0: lst = lst_bank0; break;
266
		case 2: lst = lst_bank2; break;
267
		case 3: lst = lst_bank3; break;
268
		case 4: lst = lst_bank4; break;
269
		case 5: lst = lst_bank5; break;
270
		case 7: lst = lst_bank7; break;
271
		case 8: lst = lst_bank8; break;
272
		case 9: lst = lst_bank9; break;
273
		case 10: lst = lst_bankA; break;
274
		case 11: lst = lst_bankB; break;
275
		case 12: lst = lst_bankC; break;
276
		case 13: lst = lst_bankD; break;
277
		case 14: lst = lst_bankE; break;
278
		case 15: lst = lst_bankF; break;
279
		default:
280
			return NULL;
281
	}
282
	return lst[address - 0xc000];
283
}
284
#endif
285
286
void
287
machine_dump(const char* reason)
288
{
289
	printf("Dumping system memory. Reason: %s\n", reason);
290
	int index = 0;
291
	char filename[22];
292
	for (;;) {
293
		if (!index) {
294
			strcpy(filename, "dump.bin");
295
		} else {
296
			sprintf(filename, "dump-%i.bin", index);
297
		}
298
		if (access(filename, F_OK) == -1) {
299
			break;
300
		}
301
		index++;
302
	}
303
	SDL_RWops *f = SDL_RWFromFile(filename, "wb");
304
	if (!f) {
305
		printf("Cannot write to %s!\n", filename);
306
		return;
307
	}
308
309
	if (dump_cpu) {
310
		SDL_RWwrite(f, &regs.a, sizeof(uint8_t), 1);
311
		SDL_RWwrite(f, &regs.xl, sizeof(uint8_t), 1);
312
		SDL_RWwrite(f, &regs.yl, sizeof(uint8_t), 1);
313
		SDL_RWwrite(f, &regs.sp, sizeof(uint8_t), 1);
314
		SDL_RWwrite(f, &regs.status, sizeof(uint8_t), 1);
315
		SDL_RWwrite(f, &regs.pc, sizeof(uint16_t), 1);
316
	}
317
	memory_save(f, dump_ram, dump_bank);
318
319
	if (dump_vram) {
320
		video_save(f);
321
	}
322
323
	SDL_RWclose(f);
324
	printf("Dumped system to %s.\n", filename);
325
}
326
327
void mouse_state_init(void)
328
{
329
	kernal_mouse_enabled = 0;
330
	SDL_ShowCursor((mouse_grabbed || kernal_mouse_enabled) ? SDL_DISABLE : SDL_ENABLE);
331
}
332
333
void
334
machine_reset()
335
{
336
	i2c_reset_state();
337
	ieee_init();
338
	memory_reset();
339
	vera_spi_init();
340
	via1_init();
341
	if (has_via2) {
342
		via2_init();
343
	}
344
	video_reset();
345
	mouse_state_init();
346
	reset6502(regs.is65c816);
347
	midi_serial_init();
348
	netcard_init();
349
}
350
351
void
352
machine_nmi()
353
{
354
	nmi6502();
355
}
356
357
void
358
machine_paste(char *s, bool handle_free)
359
{
360
	if (s) {
361
		paste_text = s;
362
		if (handle_free) {
363
			clipboard_buffer = s; // so that we can free this later
364
		}
365
		pasting_bas = true;
366
		if (warp_pastes) warp_mode = true;
367
	}
368
}
369
370
void
371
machine_toggle_warp()
372
{
373
	warp_mode = !warp_mode;
374
	timing_init();
375
}
376
377
378
static bool
379
is_kernal()
380
{
381
	// only for KERNAL
382
	return (debug_read6502(0xfff6, 0, USE_CURRENT_X16_BANK) == 'M' &&
383
			debug_read6502(0xfff7, 0, USE_CURRENT_X16_BANK) == 'I' &&
384
			debug_read6502(0xfff8, 0, USE_CURRENT_X16_BANK) == 'S' &&
385
			debug_read6502(0xfff9, 0, USE_CURRENT_X16_BANK) == 'T')
386
		|| (debug_read6502(0xc008, 0, USE_CURRENT_X16_BANK) == 'M' &&
387
			debug_read6502(0xc009, 0, USE_CURRENT_X16_BANK) == 'I' &&
388
			debug_read6502(0xc00a, 0, USE_CURRENT_X16_BANK) == 'S' &&
389
			debug_read6502(0xc00b, 0, USE_CURRENT_X16_BANK) == 'T');
390
}
391
392
static void
393
usage()
394
{
395
	printf("\nCommander X16 Emulator r%s (%s)", VER, VER_NAME);
396
#ifdef GIT_REV
397
	printf(", "GIT_REV);
398
#endif
399
	printf("\n(C)2019, 2023 Michael Steil et al.\n");
400
	printf("All rights reserved. License: 2-clause BSD\n\n");
401
	printf("Usage: x16emu [option] ...\n\n");
402
	printf("-rom <rom.bin>\n");
403
	printf("\tOverride KERNAL/BASIC/* ROM file.\n");
404
	printf("-ram <ramsize>\n");
405
	printf("\tSpecify banked RAM size in KB (8, 16, 32, ..., 2048).\n");
406
	printf("\tThe default is 512.\n");
407
	printf("-nvram <nvram.bin>\n");
408
	printf("\tSpecify NVRAM image. By default, the machine starts with\n");
409
	printf("\tempty NVRAM and does not save it to disk.\n");
410
	printf("-keymap <keymap>\n");
411
	printf("\tEnable a specific keyboard layout decode table.\n");
412
	printf("-sdcard <sdcard.img>\n");
413
	printf("\tSpecify SD card image (partition map + FAT32)\n");
414
	printf("-cart <crtfile.crt>\n");
415
	printf("\tLoads a specially-formatted cartridge file.\n");
416
	printf("-cartbin <romfile.bin>\n");
417
	printf("\tLoads a raw cartridge file starting at ROM bank 32. After\n");
418
	printf("\tloading, all of the affected banks will function as RAM.\n");
419
	printf("-serial\n");
420
	printf("\tConnect host fs through Serial Bus [experimental]\n");
421
	printf("-nohostieee / -nohostfs\n");
422
	printf("\tDisable HostFS through IEEE API interception.\n");
423
	printf("\tIEEE API HostFS is normally enabled unless -sdcard or\n");
424
	printf("\t-serial is specified.\n");
425
	printf("-hostfsdev <unit>\n");
426
	printf("\tSet the HostFS IEEE device number. Range 8-31. Default: %d.\n", ieee_unit);
427
	printf("-fsroot <directory>\n");
428
	printf("\tSpecify the host filesystem directory path which is to\n");
429
	printf("\tact as the emulated root directory of the Commander X16.\n");
430
	printf("\tDefault is the current working directory.\n");
431
	printf("-startin <directory>\n");
432
	printf("\tSpecify the host filesystem directory path that the\n");
433
	printf("\temulated filesystem starts in. Default is the current\n");
434
	printf("\tworking directory if it lies within the hierarchy of fsroot,\n");
435
	printf("\totherwise it defaults to fsroot itself.\n");
436
	printf("-noemucmdkeys\n");
437
	printf("\tDisable emulator command keys.\n");
438
	printf("-capture\n");
439
	printf("\tStart emulator with mouse/keyboard captured.\n");
440
	printf("-nokeyboardcapture\n");
441
	printf("\tWhile in capture mode, causes the emulator not to intercept\n");
442
	printf("\tkeyboard combinations which are used by the operating system,\n");
443
	printf("\tsuch as Alt+Tab.\n");
444
	printf("-prg <app.prg>[,<load_addr>]\n");
445
	printf("\tLoad application from the *host filesystem* into RAM,\n");
446
	printf("\teven if an SD card is attached.\n");
447
	printf("\tThe override load address is hex without a prefix.\n");
448
	printf("-bas <app.txt>\n");
449
	printf("\tInject a BASIC program in ASCII encoding through the\n");
450
	printf("\tkeyboard.\n");
451
	printf("-run\n");
452
	printf("\tStart the -prg/-bas program using RUN\n");
453
	printf("-warp\n");
454
	printf("\tEnable warp mode, run emulator as fast as possible.\n");
455
	printf("-pastewarp\n");
456
	printf("\tEnable warp mode during pastes and during loading via -bas.\n");
457
	printf("-echo [{iso|raw}]\n");
458
	printf("\tPrint all KERNAL output to the host's stdout.\n");
459
	printf("\tBy default, everything but printable ASCII characters get\n");
460
	printf("\tescaped. \"iso\" will escape everything but non-printable\n");
461
	printf("\tISO-8859-15 characters and convert the output to UTF-8.\n");
462
	printf("\t\"raw\" will not do any substitutions.\n");
463
	printf("\tWith the BASIC statement \"LIST\", this can be used\n");
464
	printf("\tto detokenize a BASIC program.\n");
465
	printf("-log {K|S|V}...\n");
466
	printf("\tEnable logging of (K)eyboard, (S)peed, (V)ideo.\n");
467
	printf("\tMultiple characters are possible, e.g. -log KS\n");
468
	printf("-gif <file.gif>[,wait]\n");
469
	printf("\tRecord a gif for the video output.\n");
470
	printf("\tUse ,wait to start paused.\n");
471
	printf("\tPOKE $9FB5,2 to start recording.\n");
472
	printf("\tPOKE $9FB5,1 to capture a single frame.\n");
473
	printf("\tPOKE $9FB5,0 to pause.\n");
474
	printf("-wav <file.wav>[{,wait|,auto}]\n");
475
	printf("\tRecord a wav for the audio output.\n");
476
	printf("\tUse ,wait to start paused, or ,auto to start paused and automatically begin recording on the first non-zero audio signal.\n");
477
	printf("\tPOKE $9FB6,2 to automatically begin recording on the first non-zero audio signal.\n");
478
	printf("\tPOKE $9FB6,1 to begin recording immediately.\n");
479
	printf("\tPOKE $9FB6,0 to pause.\n");
480
	printf("-scale {1|2|3|4}\n");
481
	printf("\tScale output to an integer multiple of 640x480\n");
482
	printf("-quality {nearest|linear|best}\n");
483
	printf("\tScaling algorithm quality\n");
484
	printf("-widescreen\n");
485
	printf("\tStretch output to 16:9 resolution to mimic display of a widescreen monitor.\n");
486
	printf("-fullscreen\n");
487
	printf("\tStart up in fullscreen mode instead of in a window.\n");
488
	printf("-opacity (0.0,...,1.0)\n");
489
	printf("\tSet the opacity value (0.0 for transparent, 1.0 for opaque) of the window. (default: %.1f)\n", window_opacity);
490
	printf("-debug [<address>]\n");
491
	printf("\tEnable debugger. Optionally, set a breakpoint\n");
492
	printf("-randram\n");
493
	printf("\t(deprecated, no effect)\n");
494
	printf("-zeroram\n");
495
	printf("\tSet all RAM to zero instead of uninitialized random values\n");
496
	printf("-wuninit\n");
497
	printf("\tPrints warning to stdout if uninitialized RAM is accessed\n");
498
	printf("-memorystats <file.txt>\n");
499
	printf("\tSaves memory access statistics to the given file when emulator exits\n");
500
	printf("-coverage <file>\n");
501
	printf("\tRecord every address an instruction runs from, with its bank,\n");
502
	printf("\tand write them to the given file on exit or SIGTERM\n");
503
	printf("-dump {C|R|B|V}...\n");
504
	printf("\tConfigure system dump: (C)PU, (R)AM, (B)anked-RAM, (V)RAM\n");
505
	printf("\tMultiple characters are possible, e.g. -dump CV ; Default: RB\n");
506
	printf("-joy1\n");
507
	printf("\tEnable binding a gamepad to SNES controller port 1\n");
508
	printf("-joy2\n");
509
	printf("\tEnable binding a gamepad to SNES controller port 2\n");
510
	printf("-joy3\n");
511
	printf("\tEnable binding a gamepad to SNES controller port 3\n");
512
	printf("-joy4\n");
513
	printf("\tEnable binding a gamepad to SNES controller port 4\n");
514
	printf("-sound <output device>\n");
515
	printf("\tSet the output device used for audio emulation\n");
516
	printf("\tIf output device is 'none', no audio is generated\n");
517
	printf("-abufs <number of audio buffers>\n");
518
	printf("\tSet the number of audio buffers used for playback.\n");
519
	printf("\tIf using HostFS, the default is 32, otherwise 8.\n");
520
	printf("\tIncreasing this will reduce stutter on slower computers,\n");
521
	printf("\tbut will increase audio latency.\n");
522
	printf("-rtc\n");
523
	printf("\tSet the real-time-clock to the current system time and date.\n");
524
	printf("-via2\n");
525
	printf("\tInstall the second VIA chip expansion at $9F10\n");
526
	printf("-testbench\n");
527
	printf("\tHeadless mode for unit testing with an external test runner\n");
528
	printf("-mhz <integer>\n");
529
	printf("\tRun the emulator with a system clock speed other than the default of\n");
530
	printf("\t8 MHz. Valid values are in the range of 1-40, inclusive. This option\n");
531
	printf("\tis meant mainly for benchmarking, and may not reflect accurate\n");
532
	printf("\thardware behavior.\n");
533
	printf("-midline-effects\n");
534
	printf("\tApproximate mid-line raster effects when changing tile, sprite,\n");
535
	printf("\tand palette data. Requires a fast host CPU.\n");
536
	printf("-enable-ym2151-irq\n");
537
	printf("\tConnect the YM2151 IRQ source to the emulated CPU. This option increases\n");
538
	printf("\tCPU usage as audio render is triggered for every CPU instruction.\n");
539
	printf("-c02\n");
540
	printf("\tRun the emulator under an emulated 65C02 (default)\n");
541
	printf("-c816\n");
542
	printf("\tRun the emulator under an emulated 65C816\n");
543
	printf("\tThis option is experimental.\n");
544
	printf("-rockwell\n");
545
	printf("\tSuppress warning emitted when encountering a Rockwell extension on the 65C02\n");
546
	printf("-longpwron\n");
547
	printf("\tSimulate a long press of the power button at system power-on.\n");
548
	printf("-netcard [<address>]\n");
549
	printf("\tInstall a virtual serial/network card at the specified address, or at $9F60\n");
550
	printf("\tby default: a 16450 UART with a Hayes modem behind it whose dial opens a\n");
551
	printf("\treal TCP connection from the emulator (ATD\"host:port\"). For testing\n");
552
	printf("\tprograms written against the TexElec serial/ESP32 card.\n");
553
	printf("-midicard [<address>]\n");
554
	printf("\tInstall a serial MIDI card at the specified address, or at $9F60 by default.\n");
555
	printf("\tThe -sf2 option must be specified along with this option.\n");
556
	printf("-sf2 <SoundFont filename>\n");
557
	printf("\tInitialize MIDI synth with the specified SoundFont.\n");
558
	printf("\tThe -midicard option must be specified along with this option.\n");
559
	printf("-midi-in\n");
560
	printf("\tConnect the system MIDI input devices to the input of the first UART\n");
561
	printf("\tof the emulated MIDI card. The -midicard option is required for this\n");
562
	printf("\toption to have any effect.\n");
563
#ifdef TRACE
564
	printf("-trace [<address>]\n");
565
	printf("\tPrint instruction trace. Optionally, a trigger address\n");
566
	printf("\tcan be specified.\n");
567
#endif
568
	printf("-version\n");
569
	printf("\tPrint additional version information of the emulator and ROM.\n");
570
	printf("\n");
571
	exit(1);
572
}
573
574
void
575
usage_keymap()
576
{
577
	printf("The following keymaps are supported:\n");
578
	for (int i = 0; i < sizeof(keymaps)/sizeof(*keymaps); i++) {
579
		printf("\t%s\n", keymaps[i]);
580
	}
581
	exit(1);
582
}
583
584
void no_fluidsynth_warning(void)
585
{
586
	static bool already_warned;
587
588
	if (!already_warned) {
589
		fprintf(stderr, "\nWarning: x16emu was built without FluidSynth support,\n");
590
		fprintf(stderr, "so the MIDI synth will be inoperative.\n\n");
591
#if defined(__linux__)
592
		fprintf(stderr, "To build x16emu with fluidsynth support, you distro may\n");
593
		fprintf(stderr, "have a libfluidsynth-dev or fluidsynth-devel package that\n");
594
		fprintf(stderr, "needs to be installed before building x16emu.\n\n");
595
#elif defined(__APPLE__)
596
		fprintf(stderr, "To build x16emu with fluidsynth support,\n");
597
		fprintf(stderr, "install the homebrew package fluid-synth before\n");
598
		fprintf(stderr, "building x16emu.\n\n");
599
#elif defined(_WIN64)
600
		fprintf(stderr, "To build x16emu with fluidsynth support under MSYS2,\n");
601
		fprintf(stderr, "install the mingw-w64-x86_64-fluidsynth package before\n");
602
		fprintf(stderr, "building x16emu.\n\n");
603
#elif defined(_WIN32)
604
		fprintf(stderr, "To build x16emu with fluidsynth support under MSYS2,\n");
605
		fprintf(stderr, "install the mingw-w64-i686-fluidsynth package before\n");
606
		fprintf(stderr, "building x16emu.\n\n");
607
#endif
608
		fprintf(stderr, "Then build x16emu with FLUIDSYNTH=1. For example:\n");
609
		fprintf(stderr, "FLUIDSYNTH=1 make\n");
610
		already_warned = true;
611
	}
612
}
613
614
int
615
main(int argc, char **argv)
616
{
617
	char *rom_filename = "rom.bin";
618
	char rom_path_data[PATH_MAX];
619
620
	char *rom_path = rom_path_data;
621
	char *prg_path = NULL;
622
	char *bas_path = NULL;
623
	char *sf2_path = NULL;
624
	char *sdcard_path = NULL;
625
	bool run_test = false;
626
	int test_number = 0;
627
	int audio_buffers = 8;
628
	bool zeroram = false;
629
	bool audio_buffers_set = false;
630
	bool hostfs_set = false;
631
632
	const char *audio_dev_name = NULL;
633
634
	run_after_load = false;
635
636
	char *base_path = SDL_GetBasePath();
637
638
	// This causes the emulator to load ROM data from the executable's directory when
639
	// no ROM file is specified on the command line.
640
	memcpy(rom_path, base_path, strlen(base_path) + 1);
641
	strncpy(rom_path + strlen(rom_path), rom_filename, PATH_MAX - strlen(rom_path));
642
	memory_randomize_ram(true);
643
644
	argc--;
645
	argv++;
646
647
	while (argc > 0) {
648
		if (!strcmp(argv[0], "-rom")) {
649
			argc--;
650
			argv++;
651
			if (!argc || argv[0][0] == '-') {
652
				usage();
653
			}
654
			rom_path = argv[0];
655
			argc--;
656
			argv++;
657
		} else if (!strcmp(argv[0], "-ram")) {
658
			argc--;
659
			argv++;
660
			if (!argc || argv[0][0] == '-') {
661
				usage();
662
			}
663
			int kb = atoi(argv[0]);
664
			if (!((kb & 7)==0) || kb < 8 || kb > 2048) {
665
				printf("-ram value must be a multiple of 8 in the range of 8-2048.\n");
666
				exit(1);
667
			}
668
			num_ram_banks = kb /8;
669
			argc--;
670
			argv++;
671
		} else if (!strcmp(argv[0], "-keymap")) {
672
			argc--;
673
			argv++;
674
			if (!argc || argv[0][0] == '-') {
675
				usage_keymap();
676
			}
677
			bool found = false;
678
			for (int i = 0; i < sizeof(keymaps)/sizeof(*keymaps); i++) {
679
				if (!strcmp(argv[0], keymaps[i])) {
680
					found = true;
681
					keymap = i;
682
				}
683
			}
684
			if (!found) {
685
				usage_keymap();
686
			}
687
			argc--;
688
			argv++;
689
		} else if (!strcmp(argv[0], "-prg")) {
690
			argc--;
691
			argv++;
692
			if (!argc || argv[0][0] == '-') {
693
				usage();
694
			}
695
			prg_path = argv[0];
696
			argc--;
697
			argv++;
698
		} else if (!strcmp(argv[0], "-netcard")) {
699
			argc--;
700
			argv++;
701
			has_netcard = true;
702
			if (argc && argv[0][0] != '-') {
703
				netcard_addr = 0x9f00 | ((uint16_t)strtol(argv[0], NULL, 16) & 0xff);
704
				netcard_addr &= 0xfff0;
705
				argc--;
706
				argv++;
707
			} else {
708
				netcard_addr = 0x9f60;
709
			}
710
		} else if (!strcmp(argv[0], "-midicard")) {
711
#ifndef HAS_FLUIDSYNTH
712
			no_fluidsynth_warning();
713
#endif
714
			argc--;
715
			argv++;
716
			has_midi_card = true;
717
			if (argc && argv[0][0] != '-') {
718
				midi_card_addr = 0x9f00 | ((uint16_t)strtol(argv[0], NULL, 16) & 0xff);
719
				midi_card_addr &= 0xfff0;
720
				argc--;
721
				argv++;
722
			} else {
723
				midi_card_addr = 0x9f60;
724
			}
725
		} else if (!strcmp(argv[0], "-sf2")) {
726
#ifndef HAS_FLUIDSYNTH
727
			no_fluidsynth_warning();
728
#endif
729
			argc--;
730
			argv++;
731
			if (!argc || argv[0][0] == '-') {
732
				usage();
733
			}
734
			sf2_path = argv[0];
735
			argc--;
736
			argv++;
737
		} else if (!strcmp(argv[0], "-midi-in")) {
738
#ifndef HAS_FLUIDSYNTH
739
			no_fluidsynth_warning();
740
#endif
741
			argc--;
742
			argv++;
743
			fs_midi_in_connect = true;
744
		} else if (!strcmp(argv[0], "-controlport")) {
745
			argc--;
746
			argv++;
747
			if (!argc || argv[0][0] == '-') {
748
				usage();
749
			}
750
			control_port = (int)strtol(argv[0], NULL, 0);
751
			argc--;
752
			argv++;
753
		} else if (!strcmp(argv[0], "-run")) {
754
			argc--;
755
			argv++;
756
			run_after_load = true;
757
		} else if (!strcmp(argv[0], "-bas")) {
758
			argc--;
759
			argv++;
760
			if (!argc || argv[0][0] == '-') {
761
				usage();
762
			}
763
			bas_path = argv[0];
764
			argc--;
765
			argv++;
766
		} else if (!strcmp(argv[0], "-test")) {
767
			argc--;
768
			argv++;
769
			if (!argc || argv[0][0] == '-') {
770
				usage();
771
			}
772
			test_number = atoi(argv[0]);
773
			run_test = true;
774
			argc--;
775
			argv++;
776
		} else if (!strcmp(argv[0], "-nvram")) {
777
			argc--;
778
			argv++;
779
			if (!argc || argv[0][0] == '-') {
780
				usage();
781
			}
782
			nvram_path = argv[0];
783
			argc--;
784
			argv++;
785
		} else if (!strcmp(argv[0], "-sdcard")) {
786
			argc--;
787
			argv++;
788
			if (!argc || argv[0][0] == '-') {
789
				usage();
790
			}
791
			sdcard_path = argv[0];
792
			argc--;
793
			argv++;
794
		} else if (!strcmp(argv[0], "-cart")) {
795
			argc--;
796
			argv++;
797
			if (!argc || argv[0][0] == '-') {
798
				usage();
799
			}
800
			cartridge_path = argv[0];
801
			argc--;
802
			argv++;
803
		} else if (!strcmp(argv[0], "-cartbin")) {
804
			argc--;
805
			argv++;
806
			if (!argc || argv[0][0] == '-') {
807
				usage();
808
			}
809
			cartridge_new();
810
			cartridge_define_bank_range(32, 255, CART_BANK_UNINITIALIZED_RAM);
811
			cartridge_import_files(argv, 1, 32, CART_BANK_INITIALIZED_RAM, 0);
812
			argc--;
813
			argv++;
814
		} else if (!strcmp(argv[0], "-warp")) {
815
			argc--;
816
			argv++;
817
			warp_mode = true;
818
		} else if (!strcmp(argv[0], "-pastewarp")) {
819
			argc--;
820
			argv++;
821
			warp_pastes = true;
822
		} else if (!strcmp(argv[0], "-echo")) {
823
			argc--;
824
			argv++;
825
			if (argc && argv[0][0] != '-') {
826
				if (!strcmp(argv[0], "raw")) {
827
					echo_mode = ECHO_MODE_RAW;
828
				} else if (!strcmp(argv[0], "iso")) {
829
						echo_mode = ECHO_MODE_ISO;
830
				} else {
831
					usage();
832
				}
833
				argc--;
834
				argv++;
835
			} else {
836
				echo_mode = ECHO_MODE_COOKED;
837
			}
838
		} else if (!strcmp(argv[0], "-log")) {
839
			argc--;
840
			argv++;
841
			if (!argc || argv[0][0] == '-') {
842
				usage();
843
			}
844
			for (char *p = argv[0]; *p; p++) {
845
				switch (tolower(*p)) {
846
					case 'k':
847
						log_keyboard = true;
848
						break;
849
					case 's':
850
						log_speed = true;
851
						break;
852
					case 'v':
853
						log_video = true;
854
						break;
855
					default:
856
						usage();
857
				}
858
			}
859
			argc--;
860
			argv++;
861
		} else if (!strcmp(argv[0], "-dump")) {
862
			argc--;
863
			argv++;
864
			if (!argc || argv[0][0] == '-') {
865
				usage();
866
			}
867
			dump_cpu = false;
868
			dump_ram = false;
869
			dump_bank = false;
870
			dump_vram = false;
871
			for (char *p = argv[0]; *p; p++) {
872
				switch (tolower(*p)) {
873
					case 'c':
874
						dump_cpu = true;
875
						break;
876
					case 'r':
877
						dump_ram = true;
878
						break;
879
					case 'b':
880
						dump_bank = true;
881
						break;
882
					case 'v':
883
						dump_vram = true;
884
						break;
885
					default:
886
						usage();
887
				}
888
			}
889
			argc--;
890
			argv++;
891
		} else if (!strcmp(argv[0], "-gif")) {
892
			argc--;
893
			argv++;
894
			// set up for recording
895
			record_gif = RECORD_GIF_PAUSED;
896
			if (!argc || argv[0][0] == '-') {
897
				usage();
898
			}
899
			gif_path = argv[0];
900
			argv++;
901
			argc--;
902
		} else if (!strcmp(argv[0], "-wav")) {
903
			argc--;
904
			argv++;
905
			// set up for recording
906
			if (!argc || argv[0][0] == '-') {
907
				usage();
908
			}
909
			wav_path = argv[0];
910
			argv++;
911
			argc--;
912
		} else if (!strcmp(argv[0], "-debug")) {
913
			argc--;
914
			argv++;
915
			debugger_enabled = true;
916
			if (argc && argv[0][0] != '-') {
917
				uint32_t bpVal = (uint32_t)strtol(argv[0], NULL, 16);
918
				struct breakpoint bp;
919
				if (bpVal < 0xA000) {
920
					bp.pc = bpVal;
921
					bp.bank = 0;
922
					bp.x16Bank = -1;
923
				} else {
924
					bp.pc = bpVal & 0xffff;
925
					bp.bank = 0;
926
					bp.x16Bank = bpVal >> 16;
927
				}
928
				DEBUGSetBreakPoint(bp);
929
				argc--;
930
				argv++;
931
			}
932
		} else if (!strcmp(argv[0], "-randram")) {
933
			/* this operation has no effect anymore, randomizing the Ram is now default */
934
			argc--;
935
			argv++;
936
		} else if (!strcmp(argv[0], "-zeroram")) {
937
			argc--;
938
			argv++;
939
			memory_randomize_ram(false);
940
			zeroram = true;
941
		} else if (!strcmp(argv[0], "-wuninit")) {
942
			argc--;
943
			argv++;
944
			memory_report_uninitialized_access(true);
945
		} else if (!strcmp(argv[0], "-memorystats")) {
946
			argc--;
947
			argv++;
948
			if (!argc || argv[0][0] == '-') {
949
				usage();
950
			}
951
			memory_report_usage_statistics(argv[0]);
952
			argv++;
953
			argc--;
954
		} else if (!strcmp(argv[0], "-joy1")) {
955
			argc--;
956
			argv++;
957
			Joystick_slots_enabled[0] = true;
958
		} else if (!strcmp(argv[0], "-joy2")){
959
			argc--;
960
			argv++;
961
			Joystick_slots_enabled[1] = true;
962
		} else if (!strcmp(argv[0], "-joy3")) {
963
			argc--;
964
			argv++;
965
			Joystick_slots_enabled[2] = true;
966
		} else if (!strcmp(argv[0], "-joy4")) {
967
			argc--;
968
			argv++;
969
			Joystick_slots_enabled[3] = true;
970
#ifdef TRACE
971
		} else if (!strcmp(argv[0], "-trace")) {
972
			argc--;
973
			argv++;
974
			if (argc && argv[0][0] != '-') {
975
				trace_mode = false;
976
				trace_address = (uint16_t)strtol(argv[0], NULL, 16);
977
				argc--;
978
				argv++;
979
			} else {
980
				trace_mode = true;
981
				trace_address = 0;
982
			}
983
#endif
984
		} else if (!strcmp(argv[0], "-scale")) {
985
			argc--;
986
			argv++;
987
			if(!argc || argv[0][0] == '-') {
988
				usage();
989
			}
990
			for(char *p = argv[0]; *p; p++) {
991
				switch(tolower(*p)) {
992
				case '1':
993
					window_scale = 1;
994
					break;
995
				case '2':
996
					window_scale = 2;
997
					break;
998
				case '3':
999
					window_scale = 3;
1000
					break;
1001
				case '4':
1002
					window_scale = 4;
1003
					break;
1004
				default:
1005
					usage();
1006
				}
1007
			}
1008
			argc--;
1009
			argv++;
1010
		} else if (!strcmp(argv[0], "-quality")) {
1011
			argc--;
1012
			argv++;
1013
			if(!argc || argv[0][0] == '-') {
1014
				usage();
1015
			}
1016
			if (!strcmp(argv[0], "nearest") ||
1017
				!strcmp(argv[0], "linear") ||
1018
				!strcmp(argv[0], "best")) {
1019
				scale_quality = argv[0];
1020
			} else {
1021
				usage();
1022
			}
1023
			argc--;
1024
			argv++;
1025
		} else if (!strcmp(argv[0], "-widescreen")) {
1026
			argc--;
1027
			argv++;
1028
			screen_x_scale = 4.0/3;
1029
		} else if (!strcmp(argv[0], "-fullscreen")) {
1030
			argc--;
1031
			argv++;
1032
			fullscreen = true;
1033
		} else if (!strcmp(argv[0], "-opacity")) {
1034
			argc--;
1035
			argv++;
1036
			if (!argc || argv[0][0] == '-') {
1037
				usage();
1038
			}
1039
			window_opacity = strtof(argv[0], NULL);
1040
			argc--;
1041
			argv++;
1042
		} else if (!strcmp(argv[0], "-sound")) {
1043
			argc--;
1044
			argv++;
1045
			if (!argc || argv[0][0] == '-') {
1046
				audio_usage();
1047
			}
1048
			audio_dev_name = argv[0];
1049
			argc--;
1050
			argv++;
1051
		} else if (!strcmp(argv[0], "-abufs")) {
1052
			argc--;
1053
			argv++;
1054
			if (!argc || argv[0][0] == '-') {
1055
				usage();
1056
			}
1057
			audio_buffers = (int)strtol(argv[0], NULL, 10);
1058
			audio_buffers_set = true;
1059
			argc--;
1060
			argv++;
1061
		} else if (!strcmp(argv[0], "-rtc")) {
1062
			argc--;
1063
			argv++;
1064
			set_system_time = true;
1065
		} else if (!strcmp(argv[0], "-serial")) {
1066
			argc--;
1067
			argv++;
1068
			has_serial = true;
1069
		} else if (!strcmp(argv[0], "-nohostieee") || !strcmp(argv[0], "-nohostfs")) {
1070
			argc--;
1071
			argv++;
1072
			no_ieee_intercept = true;
1073
			hostfs_set = false;
1074
			using_hostfs = false;
1075
		} else if (!strcmp(argv[0], "-hostfsdev")){
1076
			argc--;
1077
			argv++;
1078
			if (!argc || argv[0][0] == '-') {
1079
				usage();
1080
			}
1081
			ieee_unit = (uint8_t)strtol(argv[0], NULL, 10);
1082
			if (ieee_unit < 8 || ieee_unit > 31) {
1083
				usage();
1084
			}
1085
			hostfs_set = true;
1086
			using_hostfs = true;
1087
			argc--;
1088
			argv++;
1089
		} else if (!strcmp(argv[0], "-fsroot")) {
1090
			argc--;
1091
			argv++;
1092
			if (!argc || argv[0][0] == '-') {
1093
				usage();
1094
			}
1095
			fsroot_path = (uint8_t *)argv[0];
1096
			argc--;
1097
			argv++;
1098
		} else if (!strcmp(argv[0], "-startin")) {
1099
			argc--;
1100
			argv++;
1101
			if (!argc || argv[0][0] == '-') {
1102
				usage();
1103
			}
1104
			startin_path = (uint8_t *)argv[0];
1105
			argc--;
1106
			argv++;
1107
		} else if (!strcmp(argv[0], "-noemucmdkeys")) {
1108
			argc--;
1109
			argv++;
1110
			disable_emu_cmd_keys = true;
1111
		} else if (!strcmp(argv[0], "-capture")) {
1112
			argc--;
1113
			argv++;
1114
			grab_mouse = true;
1115
		} else if (!strcmp(argv[0], "-longpwron")) {
1116
			argc--;
1117
			argv++;
1118
			pwr_long_press = true;
1119
		} else if (!strcmp(argv[0], "-nokeyboardcapture")) {
1120
			argc--;
1121
			argv++;
1122
			no_keyboard_capture = true;
1123
		} else if (!strcmp(argv[0], "-via2")) {
1124
			argc--;
1125
			argv++;
1126
			has_via2 = true;
1127
		} else if (!strcmp(argv[0], "-version")){
1128
			printf("%s", VER_INFO);
1129
#ifdef GIT_REV
1130
			printf(" "GIT_REV"\n");
1131
#else
1132
			printf("\n");
1133
#endif
1134
			argc--;
1135
			argv++;
1136
			exit(0);
1137
		} else if (!strcmp(argv[0], "-coverage")) {
1138
			argc--;
1139
			argv++;
1140
			if (!argc || argv[0][0] == '-') {
1141
				usage();
1142
			}
1143
			if (!coverage_init(argv[0])) {
1144
				printf("Cannot set up coverage for %s!\n", argv[0]);
1145
				exit(1);
1146
			}
1147
			argc--;
1148
			argv++;
1149
		} else if (!strcmp(argv[0], "-testbench")){
1150
			printf("Testbench mode...\n");
1151
			fflush(stdout);
1152
			argc--;
1153
			argv++;
1154
			testbench=true;
1155
			headless=true;
1156
		} else if (!strcmp(argv[0], "-mhz")){
1157
			argc--;
1158
			argv++;
1159
			if (!argc || argv[0][0] == '-') {
1160
				usage();
1161
			}
1162
			MHZ = (uint8_t)strtol(argv[0], NULL, 10);
1163
			if (MHZ < 1 || MHZ > 40) {
1164
				usage();
1165
			}
1166
			argc--;
1167
			argv++;
1168
		} else if (!strcmp(argv[0], "-midline-effects")){
1169
			argc--;
1170
			argv++;
1171
			enable_midline = true;
1172
		} else if (!strcmp(argv[0], "-enable-ym2151-irq")){
1173
			argc--;
1174
			argv++;
1175
			ym2151_irq_support = true;
1176
		} else if (!strcmp(argv[0], "-c816")){
1177
			argc--;
1178
			argv++;
1179
			regs.is65c816 = true;
1180
			is_gen2 = false;
1181
		} else if (!strcmp(argv[0], "-c02")){
1182
			argc--;
1183
			argv++;
1184
			regs.is65c816 = false;
1185
			is_gen2 = false;
1186
		} else if (!strcmp(argv[0], "-gs")) {
1187
			argc--;
1188
			argv++;
1189
			regs.is65c816 = true;
1190
			is_gen2 = true;
1191
		}
1192
		else if (!strcmp(argv[0], "-rockwell")){
1193
			argc--;
1194
			argv++;
1195
			warn_rockwell = false;
1196
		} else {
1197
			usage();
1198
		}
1199
	}
1200
1201
	if (is_gen2) {
1202
		num_banks = NUM_MAX_BANKS;
1203
		num_ram_banks = NUM_MAX_RAM_BANKS;
1204
	}
1205
1206
	SDL_RWops *f = SDL_RWFromFile(rom_path, "rb");
1207
	if (!f) {
1208
		printf("Cannot open %s!\n", rom_path);
1209
		exit(1);
1210
	}
1211
	size_t rom_size = SDL_RWread(f, ROM, ROM_SIZE, 1);
1212
	(void)rom_size;
1213
	SDL_RWclose(f);
1214
1215
	if (nvram_path) {
1216
		SDL_RWops *f = SDL_RWFromFile(nvram_path, "rb");
1217
		if (f) {
1218
			SDL_RWread(f, nvram, 1, sizeof(nvram));
1219
			SDL_RWclose(f);
1220
		}
1221
	}
1222
1223
	if (sdcard_path) {
1224
		sdcard_set_path(sdcard_path);
1225
		if (!hostfs_set) {
1226
			using_hostfs = false;
1227
		}
1228
	}
1229
1230
	if (using_hostfs && !audio_buffers_set) {
1231
#ifdef __EMSCRIPTEN__
1232
		audio_buffers = 8; // wasm has larger buffers in audio.c, so we keep it 8 even w/ HostFS
1233
#else
1234
		audio_buffers = 32;
1235
#endif
1236
	}
1237
1238
	if (sf2_path && has_midi_card) {
1239
		if (midi_card_addr < 0x9f60) {
1240
			fprintf(stderr, "Warning: Serial MIDI card address must be in the range of 9F60-9FF0\n");
1241
		} else {
1242
			midi_init();
1243
			midi_load_sf2((uint8_t *)sf2_path);
1244
		}
1245
	} else if (sf2_path || has_midi_card) {
1246
		fprintf(stderr, "Warning: -sf2 and -midicard must be specified together in order to enable the MIDI synth.\n");
1247
		has_midi_card = false;
1248
	}
1249
1250
	if (cartridge_path) {
1251
		if (!cartridge_load(cartridge_path, !zeroram)) {
1252
			printf("Cannot open %s!\n", cartridge_path);
1253
			exit(1);
1254
		}
1255
	}
1256
1257
	prg_override_start = -1;
1258
	if (prg_path) {
1259
		char *comma = strchr(prg_path, ',');
1260
		if (comma) {
1261
			prg_override_start = (uint16_t)strtol(comma + 1, NULL, 16);
1262
			*comma = 0;
1263
		}
1264
1265
		prg_file = SDL_RWFromFile(prg_path, "rb");
1266
		if (!prg_file) {
1267
			printf("Cannot open %s!\n", prg_path);
1268
			exit(1);
1269
		}
1270
	}
1271
1272
	if (bas_path) {
1273
		SDL_RWops *bas_file = SDL_RWFromFile(bas_path, "r");
1274
		if (!bas_file) {
1275
			printf("Cannot open %s!\n", bas_path);
1276
			exit(1);
1277
		}
1278
		paste_text = paste_text_data;
1279
		size_t paste_size = SDL_RWread(bas_file, paste_text, 1, sizeof(paste_text_data) - 1);
1280
		if (run_after_load) {
1281
			strncpy(paste_text + paste_size, "\rRUN\r", sizeof(paste_text_data) - paste_size);
1282
		} else {
1283
			paste_text[paste_size] = 0;
1284
		}
1285
		SDL_RWclose(bas_file);
1286
	}
1287
1288
	if (run_test) {
1289
		paste_text = paste_text_data;
1290
		snprintf(paste_text, sizeof(paste_text_data), "TEST %d\r", test_number);
1291
	}
1292
1293
#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR
1294
	// Don't disable compositing (on KDE for example)
1295
	// Available since SDL 2.0.8
1296
	SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
1297
#endif
1298
#ifdef __EMSCRIPTEN__
1299
	emscripten_set_main_loop(emscripten_main_loop, 0, 0);
1300
#endif
1301
	if (!headless) {
1302
		// Shows up in the power management area of Linux desktops of applications inhibiting the screensaver
1303
		// As well as the audio mixer
1304
		// Unless hinted, defaults are "My SDL application" and "Playing a game"
1305
#ifdef SDL_HINT_AUDIO_DEVICE_APP_NAME
1306
		SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, "Commander X16 Emulator");
1307
#endif
1308
#ifdef SDL_HINT_APP_NAME
1309
		SDL_SetHint(SDL_HINT_APP_NAME, "Commander X16 Emulator");
1310
#endif
1311
#ifdef SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME
1312
		SDL_SetHint(SDL_HINT_SCREENSAVER_INHIBIT_ACTIVITY_NAME, "Emulating modern retro awesomeness");
1313
#endif
1314
		int e = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO | SDL_INIT_TIMER);
1315
		if (e < 0) {
1316
			fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
1317
			exit(-1);
1318
		}
1319
		audio_init(audio_dev_name, audio_buffers);
1320
		video_init(window_scale, screen_x_scale, scale_quality, fullscreen, window_opacity);
1321
	}
1322
1323
	wav_recorder_set_path(wav_path);
1324
1325
	memory_init();
1326
1327
	joystick_init();
1328
1329
	rtc_init(set_system_time);
1330
1331
	machine_reset();
1332
1333
	timing_init();
1334
1335
	instruction_counter = 0;
1336
1337
	if (control_port > 0) {
1338
		x16_control_start(control_port);
1339
	}
1340
1341
#ifdef __EMSCRIPTEN__
1342
	emscripten_cancel_main_loop();
1343
	emscripten_set_main_loop(emscripten_main_loop, 0, 1);
1344
#else
1345
	emulator_loop(NULL);
1346
#endif
1347
1348
	main_shutdown();
1349
	memory_dump_usage_counts();
1350
	return 0;
1351
}
1352
1353
void main_shutdown() {
1354
	coverage_write();
1355
	if (!headless){
1356
		wav_recorder_shutdown();
1357
		audio_close();
1358
		video_end();
1359
		SDL_Quit();
1360
	}
1361
	if(cartridge_path) {
1362
		cartridge_save_nvram();
1363
		cartridge_unload();
1364
	}
1365
	files_shutdown();
1366
1367
#ifdef PERFSTAT
1368
	for (int pc = 0xc000; pc < sizeof(stat)/sizeof(*stat); pc++) {
1369
		if (stat[pc] == 0) {
1370
			continue;
1371
		}
1372
		char *label = label_for_address(pc);
1373
		if (!label) {
1374
			continue;
1375
		}
1376
		char *original_label = label;
1377
		uint16_t pc2 = pc;
1378
		if (label[0] == '@') {
1379
			label = NULL;
1380
			while (!label || label[0] == '@') {
1381
				pc2--;
1382
				label = label_for_address(pc2);
1383
			}
1384
		}
1385
		printf("%d\t $%04X %s+%d", stat[pc], pc, label, pc-pc2);
1386
		if (pc-pc2 != 0) {
1387
			printf(" (%s)", original_label);
1388
		}
1389
		printf("\n");
1390
	}
1391
#endif
1392
1393
}
1394
1395
bool
1396
set_kernal_status(uint8_t s)
1397
{
1398
	// There is no KERNAL API to write the STATUS variable.
1399
	// But there is code to read it, READST, which should
1400
	// always look like this:
1401
	// 00:.,d6a0 ad 89 02 lda $0289
1402
	// 00:.,d6a3 0d 89 02 ora $0289
1403
	// 00:.,d6a6 8d 89 02 sta $0289
1404
	// We can extract the location of the STATUS variable
1405
	// from it.
1406
1407
	// JMP in the KERNAL API vectors
1408
	if (debug_read6502(0xffb7, 0, 0) != 0x4c) {
1409
		return false;
1410
	}
1411
	// target of KERNAL API vector JMP
1412
	uint16_t readst = debug_read6502(0xffb8, 0, 0) | debug_read6502(0xffb9, 0, 0) << 8;
1413
	if (readst < 0xc000) {
1414
		return false;
1415
	}
1416
	// ad 89 02 lda $0289
1417
	if (debug_read6502(readst, 0, 0) != 0xad) {
1418
		return false;
1419
	}
1420
	// ad 89 02 lda $0289
1421
	if (debug_read6502(readst + 3, 0, 0) != 0x0d) {
1422
		return false;
1423
	}
1424
	// ad 89 02 lda $0289
1425
	if (debug_read6502(readst + 6, 0, 0) != 0x8d) {
1426
		return false;
1427
	}
1428
	uint16_t status0 = debug_read6502(readst+1, 0, 0) | debug_read6502(readst+2, 0, 0) << 8;
1429
	uint16_t status1 = debug_read6502(readst+4, 0, 0) | debug_read6502(readst+5, 0, 0) << 8;
1430
	uint16_t status2 = debug_read6502(readst+7, 0, 0) | debug_read6502(readst+8, 0, 0) << 8;
1431
	// all three addresses must be the same
1432
	if (status0 != status1 || status0 != status2) {
1433
		return false;
1434
	}
1435
1436
	// everything okay, write the status!
1437
	RAM[status0] = s;
1438
	return true;
1439
}
1440
1441
bool
1442
handle_ieee_intercept()
1443
{
1444
	if (no_ieee_intercept) {
1445
		return false;
1446
	}
1447
1448
	if (regs.pc < 0xFEA8 || (is_gen2 && regs.k != 0) || !is_kernal()) {
1449
		return false;
1450
	}
1451
1452
	if (has_serial) {
1453
		// if we do bit-level serial bus emulation, we don't
1454
		// do high-level KERNAL IEEE API interception
1455
		return false;
1456
	}
1457
1458
	if (sdcard_attached && !prg_file && !using_hostfs) {
1459
		// if should emulate an SD card (and don't need to
1460
		// hack a PRG into RAM), we skip HostFS if it uses unit 8
1461
		return false;
1462
	}
1463
1464
	if (sdcard_attached && prg_file && prg_finished_loading && !using_hostfs) {
1465
		// also skip if we should do SD card and we're done
1466
		// with the PRG hack if HostFS uses unit 8
1467
		return false;
1468
	}
1469
1470
	uint64_t base_ticks = SDL_GetPerformanceCounter();
1471
1472
	static int count_unlistn = 0;
1473
	bool handled = true;
1474
	int s = -1;
1475
	switch(regs.pc) {
1476
		case 0xFEA8: { // EXTAPI16
1477
			if (!regs.is65c816 || (regs.status & 0x20)) { // don't handle if not '816 or if m=1
1478
				handled = false; // punt to kernal's 65C816 check and will break if m=1
1479
				break;
1480
			}
1481
			switch(regs.c) {
1482
				case 5: { // XMACPTR
1483
					s=XMACPTR(regs.status & 0x01);
1484
					if (s == -2) {
1485
						handled = false;
1486
					} else if (s == -3) {
1487
						regs.status |= 1; // SEC (unsupported, or in this case, no open context)
1488
						regs.status &= ~0x30; // REP #$30
1489
					} else {
1490
						regs.status &= ~0x31; // REP #$30 and CLC -> supported
1491
					}
1492
					break;
1493
				}
1494
				case 6: { // XMCIOUT
1495
					s=XMCIOUT(regs.status & 0x01);
1496
					if (s == -2) {
1497
						handled = false;
1498
					} else if (s == -3) {
1499
						regs.status |= 1; // SEC (unsupported, or in this case, no open context)
1500
						regs.status &= ~0x30; // REP #$30
1501
					} else {
1502
						regs.status &= ~0x31; // REP #$30 and CLC -> supported
1503
					}
1504
					break;
1505
				}
1506
				default:
1507
					handled = false;
1508
					break;
1509
			}
1510
			break;
1511
		}
1512
		case 0xFEB1: {
1513
			uint16_t count = regs.a;
1514
			s=MCIOUT(regs.yl << 8 | regs.xl, &count, regs.status & 0x01);
1515
			if (s == -2) {
1516
				handled = false;
1517
			} else if (s == -3) {
1518
				regs.status = (regs.status | 1); // SEC (unsupported, or in this case, no open context)
1519
			} else {
1520
				regs.x = count & 0xff;
1521
				regs.y = count >> 8;
1522
				regs.status &= 0xfe; // clear C -> supported
1523
			}
1524
			break;
1525
		}
1526
		case 0xFF44: {
1527
			uint16_t count = regs.a;
1528
			s=MACPTR(regs.yl << 8 | regs.xl, &count, regs.status & 0x01);
1529
			if (s == -2) {
1530
				handled = false;
1531
			} else if (s == -3) {
1532
				regs.status = (regs.status | 1); // SEC (unsupported, or in this case, no open context)
1533
			} else {
1534
				regs.x = count & 0xff;
1535
				regs.y = count >> 8;
1536
				regs.status &= 0xfe; // clear C -> supported
1537
			}
1538
			break;
1539
		}
1540
		case 0xFF93:
1541
			s=SECOND(regs.a);
1542
			if (s == -2) {
1543
				handled = false;
1544
			}
1545
			break;
1546
		case 0xFF96:
1547
			s=TKSA(regs.a);
1548
			if (s == -2) {
1549
				handled = false;
1550
			}
1551
			break;
1552
		case 0xFFA5:
1553
			s=ACPTR(&regs.a);
1554
			if (s == -2) {
1555
				handled = false;
1556
			} else {
1557
				regs.status = (regs.status & ~3) | (!regs.a << 1); // unconditional CLC, and set zero flag based on byte read
1558
			}
1559
			break;
1560
		case 0xFFA8:
1561
			s=CIOUT(regs.a);
1562
			if (s == -2) {
1563
				handled = false;
1564
			} else {
1565
				regs.status = (regs.status & ~1); // unconditonal CLC
1566
			}
1567
			break;
1568
		case 0xFFAB:
1569
			s=UNTLK();
1570
			if (s == -2) {
1571
				handled = false;
1572
			}
1573
			break;
1574
		case 0xFFAE:
1575
			s=UNLSN();
1576
			if (s == -2) {
1577
				handled = false;
1578
			}
1579
			if (prg_file && sdcard_path_is_set() && ++count_unlistn == 4) {
1580
				// after auto-loading a PRG from the host fs,
1581
				// switch to the SD card if requested
1582
				// 4x UNLISTEN:
1583
				//    2x for LOAD"AUTOBOOT.X16*"
1584
				//    2x for LOAD":*"
1585
				prg_finished_loading = true;
1586
				sdcard_attach();
1587
			}
1588
			break;
1589
		case 0xFFB1:
1590
			s=LISTEN(regs.a);
1591
			if (s == -2) {
1592
				handled = false;
1593
			} else {
1594
				regs.status = (regs.status & ~1); // unconditonal CLC
1595
			}
1596
			break;
1597
		case 0xFFB4:
1598
			s=TALK(regs.a);
1599
			if (s == -2) {
1600
				handled = false;
1601
			} else {
1602
				regs.status = (regs.status & ~1); // unconditonal CLC
1603
			}
1604
			break;
1605
		default:
1606
			handled = false;
1607
			break;
1608
	}
1609
1610
	if (handled) {
1611
		// Add the number CPU cycles equivalent to the amount of time that the operation actually took
1612
		// to prevent the emu from warping after a hostfs load
1613
		uint64_t perf_diff = SDL_GetPerformanceCounter() - base_ticks;
1614
		uint32_t missed_ticks = (uint64_t)(perf_diff * 1000000ULL * MHZ) / SDL_GetPerformanceFrequency();
1615
		clockticks6502 += missed_ticks;
1616
		if (s >= 0) {
1617
			if (!set_kernal_status(s)) {
1618
				printf("Warning: Could not set STATUS!\n");
1619
			}
1620
		}
1621
1622
		increment_wrap_at_page_boundary(&regs.sp);
1623
		uint8_t low = debug_read6502(regs.sp, 0, USE_CURRENT_X16_BANK);
1624
		increment_wrap_at_page_boundary(&regs.sp);
1625
		regs.pc = ((debug_read6502(regs.sp, 0, USE_CURRENT_X16_BANK) << 8) | low) + 1;
1626
	}
1627
	return handled;
1628
}
1629
8e8d9cad Andy McCall ↶ 1630
// While the CPU waits in WAI, nothing it can see changes until an
1631
// interrupt, yet the loop below would go round once a clock, stepping every
1632
// device each time: at the BASIC prompt, most of the run time. This steps
1633
// them by many clocks at once instead, but only as far as the next clock on
1634
// which something could raise an interrupt, so the machine does exactly
1635
// what it did a clock at a time: VERA to the end of the line, audio to its
1636
// next render, and VIA1 a clock at a time here, stopping on the clock its
1637
// interrupt rises (via_step() does not take a stretch that ends on its
1638
// timer's -1 state the way it takes single clocks). Everything not shown to
1639
// add up that way keeps the clock-by-clock loop: VIA2, the serial bus, the
1640
// MIDI card, an SPI byte in flight, YM2151 interrupts, the debugger, BASIC
1641
// pasting, the KERNAL hooks at $FEA8 and up.
1642
// Returns the clocks taken, with VIA1 stepped through them, or 0.
1643
static uint32_t
1644
wai_fast_forward(void)
1645
{
1646
	if (!waiting || headless || debugger_enabled || has_serial || has_via2 || has_midi_card ||
1647
	    ym2151_irq_support || pasting_bas || regs.pc >= 0xFEA8 || vera_spi_busy()) {
1648
		return 0;
1649
	}
1650
	uint32_t limit = video_clocks_to_line_end(MHZ);
1651
	uint32_t audio = audio_clocks_to_render();
1652
	if (audio < limit) {
1653
		limit = audio;
1654
	}
1655
	if (limit < 2) {
1656
		return 0;
1657
	}
1658
	uint32_t n = 0;
1659
	do {
1660
		via1_step(1);
1661
		n++;
1662
	} while (n < limit && !via1_irq());
1663
	clockticks6502 += n;
1664
	return n;
1665
}
1666
03a3459e Andy McCall 1667
void
1668
emscripten_main_loop(void) {
1669
	emulator_loop(NULL);
1670
}
1671
1672
1673
void *
1674
emulator_loop(void *param)
1675
{
1676
	uint32_t old_clockticks6502 = clockticks6502;
1677
	for (;;) {
1678
		if (coverage_stop) break;
1679
		if (smc_requested_reset) machine_reset();
1680
1681
		retro_control_service();
1682
		if (!retro_control_running()) {
1683
			SDL_Delay(1);
1684
			continue;
1685
		}
1686
1687
		if (testbench && regs.pc == 0xfffd){
1688
			testbench_init();
1689
		}
1690
1691
		if (debugger_enabled) {
1692
			int dbgCmd = DEBUGGetCurrentStatus();
1693
			if (dbgCmd > 0) continue;
1694
			if (dbgCmd < 0) break;
1695
		}
1696
1697
#ifdef PERFSTAT
1698
1699
//		if (memory_get_rom_bank() == 3) {
1700
//			stat[pc]++;
1701
//		}
1702
		if (memory_get_rom_bank() == 3) {
1703
			static uint8_t old_sp;
1704
			static uint16_t base_pc;
1705
			if (regs.sp < old_sp) {
1706
				base_pc = pc;
1707
			}
1708
			old_sp = regs.sp;
1709
			stat[base_pc]++;
1710
		}
1711
#endif
1712
1713
#ifdef TRACE
1714
		if (regs.pc == trace_address && trace_address != 0) {
1715
			trace_mode = true;
1716
		}
1717
		if (trace_mode && !waiting) {
1718
			char *lst = lst_for_address(regs.pc);
1719
			if (lst) {
1720
				char *lf;
1721
				while ((lf = strchr(lst, '\n'))) {
1722
					for (int i = 0; i < 120; i++) {
1723
						printf(" ");
1724
					}
1725
					if (regs.is65c816) {
1726
						printf("        "); // 8 extra width
1727
					}
1728
					for (char *c = lst; c < lf; c++) {
1729
						printf("%c", *c);
1730
					}
1731
					printf("\n");
1732
					lst = lf + 1;
1733
				}
1734
			}
1735
1736
			printf("[%8d] ", instruction_counter);
1737
1738
			int32_t eff_addr;
1739
1740
			char *label = label_for_address(regs.pc);
1741
			int label_len = label ? strlen(label) : 0;
1742
			if (label) {
1743
				printf("%s", label);
1744
			}
1745
			for (int i = 0; i < 20 - label_len; i++) {
1746
				printf(" ");
1747
			}
1748
1749
			if (regs.pc >= 0xc000 && regs.k == 0) {
1750
				printf (" %02x", memory_get_rom_bank());
1751
			} else if (regs.pc >= 0xa000 && regs.k == 0) {
1752
				printf (" %02x", memory_get_ram_bank());
1753
			} else {
1754
				printf (" --");
1755
			}
1756
1757
			printf(":.,%04x ", regs.pc);
1758
1759
			char disasm_line[15];
1760
			int len = disasm(regs.pc, regs.k, RAM, disasm_line, sizeof(disasm_line), -1, regs.status, &eff_addr);
1761
			for (int i = 0; i < len; i++) {
1762
				printf("%02x ", debug_read6502(regs.pc + i, regs.k, USE_CURRENT_X16_BANK));
1763
			}
1764
			for (int i = 0; i < 9 - 3 * len; i++) {
1765
				printf(" ");
1766
			}
1767
			printf("%s", disasm_line);
1768
			for (int i = 0; i < 15 - strlen(disasm_line); i++) {
1769
				printf(" ");
1770
			}
1771
			if (regs.is65c816) {
1772
				printf("C=$%04x X=$%04x Y=$%04x S=$%04x P=", regs.c, regs.x, regs.y, regs.sp);
1773
				for (int i = 7; i >= 0; i--) {
1774
					printf("%c", (regs.status & (1 << i)) ? "czidxmvn"[i] : '-');
1775
				}
1776
1777
				putchar(regs.e ? 'e' : '-');
1778
			} else {
1779
				printf("A=$%02x X=$%02x Y=$%02x S=$%02x P=", regs.a, regs.xl, regs.yl, regs.sp & 0xff);
1780
				for (int i = 7; i >= 0; i--) {
1781
					printf("%c", (regs.status & (1 << i)) ? "czidb-vn"[i] : '-');
1782
				}
1783
			}
1784
1785
			if (eff_addr == 0x9f23 && regs.k == 0) {
1786
				printf(" VRAM=$%05x ", video_get_address(0));
1787
			} else if (eff_addr == 0x9f24 && regs.k == 0) {
1788
				printf(" VRAM=$%05x ", video_get_address(1));
1789
			} else if (eff_addr >= 0xc000 && regs.k == 0) {
1790
				printf(" EA=$%02x:%04x ", memory_get_rom_bank(), eff_addr);
1791
			} else if (eff_addr >= 0xa000 && regs.k == 0) {
1792
				printf(" EA=$%02x:%04x ", memory_get_ram_bank(), eff_addr);
1793
			} else if (eff_addr >= 0) {
1794
				printf(" EA=$--:%04x ", eff_addr);
1795
			} else {
1796
				printf("             ");
1797
			}
1798
1799
			if (lst) {
1800
				printf("%s      %s", regs.is65c816 ? "" : " ", lst);
1801
			}
1802
1803
			printf("\n");
1804
		}
1805
#endif
1806
0adc2e74 Andy McCall ↶ 1807
		// Only the KERNAL's IEEE entry points, $FEA8 and up, are intercepted;
1808
		// test that here, so that every other instruction skips the call.
1809
		if (regs.pc >= 0xFEA8 && handle_ieee_intercept()) {
03a3459e Andy McCall 1810
			continue;
1811
		}
1812
1813
		instruction_counter += waiting ^ 0x1;
1814
1815
		if (coverage_enabled && !waiting) {
1816
			coverage_mark(regs.pc);
1817
		}
8e8d9cad Andy McCall ↶ 1818
		uint32_t skipped = wai_fast_forward();
1819
		if (!skipped) {
1820
			step6502();
1821
		}
03a3459e Andy McCall 1822
		uint32_t clocks = clockticks6502 - old_clockticks6502;
1823
		old_clockticks6502 = clockticks6502;
1824
		bool new_frame = false;
8e8d9cad Andy McCall ↶ 1825
		if (!skipped) {
1826
			via1_step(clocks);
1827
		}
03a3459e Andy McCall 1828
		vera_spi_step(MHZ, clocks);
1829
		if (has_serial) {
1830
			serial_step(clocks);
1831
		}
1832
		if (has_via2) {
1833
			via2_step(clocks);
1834
		}
1835
		if (!headless) {
1836
			new_frame |= video_step(MHZ, clocks, false);
1837
		}
1838
1839
		if (new_frame) {
1840
			retro_control_on_frame();
1841
			if (has_netcard) {
1842
				netcard_step();
1843
			}
1844
		}
1845
8e8d9cad Andy McCall ↶ 1846
		// The I2C bus acts only on a change of the clock and data lines the
1847
		// VIA drives, which happen between instructions, so once is enough.
1848
		if (clocks) {
03a3459e Andy McCall 1849
			i2c_step();
1850
		}
1851
		rtc_step(clocks);
1852
1853
		if (!headless) {
1854
			audio_step(clocks);
1855
		}
1856
51099110 Andy McCall ↶ 1857
		if (has_midi_card) {
1858
			midi_serial_step(clocks);
1859
		}
03a3459e Andy McCall 1860
1861
		if (!headless && new_frame) {
1862
			if (nvram_dirty && nvram_path) {
1863
				SDL_RWops *f = SDL_RWFromFile(nvram_path, "wb");
1864
				if (f) {
1865
					SDL_RWwrite(f, nvram, 1, sizeof(nvram));
1866
					SDL_RWclose(f);
1867
				}
1868
				nvram_dirty = false;
1869
			}
1870
1871
			if (!video_update()) {
1872
				break;
1873
			}
1874
1875
			timing_update();
1876
#ifdef __EMSCRIPTEN__
1877
			// After completing a frame we yield back control to the browser to stay responsive
1878
			return 0;
1879
#endif
1880
		}
1881
1882
		// The optimization from the opportunistic batching of audio rendering
1883
		// is lost if we need to track the YM2151 IRQ, so it has been made a
1884
		// command-line switch that's disabled by default.
1885
		if (ym2151_irq_support) {
1886
			audio_render();
1887
		}
1888
1889
		if (video_get_irq_out() || via1_irq() || (has_via2 && via2_irq()) || (ym2151_irq_support && YM_irq()) || (has_midi_card && midi_serial_irq())) {
1890
//			printf("IRQ!\n");
1891
			irq6502();
1892
		}
1893
1894
		if (regs.pc == 0xffff) {
1895
			if (save_on_exit) {
1896
				machine_dump("CPU program counter reached $ffff");
1897
			}
1898
			break;
1899
		}
1900
1901
		// Change this comparison value if ever additional KERNAL
1902
		// API calls are snooped in this routine.
1903
1904
		if (regs.pc >= 0xff68 && (!is_gen2 || regs.k == 0) && is_kernal()) {
1905
			if (regs.pc == 0xff68) {
1906
				kernal_mouse_enabled = !!regs.a;
1907
				SDL_ShowCursor((mouse_grabbed || kernal_mouse_enabled) ? SDL_DISABLE : SDL_ENABLE);
1908
			}
1909
1910
			if (echo_mode != ECHO_MODE_NONE && regs.pc == 0xffd2) {
1911
				uint8_t c = regs.a;
1912
				if (echo_mode == ECHO_MODE_COOKED) {
1913
					if (c == 0x0d) {
1914
						printf("\n");
1915
					} else if (c == 0x0a) {
1916
						// skip
1917
					} else if (c < 0x20 || c >= 0x80) {
1918
						printf("\\X%02X", c);
1919
					} else {
1920
						printf("%c", c);
1921
					}
1922
				} else if (echo_mode == ECHO_MODE_ISO) {
1923
					if (c == 0x0d) {
1924
						printf("\n");
1925
					} else if (c == 0x0a) {
1926
						// skip
1927
					} else if (c < 0x20 || (c >= 0x80 && c < 0xa0)) {
1928
						printf("\\X%02X", c);
1929
					} else {
1930
						print_iso8859_15_char(c);
1931
					}
1932
				} else {
1933
					printf("%c", c);
1934
				}
1935
				fflush(stdout);
1936
			}
1937
1938
			if (regs.pc == 0xffcf) {
1939
				// as soon as BASIC starts reading a line...
1940
				static bool prg_done = false;
1941
1942
				if (prg_file && !prg_done) {
1943
					int loadlen = 0;
1944
					// LOAD":*" will cause the IEEE library
1945
					// to load from "prg_file"
1946
					if (prg_override_start >= 0) {
1947
						loadlen = snprintf(paste_text_data, sizeof(paste_text_data), "LOAD\":*\",%d,1,$%04X\r", ieee_unit, prg_override_start);
1948
					} else {
1949
						loadlen = snprintf(paste_text_data, sizeof(paste_text_data), "LOAD\":*\",%d,1\r", ieee_unit);
1950
					}
1951
					paste_text = paste_text_data;
1952
					prg_done = true;
1953
1954
					if (run_after_load) {
1955
						if (prg_override_start >= 0) {
1956
							snprintf(paste_text_data + loadlen, sizeof(paste_text_data) - loadlen, "SYS$%04X\r", prg_override_start);
1957
						} else {
1958
							snprintf(paste_text_data + loadlen, sizeof(paste_text_data) - loadlen, "RUN\r");
1959
						}
1960
					}
1961
				}
1962
				else if (testbench && !test_init_complete){
1963
					snprintf(paste_text_data, sizeof(paste_text_data), "SYS65533\r");
1964
					paste_text = paste_text_data;
1965
					test_init_complete=true;
1966
				}
1967
1968
				if (paste_text) {
1969
					// ...paste BASIC code into the keyboard buffer
1970
					pasting_bas = true;
1971
					if (warp_pastes) warp_mode = true;
1972
				}
1973
			}
1974
1975
		}
1976
#if 0 // enable this for slow pasting
1977
		if (!(instruction_counter % 100000))
1978
#endif
1979
		while (pasting_bas && BRAM[NDX - 0xa000] < 10 && !(regs.status & 0x04)) {
1980
			uint32_t c;
1981
			int e = 0;
1982
1983
			if (paste_text[0] == '\\' && paste_text[1] == 'X' && paste_text[2] && paste_text[3]) {
1984
				uint8_t hi = strtol((char[]){paste_text[2], 0}, NULL, 16);
1985
				uint8_t lo = strtol((char[]){paste_text[3], 0}, NULL, 16);
1986
				c = hi << 4 | lo;
1987
				paste_text += 4;
1988
			} else {
1989
				paste_text = utf8_decode(paste_text, &c, &e);
1990
				c = iso8859_15_from_unicode(c);
1991
			}
1992
			if (c && !e) {
1993
				BRAM[KEYD - 0xa000 + BRAM[NDX - 0xa000]] = c;
1994
				BRAM[NDX - 0xa000]++;
1995
			} else {
1996
				pasting_bas = false;
1997
				if (warp_pastes) warp_mode = false;
1998
				paste_text = NULL;
1999
				if (clipboard_buffer) {
2000
					SDL_free(clipboard_buffer);
2001
					clipboard_buffer = NULL;
2002
				}
2003
			}
2004
		}
2005
	}
2006
2007
	return 0;
2008
}