emulator.cpp 17.7 KB
Newer Older
Sergio Costas's avatar
Sergio Costas committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright 2003-2009 (C) Raster Software Vigo (Sergio Costas)
 * This file is part of FBZX
 *
 * FBZX is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * FBZX is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 *
Sergio Costas's avatar
Sergio Costas committed
18 19 20 21 22 23 24
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
Sergio Costas's avatar
Sergio Costas committed
25

26 27
#include "z80free/Z80free.h"
#include "emulator.hh"
Sergio Costas's avatar
Sergio Costas committed
28 29
#include "cargador.hh"
#include "computer.hh"
30
#include "llscreen.hh"
Sergio Costas's avatar
Sergio Costas committed
31 32
#include "menus.hh"
#include "microdrive.hh"
33 34
#include "computer.hh"
#include "cmdline.hh"
35
#include "llsound.hh"
36
#include "keyboard.hh"
37
#include "spk_ay.hh"
38
#include "mouse.hh"
Sergio Costas's avatar
Sergio Costas committed
39

40
bool debug_var = false;
Sergio Costas's avatar
Sergio Costas committed
41 42

Z80FREE procesador;
43 44 45 46 47
char    salir;
char    path_snaps[2049];
char    path_taps[2049];
char    path_mdrs[2049];
string  filenames[5];
Sergio Costas's avatar
Sergio Costas committed
48

49
string load_a_rom(string *filenames) {
50 51
	string *  pointer;
	int       offset = 0;
52
	ifstream *fichero;
53

54 55 56
	for (pointer = filenames; *pointer != ""; pointer++) {
		fichero = llscreen->myfopen(*pointer, ios::in | ios::binary);
		if (fichero == NULL) {
Sergio Costas's avatar
Sergio Costas committed
57 58
			return (*pointer);
		}
59 60
		fichero->read((char *) ordenador->memoria + offset, 16384);
		offset += 16384;
61 62
		fichero->close();
		delete fichero;
Sergio Costas's avatar
Sergio Costas committed
63
	}
64
	return "";
Sergio Costas's avatar
Sergio Costas committed
65 66 67
}

void load_rom(char type) {
68
	string    retval;
69
	ifstream *fichero;
Sergio Costas's avatar
Sergio Costas committed
70

71
	switch (type) {
Sergio Costas's avatar
Sergio Costas committed
72
	case 0:
73 74 75
		filenames[0] = "spectrum-roms/48.rom";
		filenames[1] = "";
		retval       = load_a_rom(filenames);
76
		if (retval != "") {
77
			printf("Can't load file %s\n", retval.c_str());
Sergio Costas's avatar
Sergio Costas committed
78 79
			exit(1);
		}
80 81
		break;

Sergio Costas's avatar
Sergio Costas committed
82
	case 1:
83 84 85 86
		filenames[0] = "spectrum-roms/128-0.rom";
		filenames[1] = "spectrum-roms/128-1.rom";
		filenames[2] = "";
		retval       = load_a_rom(filenames);
87
		if (retval != "") {
88
			printf("Can't load file %s\n", retval.c_str());
Sergio Costas's avatar
Sergio Costas committed
89 90
			exit(1);
		}
91 92
		break;

Sergio Costas's avatar
Sergio Costas committed
93
	case 2:
94 95 96 97
		filenames[0] = "spectrum-roms/plus2-0.rom";
		filenames[1] = "spectrum-roms/plus2-1.rom";
		filenames[2] = "";
		retval       = load_a_rom(filenames);
98
		if (retval != "") {
99
			printf("Can't load file %s\n", retval.c_str());
Sergio Costas's avatar
Sergio Costas committed
100 101
			exit(1);
		}
102 103
		break;

Sergio Costas's avatar
Sergio Costas committed
104 105
	case 3:
		// first, try last version of PLUS3 roms
106

107 108 109 110 111 112
		filenames[0] = "spectrum-roms/plus3-41-0.rom";
		filenames[1] = "spectrum-roms/plus3-41-1.rom";
		filenames[2] = "spectrum-roms/plus3-41-2.rom";
		filenames[3] = "spectrum-roms/plus3-41-3.rom";
		filenames[4] = "";
		retval       = load_a_rom(filenames);
113
		if (retval != "") {
Sergio Costas's avatar
Sergio Costas committed
114
			printf("Can't load the Spectrum +3 ROM version 4.1. Trying with version 4.0\n");
115 116 117 118 119 120
			filenames[0] = "spectrum-roms/plus3-40-0.rom";
			filenames[1] = "spectrum-roms/plus3-40-1.rom";
			filenames[2] = "spectrum-roms/plus3-40-2.rom";
			filenames[3] = "spectrum-roms/plus3-40-3.rom";
			filenames[4] = "";
			retval       = load_a_rom(filenames);
121
			if (retval != "") {
Sergio Costas's avatar
Sergio Costas committed
122
				printf("Can't load the Spectrum +3 ROM version 4.0. Trying with legacy filenames\n");
123 124 125 126 127 128
				filenames[0] = "spectrum-roms/plus3-0.rom";
				filenames[1] = "spectrum-roms/plus3-1.rom";
				filenames[2] = "spectrum-roms/plus3-2.rom";
				filenames[3] = "spectrum-roms/plus3-3.rom";
				filenames[4] = "";
				retval       = load_a_rom(filenames);
129
				if (retval != "") {
130
					printf("Can't load file %s\n", retval.c_str());
Sergio Costas's avatar
Sergio Costas committed
131 132 133 134
					exit(1);
				}
			}
		}
135 136
		break;

Sergio Costas's avatar
Sergio Costas committed
137
	case 4:
138 139 140 141
		filenames[0] = "spectrum-roms/128-spanish-0.rom";
		filenames[1] = "spectrum-roms/128-spanish-1.rom";
		filenames[2] = "";
		retval       = load_a_rom(filenames);
142
		if (retval != "") {
143
			printf("Can't load file %s\n", retval.c_str());
Sergio Costas's avatar
Sergio Costas committed
144 145
			exit(1);
		}
146
		break;
Sergio Costas's avatar
Sergio Costas committed
147
	}
148

149 150
	fichero = llscreen->myfopen("spectrum-roms/if1-2.rom", ios::in | ios::binary); // load Interface1 ROM
	if (fichero == NULL) {
151
		delete fichero;
Sergio Costas's avatar
Sergio Costas committed
152
		// try legacy name
153 154
		fichero = llscreen->myfopen("spectrum-roms/if1-v2.rom", ios::in | ios::binary);
		if (fichero == NULL) {
155
			delete fichero;
Sergio Costas's avatar
Sergio Costas committed
156 157 158 159
			printf("Can't open Interface1 ROM file\n");
			exit(1);
		}
	}
160 161 162
	fichero->read((char *) ordenador->shadowrom, 8192);
	fichero->close();
	delete fichero;
Sergio Costas's avatar
Sergio Costas committed
163 164 165
}

void end_system() {
166
	delete(llsound);
167
	delete(llscreen);
Sergio Costas's avatar
Sergio Costas committed
168 169
}

170
void load_main_game(const char *nombre) {
171
	int   longitud;
Sergio Costas's avatar
Sergio Costas committed
172
	char *puntero;
173

174 175
	longitud = strlen(nombre);
	if (longitud < 5) {
Sergio Costas's avatar
Sergio Costas committed
176 177
		return;
	}
178 179
	puntero = (char *) (nombre + (longitud - 4));
	if ((0 == strcasecmp(".z80", puntero)) || (0 == strcasecmp(".sna", puntero))) {
Sergio Costas's avatar
Sergio Costas committed
180 181 182
		load_z80(nombre);
		return;
	}
183

184
	if ((0 == strcasecmp(".tap", puntero)) || (0 == strcasecmp(".tzx", puntero))) {
185 186
		ordenador->current_tap = nombre;
		OOTape->load_file(nombre);
Sergio Costas's avatar
Sergio Costas committed
187 188 189 190
		return;
	}
}

191
void save_config() {
192 193
	char  config_path[1024];
	int   length;
Sergio Costas's avatar
Sergio Costas committed
194
	FILE *fconfig;
195

196 197 198 199 200 201 202 203
	strcpy(config_path, getenv("HOME"));
	length = strlen(config_path);
	if ((length > 0) && (config_path[length - 1] != '/')) {
		strcat(config_path, "/");
	}
	strcat(config_path, ".fbzx");
	fconfig = fopen(config_path, "wb");
	if (fconfig == NULL) {
Sergio Costas's avatar
Sergio Costas committed
204 205
		return;
	}
206 207 208 209 210 211 212 213 214 215 216
	fprintf(fconfig, "mode=%c%c", 48 + ordenador->current_mode, 10);
	fprintf(fconfig, "issue=%c%c", (ordenador->issue_3 ? 51 : 50), 10);
	fprintf(fconfig, "joystick=%c%c", 48 + keyboard->joystick, 10);
	fprintf(fconfig, "ay_sound=%c%c", 48 + spk_ay->ay_emul, 10);
	fprintf(fconfig, "interface1=%c%c", 48 + microdrive->mdr_active, 10);
	fprintf(fconfig, "doublescan=%c%c", ordenador->dblscan ? '1' : '0', 10);
	fprintf(fconfig, "volume=%c%c", 65 + (llsound->volume / 4), 10);
	fprintf(fconfig, "bw=%c%c", ordenador->bw ? '1' : '0', 10);
	fprintf(fconfig, "fast=%c%c", ordenador->tape_fast_load ? '1' : '0', 10);
	fprintf(fconfig, "turboload=%c%c", ordenador->turbo_play ? '1' : '0', 10);
	fprintf(fconfig, "mouse=%c%c", mouse->enabled ? '1' : '0', 10);
Sergio Costas's avatar
Sergio Costas committed
217 218 219
	fclose(fconfig);
}

220
void load_config() {
221 222 223 224 225 226 227 228 229 230 231 232 233 234
	char          config_path[1024];
	char          line[1024], carac, done;
	int           length, pos;
	FILE *        fconfig;
	unsigned char volume = 255, mode128k = 255, issue = 255, joystick = 255, ay_emul = 255, mdr_active = 255, dblscan = 255, bw = 255, fast = 255, turboload = 255, mouse_enabled = 255;

	strcpy(config_path, getenv("HOME"));
	length = strlen(config_path);
	if ((length > 0) && (config_path[length - 1] != '/')) {
		strcat(config_path, "/");
	}
	strcat(config_path, ".fbzx");
	fconfig = fopen(config_path, "rb");
	if (fconfig == NULL) {
Sergio Costas's avatar
Sergio Costas committed
235 236
		return;
	}
237

238 239 240 241
	done    = 1;
	pos     = 0;
	line[0] = 0;
	while (!feof(fconfig)) {
Sergio Costas's avatar
Sergio Costas committed
242
		if (done) {
243 244 245
			line[0] = 0;
			pos     = 0;
			done    = 0;
Sergio Costas's avatar
Sergio Costas committed
246
		}
247 248 249 250
		if (0 != fread(&carac, 1, 1, fconfig)) {
			if ((carac != 13) && (carac != 10)) {
				line[pos] = carac;
				if (pos < 1023) {
Sergio Costas's avatar
Sergio Costas committed
251 252 253 254 255
					pos++;
				}
				continue;
			}
		}
256 257 258
		done      = 1;
		line[pos] = 0;
		if (line[0] == '#') { // coment
Sergio Costas's avatar
Sergio Costas committed
259 260
			continue;
		}
261 262
		if (!strncmp(line, "mode=", 5)) {
			mode128k = line[5] - '0';
Sergio Costas's avatar
Sergio Costas committed
263 264
			continue;
		}
265 266
		if (!strncmp(line, "issue=", 6)) {
			issue = line[6] - '0';
Sergio Costas's avatar
Sergio Costas committed
267 268
			continue;
		}
269 270
		if (!strncmp(line, "joystick=", 9)) {
			joystick = line[9] - '0';
Sergio Costas's avatar
Sergio Costas committed
271 272
			continue;
		}
273 274
		if (!strncmp(line, "ay_sound=", 9)) {
			ay_emul = line[9] - '0';
Sergio Costas's avatar
Sergio Costas committed
275 276
			continue;
		}
277 278
		if (!strncmp(line, "interface1=", 11)) {
			mdr_active = line[11] - '0';
Sergio Costas's avatar
Sergio Costas committed
279 280
			continue;
		}
281 282
		if (!strncmp(line, "doublescan=", 11)) {
			dblscan = line[11] - '0';
Sergio Costas's avatar
Sergio Costas committed
283 284
			continue;
		}
285 286
		if (!strncmp(line, "volume=", 7)) {
			volume = 4 * (line[7] - 'A');
Sergio Costas's avatar
Sergio Costas committed
287 288
			continue;
		}
289 290
		if (!strncmp(line, "bw=", 3)) {
			bw = (line[3] - '0');
291 292
			continue;
		}
293 294
		if (!strncmp(line, "fast=", 5)) {
			fast = (line[5] - '0');
295 296
			continue;
		}
297 298
		if (!strncmp(line, "turboload=", 10)) {
			turboload = (line[10] - '0');
299 300
			continue;
		}
301 302
		if (!strncmp(line, "mouse=", 6)) {
			mouse_enabled = (line[6] - '0');
303 304
			continue;
		}
Sergio Costas's avatar
Sergio Costas committed
305
	}
306

307
	if (mode128k < 5) {
308
		switch (mode128k) {
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
		case 0:
			ordenador->current_mode = MODE_48K;
			break;

		case 1:
			ordenador->current_mode = MODE_128K;
			break;

		case 2:
			ordenador->current_mode = MODE_P2;
			break;

		case 3:
			ordenador->current_mode = MODE_P3;
			break;

		case 4:
			ordenador->current_mode = MODE_128K_SPA;
			break;
328
		}
Sergio Costas's avatar
Sergio Costas committed
329
	}
330
	if (issue < 4) {
331
		ordenador->issue_3 = issue == 3;
Sergio Costas's avatar
Sergio Costas committed
332
	}
333
	if (joystick < 4) {
334 335 336
		switch (joystick) {
		case 0:
			keyboard->joystick = JOYSTICK_CURSOR;
337 338
			break;

339 340
		case 1:
			keyboard->joystick = JOYSTICK_KEMPSTON;
341 342
			break;

343 344
		case 2:
			keyboard->joystick = JOYSTICK_SINCLAIR1;
345 346
			break;

347 348
		case 3:
			keyboard->joystick = JOYSTICK_SINCLAIR2;
349
			break;
350
		}
Sergio Costas's avatar
Sergio Costas committed
351
	}
352 353
	if (ay_emul < 2) {
		spk_ay->ay_emul = ay_emul;
Sergio Costas's avatar
Sergio Costas committed
354
	}
355 356
	if (mdr_active < 2) {
		microdrive->mdr_active = mdr_active;
Sergio Costas's avatar
Sergio Costas committed
357
	}
358 359
	if (dblscan < 2) {
		ordenador->dblscan = dblscan == 0 ? false : true;
Sergio Costas's avatar
Sergio Costas committed
360
	}
361 362
	if (bw < 2) {
		ordenador->bw = bw == 0 ? false : true;
363
	}
364 365
	if (fast < 2) {
		ordenador->tape_fast_load = fast == 0 ? false : true;
366
	}
367 368
	if (turboload < 2) {
		ordenador->turbo_play = turboload == 0 ? false : true;
369
	}
370 371
	if (mouse_enabled < 2) {
		mouse->enabled = mouse_enabled == 0 ? false : true;
372
	}
373
	if (volume < 255) {
374
		llsound->set_volume(volume);
Sergio Costas's avatar
Sergio Costas committed
375
	}
376

Sergio Costas's avatar
Sergio Costas committed
377 378 379
	fclose(fconfig);
}

380 381
int main(int argc, char *argv[]) {
	int    bucle, tstados, argumento, length;
382
	string gamefile;
383
	word   PC = 0;
Sergio Costas's avatar
Sergio Costas committed
384

385
	CMDLine parse(argc, argv);
386 387 388

	osd = new OSD();
	if ((!parse.mini) && (!parse.rotate)) {
389
		llscreen         = new LLScreen(640, 480, 0, 0, parse.db, parse.hw, parse.setres);
390 391
		llscreen->rotate = false;
	} else {
392
		llscreen         = new LLScreen(480, 640, 0, 0, parse.db, parse.hw, parse.setres);
393 394 395
		llscreen->rotate = true;
	}

Sergio Costas's avatar
Sergio Costas committed
396
	// by default, try all sound modes
397 398 399 400 401 402 403 404 405 406 407 408 409
	enum e_soundtype sound_type = SOUND_AUTOMATIC;
	if (parse.nosound) {
		sound_type = SOUND_NO;
	}
	if (parse.oss) {
		sound_type = SOUND_OSS;
	}
	if (parse.pulse) {
		sound_type = SOUND_PULSEAUDIO;
	}
	if (parse.alsa) {
		sound_type = SOUND_ALSA;
	}
410

411 412 413 414
	llsound    = new LLSound(sound_type);
	OOTape     = new Tape();
	keyboard   = new Keyboard();
	ordenador  = new computer();
415
	microdrive = new Microdrive();
416 417
	spk_ay     = new SPK_AY();
	mouse      = new Mouse();
418

419
	load_config();
420 421 422 423 424 425

	ordenador->zaurus_mini = 0;
	if (parse.mini) {
		ordenador->zaurus_mini = 1;
	} else if (parse.rotate) {
		ordenador->zaurus_mini = 2;
Sergio Costas's avatar
Sergio Costas committed
426
	}
427 428 429

	if (parse.ds) {
		ordenador->dblscan = true;
Sergio Costas's avatar
Sergio Costas committed
430
	}
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
	if (parse.ss) {
		ordenador->dblscan = false;
	}

	if (parse.bw) {
		ordenador->bw = true;
	}
	if (parse.color) {
		ordenador->bw = false;
	}
	screen = new Screen();

	gamefile = "";

	printf("Computer init\n");
446
	printf("Modo: %d\n", ordenador->current_mode);
447 448 449

	atexit(end_system);

450
	if (parse.fs) {
451
		llscreen->fullscreen_switch();
Sergio Costas's avatar
Sergio Costas committed
452
	}
453

454
	ordenador->interr = 0;
Sergio Costas's avatar
Sergio Costas committed
455 456 457

	// assign initial values for PATH variables

458 459 460 461
	strcpy(path_snaps, getenv("HOME"));
	length = strlen(path_snaps);
	if ((length > 0) && (path_snaps[length - 1] != '/')) {
		strcat(path_snaps, "/");
462
	}
463 464
	strcpy(path_taps, path_snaps);
	strcpy(path_mdrs, path_snaps);
465
	ordenador->current_tap = "";
Sergio Costas's avatar
Sergio Costas committed
466 467 468

	// assign random values to the memory before start execution

469
	printf("Reset memory\n");
470 471 472 473
	printf("Modo: %d\n", ordenador->current_mode);
	for (bucle = 0; bucle < 196608; bucle++) {
		ordenador->memoria[bucle] = (unsigned char) rand();
	}
Sergio Costas's avatar
Sergio Costas committed
474

475
	printf("Memory resetted\n");
476
	printf("Modo: %d\n", ordenador->current_mode);
477

478
	salir = 1;
479

480
	printf("Init microdrive\n");
481

482
	printf("Reset computer\n");
Sergio Costas's avatar
Sergio Costas committed
483 484 485 486
	ResetComputer();

	sleep(1);

487
	printf("Reset screen\n");
488
	llscreen->clear_screen();
Sergio Costas's avatar
Sergio Costas committed
489

490
	if (llsound->sound_aborted) {
491
		osd->set_message("Running without sound (read the FAQ)", 2000);
Sergio Costas's avatar
Sergio Costas committed
492 493
	}

494
	printf("Modo: %d\n", ordenador->current_mode);
495
	printf("load main game\n");
496
	load_main_game(parse.gamefile.c_str());
497
	printf("Modo: %d\n", ordenador->current_mode);
Sergio Costas's avatar
Sergio Costas committed
498

499
	osd->set_message("Press F1 for help", 4000);
Sergio Costas's avatar
Sergio Costas committed
500

501
	debug_var = false;
502

503
	while (salir) {
Sergio Costas's avatar
Sergio Costas committed
504
		do {
505
			ordenador->contended_cicles = 0;
506 507 508
			tstados = Z80free_ustep(&procesador);
			if ((tstados - ordenador->contended_cicles) < 0) {
				printf("Error %X\n", procesador.PC);
Sergio Costas's avatar
Sergio Costas committed
509 510
				exit(1);
			}
511
			ordenador->emulate(tstados - ordenador->contended_cicles); // execute the whole hardware emulation for that number of TSTATES
512 513
		} while (procesador.Status != Z80XX);
		PC = procesador.PC;
514

Sergio Costas's avatar
Sergio Costas committed
515
		/* if PC is 0x0556, a call to LD_BYTES has been made, so if
516
		 * FAST_LOAD is 1, we must load the block in memory and return */
Sergio Costas's avatar
Sergio Costas committed
517

518 519 520
		if ((!microdrive->mdr_paged) && (PC == 0x0556) && (ordenador->tape_fast_load) && (ordenador->page48k == 1)) {
			if (ordenador->current_tap != "") {
				// procesador.Rm.br.F &= ~F_Z;
521 522
				do_fast_load();
			} else {
523
				osd->set_message("No TAP/TZX file selected", 2000);
Sergio Costas's avatar
Sergio Costas committed
524
			}
525
			continue;
526
		}
527

Sergio Costas's avatar
Sergio Costas committed
528
		/* if PC is 0x04C2, a call to SA_BYTES has been made, so if
529
		 * we want to save to the TAP file, we do it */
530

531 532 533
		if ((!microdrive->mdr_paged) && ((PC == 0x04C2) || (PC == 0x04C6)) && (ordenador->tape_write == 1) && (ordenador->page48k == 1)) {
			if (ordenador->current_tap == "") {
				osd->set_message("No TAP/TZX file selected", 2000);
Sergio Costas's avatar
Sergio Costas committed
534 535 536
				continue;
			}

537
			uint8_t *data;
538 539
			uint8_t  op_xor;
			uint8_t  dato;
540
			uint32_t length;
541
			int      pointer;
542

543 544 545
			if (PC == 0x04C2) {
				do_push(0x053F); // return address to SA/LD-RET
			}
546

547
			length  = (uint32_t) (procesador.Rm.wr.DE);
548 549
			length += 2;

550 551
			data            = new uint8_t[length];
			pointer         = 0;
552 553 554 555 556 557
			data[pointer++] = procesador.Rm.br.A; // flag

			op_xor = procesador.Rm.br.A;

			salir = 0;
			do {
558
				if (procesador.Rm.wr.DE == 0) {
559
					salir = 2;
560
				}
561
				if (!salir) {
562 563
					dato            = ordenador->read_memory(procesador.Rm.wr.IX); // read data
					op_xor         ^= dato;
564 565 566 567 568 569 570 571
					data[pointer++] = dato;
					procesador.Rm.wr.IX++;
					procesador.Rm.wr.DE--;
				}
			} while (!salir);
			data[pointer] = op_xor;
			procesador.Rm.wr.IX++;
			procesador.Rm.wr.IX++;
572 573
			OOTape->add_block(data, length);
			ordenador->other_ret = 1; // next instruction must be RET
574
			continue;
Sergio Costas's avatar
Sergio Costas committed
575
		}
576

577
		/* if ordenador->mdr_paged is 2, we have executed the RET at 0x0700, so
578
		 * we have to return to the classic ROM */
579

580
		if (microdrive->mdr_paged == 2) {
581 582
			microdrive->mdr_paged = 0;
		}
583

Sergio Costas's avatar
Sergio Costas committed
584
		/* if PC is 0x0008 or 0x1708, and we have a microdrive, we have to page
585
		 * the Interface 1 ROM */
586

587
		if (((PC == 0x0008) || (PC == 0x1708)) && (microdrive->mdr_active)) {
588
			microdrive->mdr_paged = 1;
589
		}
590

Sergio Costas's avatar
Sergio Costas committed
591
		/* if PC is 0x0700 and we have a microdrive, we have to unpage
592
		 * the Interface 1 ROM after the last instruction */
593

594
		if ((PC == 0x0700) && (microdrive->mdr_active)) {
595
			microdrive->mdr_paged = 2;
596
		}
Sergio Costas's avatar
Sergio Costas committed
597

598 599 600
		if (ordenador->interr >= 1) {
			keyboard->read_keyboard(NULL); // read the physical keyboard
			ordenador->interr = 0;
Sergio Costas's avatar
Sergio Costas committed
601 602 603
		}
	}

604
	save_config();
Sergio Costas's avatar
Sergio Costas committed
605 606
	return 0;
}
607

608
void print_status() {
609 610 611 612 613
	printf("\nPC: 0x%04X   SP:0x%04X\n", procesador.PC, procesador.Rm.wr.SP);
	printf("AF: 0x%04X   BC: 0x%04X   DE: 0x%04X   HL: 0x%04X\n", procesador.Rm.wr.AF, procesador.Rm.wr.BC, procesador.Rm.wr.DE, procesador.Rm.wr.HL);
	printf("AF': 0x%04X  BC': 0x%04X  DE': 0x%04X  HL': 0x%04X\n", procesador.Ra.wr.AF, procesador.Ra.wr.BC, procesador.Ra.wr.DE, procesador.Ra.wr.HL);
	printf("IX: 0x%04X   IY: 0x%04X   IX': 0x%04X  IY': 0x%04X\n", procesador.Rm.wr.IX, procesador.Rm.wr.IY, procesador.Ra.wr.IX, procesador.Ra.wr.IY);
	printf("IFF1: %X   IFF2: %X   I: %X   R: %X\n\n\n", procesador.IFF1, procesador.IFF2, procesador.I, procesador.R | procesador.R2);
614 615 616 617
}

void do_push(uint16_t value) {
	procesador.Rm.wr.SP -= 2;
618
	ordenador->write_memory(procesador.Rm.wr.SP, (uint8_t) (value & 0xFF));
619
	value >>= 8;
620
	ordenador->write_memory(procesador.Rm.wr.SP + 1, (uint8_t) (value & 0xFF));
621 622
}

623 624
void do_fast_load() {
	if (!(procesador.Rm.br.F & F_C)) { // if Carry=0, is VERIFY, so return OK
625 626
		do_push(0x053F);            // LD_BYTES pushes this address in the stack to return through it
		procesador.Rm.br.F  |= F_C; // verify OK
627
		procesador.Rm.wr.IX += procesador.Rm.wr.DE;
628 629
		procesador.Rm.wr.DE  = 0;
		ordenador->other_ret = 1; // next instruction must be RET
630 631 632
		return;
	}

633
	uint16_t size;
634 635
	uint8_t  flag = procesador.Rm.br.A;
	uint8_t  data[65538];
636
	uint16_t counter;
637

638 639
	while (true) {
		enum FastLoadReturn retval = OOTape->fast_read(data, size, flag);
640 641 642

		switch (retval) {
		case FASTLOAD_NO_TAPE:
643
			procesador.Rm.br.F  &= (~F_C); // Load error
644
			procesador.Rm.wr.IX += procesador.Rm.wr.DE;
645 646 647 648
			procesador.Rm.wr.DE  = 0;
			osd->set_message("No tape selected", 2000);
			do_push(0x053F);          // LD_BYTES pushes this address in the stack to return through it
			ordenador->other_ret = 1; // next instruction must be RET
649
			return;
650 651 652

			break;

653
		case FASTLOAD_NO_BLOCK:
Sergio Costas's avatar
Sergio Costas committed
654
			if (OOTape->get_pause()) {
655
				osd->set_message("Can't do fast load. Press F6 to play", 2000);
Sergio Costas's avatar
Sergio Costas committed
656
			}
657
			ordenador->other_ret = 0; // next instruction must NOT be RET
658
			return;
659 660 661

			break;

662
		case FASTLOAD_END_TAPE:
663
			procesador.Rm.br.F  &= (~F_C); // Load error
664
			procesador.Rm.wr.IX += procesador.Rm.wr.DE;
665 666 667 668
			procesador.Rm.wr.DE  = 0;
			osd->set_message("End of tape. Rewind it.", 2000);
			do_push(0x053F);          // LD_BYTES pushes this address in the stack to return through it
			ordenador->other_ret = 1; // next instruction must be RET
669
			return;
670

671
		case FASTLOAD_OK:
672 673 674 675 676
			counter = 0;
			while (true) {
				if ((size == 0) || (procesador.Rm.wr.DE == 0)) {
					break;
				}
677
				ordenador->write_memory(procesador.Rm.wr.IX, (byte) data[counter]); // store the byte
678 679 680 681 682
				procesador.Rm.wr.IX++;
				procesador.Rm.wr.DE--;
				counter++;
				size--;
			}
683 684 685 686 687 688 689 690
			procesador.Rm.wr.AF  = 0x0093;
			procesador.Rm.br.H   = 0;
			procesador.Ra.wr.AF  = 0x0145;
			procesador.Rm.wr.BC  = 0xB001;
			procesador.IFF1      = 0;
			procesador.IFF2      = 0;
			ordenador->other_ret = 1; // next instruction must be RET
			do_push(0x053F);          // LD_BYTES pushes this address in the stack to return through it
691 692
			if (size == 0) {
				if (procesador.Rm.wr.DE == 0) {
693
					procesador.Rm.br.F |= (F_C); // Load OK
694 695 696
					return;
				}
			}
697
			procesador.Rm.br.F &= (~F_C); // Load error
698
			return;
699 700 701

			break;

702
		case FASTLOAD_NO_FLAG:
703
			continue;
704 705
			break;

706
		case FASTLOAD_NODATA:
707
			continue;
708
			break;
709 710 711
		}
	}
}