/*
 *  a2, an Apple II emulator in C
 *  (c) Copyright 1990 by Rich Skrenta
 *
 *  Command line interface written by Tom Markson
 *
 *  Distribution agreement:
 *
 *	You may freely copy or redistribute this software, so long
 *	as there is no profit made from its use, sale, trade or
 *	reproduction.  You may not change this copyright notice,
 *	and it must be included prominently in any copy made.
 *
 *  Send emulator related mail to:  skrenta@blekko.commodore.com
 *				    skrenta@blekko.uucp
 */


#include	<stdio.h>
#include	<fcntl.h>
#include	"a2.h"

#define		jump_check(a,b,c,d)	(mem[Pc]==a && mem[Pc+1]==b && mem[Pc+2]==c && mem[Pc+3]==d)

/*
 *  In order to improve performance, we intercept the PC on JSR's
 *  and JMP's to certain locations and handled the intented function
 *  in C instead of letting the emulator interpret 6502.
 *
 *  This is done for video output, to produce an acceptable scroll;
 *  for the boot prom, so it crashes if there is no disk in the drive;
 *  and for other miscellaneous routines such as WAIT for speed.
 *
 *  In most cases the interceptor routine checks to see if the code it's
 *  intercepting looks like what it thinks should be there; it doesn't
 *  snatch the PC if the first four bytes of the routine don't match.
 */


#define		I_WAIT		1	/* defeat ROM WAIT routine           */
#define		I_PRODOS	2	/* Prodos high level intercept       */
#define		I_RWTS		3	/* DOS 3.3 high-level intercept      */
#define		I_BELL		4	/* don't toggle C030, output a bell  */
#define		I_VIDOUT	5	/* speeds up scrolling tremendously  */
#define		I_BOOT		6	/* crash if no disk on boot          */
#define		I_BOOTWAIT	7	/* defeat delay loops in DOS boot    */
#define		I_BOOTPATCH	8	/* patch dos for fast raw access     */


extern int map_to_upper;
extern unsigned char disk_ref();


set_special_jumps() {
extern int set_c0();
extern int set_writep();

	mem[0x43] = 0x60;			/* for ProDos boot */

	jmp_tbl[0xBD00] = I_RWTS;
	jmp_tbl[0xC600] = I_BOOT;
	jmp_tbl[0xC680] = I_PRODOS;	/* patched into boot prom below */
	jmp_tbl[0x9D84] = I_BOOTPATCH;	/* fast raw dos access */
	jmp_tbl[0xFBD9] = I_BELL;
	jmp_tbl[0xFBFD] = I_VIDOUT;
	jmp_tbl[0xFCA8] = I_WAIT;

	jmp_tbl[0x3A00] = I_BOOTWAIT;

	mem_set[0xC0] = set_c0;
	mem_set[0xC6] = set_writep;	/* write protect disk prom */

#if 0
	mem[0xC600] = 0;		/* flag for boot interception */
	mem[0xC601] = 0x20;		/* disk prom magic number */
	mem[0xC603] = 0x00;
	mem[0xC605] = 0x03;
	mem[0xC607] = 0x3C;
#endif

/*
 *  Patch boot rom for fake Prodos driver
 */

	mem[0xC6FF] = 0x80;		/* C680 is driver address */
	mem[0xC6FE] = 0x1F;		/* info about device */

	screen_setup();
}


jump(key)
int key;
{
int i;

	switch (key) {
	case I_WAIT:					/* FCA8 */
		if (jump_check(0x38, 0x48, 0xE9, 0x01)) {
			A = 0;
			N = 0;
			V = 0;
			C = 1;
			DO_RTS;
		}
		break;

	case I_PRODOS:					/* C680 */
		prodos();
		break;

	case I_RWTS:					/* BD00 */
		if (jump_check(0x84, 0x48, 0x85, 0x49))
			rwts();
		break;

	case I_BELL:					/* FBD9 */
		if (jump_check(0x60, 0x87, 0xD0, 0x12)) {
			putchar(7);
			fflush(stdout);
			DO_RTS;
		}
		break;

	case I_VIDOUT:					/* FBFD */
		if (jump_check(0xC9, 0xA0, 0xB0, 0xEF))
			vidout();
		break;

	case I_BOOT:					/* C600 */
		if (disk[0] < 0) {
			info("boot: no disk");
			PCINC;			/* BRK into the monitor */
			push(high(Pc));
			push(low(Pc));
			B = 1;
			push(get_status());
			Pc = mem[0xFFFE] | (mem[0xFFFF] << 8);
			return;
		}
		info("boot");

/* 
 *  We read the second half of a 512 byte block in case we're
 *  booting something that depends on this being a newer boot prom
 */

		drive = 0;
		read_disk(0, 14, &mem[0x900]);
		break;

/*
 *  Standard DOS 3.3 has some pretty gross delay loops in its
 *  boot code.  The following patches defeat two of them.
 *  This could be dangerous; it seems to work, but DOS's original
 *  side effects are not maintained.  Comment out the jmp_tbl assignment
 *  of I_BOOTWAIT above if you are distrustful.
 *
 *  Interesting.  Dos relocates the patches when it moves into higher
 *  memory.  If you boot with a fast-booting dos that doesn't have the
 *  delays at 3A00, but still has them at BA00 & BD9E when it starts
 *  up, it will be slow if you turn off RWTS interception and use the
 *  raw interface.  However, slow-booting real DOS that gets patched
 *  while it's booting will have a faster raw interface, since it
 *  relocated the patches...
 */

	case I_BOOTWAIT:				/* 3A00 */
		if (jump_check(0xA2, 0x11, 0xCA, 0xD0)) {
			mem[0x3A00] = 0x60;		/* RTS */
			if (mem[0x3D9E] == 0xA0
			&&  mem[0x3D9F] == 0x12
			&&  mem[0x3DA0] == 0x88) {
				mem[0x3D9E] = 0x4C;	/* JMP past it */
				mem[0x3D9F] = 0xAB;
				mem[0x3DA0] = 0x3D;	/* gets relocated */
			}
		}
		break;

/*
 *  This one is unnecessary since we do high-level RWTS interception
 */

	case I_BOOTPATCH:				/* 9D84 */
		if (jump_check(0xAD, 0xE9, 0xB7, 0x4A)) {
			if (mem[0xBA00] == 0xA2
			&&  mem[0xBA01] == 0x11
			&&  mem[0xBA02] == 0xCA) {
				mem[0xBA00] = 0x60;		/* RTS */
				if (mem[0xBD9E] == 0xA0
				&&  mem[0xBD9F] == 0x12
				&&  mem[0xBDA0] == 0x88) {
					mem[0xBD9E] = 0x4C;
					mem[0xBD9F] = 0xAB;
					mem[0xBDA0] = 0xBD;
				}
			}
		}
		break;

	default:
		fprintf(stderr, "bad jump intercept key: %d\n", key);
		assert(FALSE);
	}
}


static int key_clear = TRUE;
static unsigned char last_key = 0;
static unsigned char temp_key;

extern int save_flags;

unsigned char
mem_map(a)
unsigned short a;
{

	switch (a) {
	case 0xC000:
		if (key_clear) {
			fcntl (0, F_SETFL, save_flags | O_NDELAY);
	  
			if (read (0, &temp_key, 1) == 1) {
				key_clear = FALSE;
				if (temp_key == '\n')
					temp_key = '\r';
				else if (temp_key == 127)
					temp_key = 8;
				if (map_to_upper)
					temp_key = toupper(temp_key);
				last_key = temp_key | 0x80;
			}
	  
			fcntl (0, F_SETFL, save_flags);
		}
		return(last_key);

	case 0xC010:
		key_clear = TRUE;
		last_key &= 0x7F;
		return(0);			/* what should this be? */

	case 0xC011:
		if (bank2_enable)
			return(0xFF);
		return(0x00);

	case 0xC012:
		if (ram_read)
			return(0xFF);
		return(0x00);

	case 0xC080: case 0xC081: case 0xC082: case 0xC083:
	case 0xC088: case 0xC089: case 0xC08A: case 0xC08B:
		ram_card(a);
		return(0x00);

/*
 *  Slot 6 Disk II memory map 
 */

	case 0xC0E0: case 0xC0E1: case 0xC0E2: case 0xC0E3:
	case 0xC0E4: case 0xC0E5: case 0xC0E6: case 0xC0E7:
	case 0xC0E8: case 0xC0E9: case 0xC0EA: case 0xC0EB:
	case 0xC0EC: case 0xC0ED: case 0xC0EE: case 0xC0EF:
		return( disk_ref(a, 0) );

#if 0
/*
 *  Keep the boot prom magic number from appearing if there is
 *  no disk in the drive
 */

	case 0xC600:
	case 0xC601:
		if (disk[0] < 0)
			return(0);
		break;
#endif

	}

	return(mem[a]);		/* default */
}


set_c0(a, n)
unsigned short a;
unsigned char n;
{

	switch (a & 0xFF) {
	case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15:
	case 0x16: case 0x17: case 0x18: case 0x19: case 0x1A: case 0x1B:
	case 0x1C: case 0x1D: case 0x1E: case 0x1F:
		key_clear = TRUE;
		last_key &= 0x7F;
		break;

	case 0x80: case 0x81: case 0x82: case 0x83:
	case 0x88: case 0x89: case 0x8A: case 0x8B:
		ram_card(a);
		break;

/*
 *  Slot 6 Disk II memory map 
 */

	case 0xC0E0: case 0xC0E1: case 0xC0E2: case 0xC0E3:
	case 0xC0E4: case 0xC0E5: case 0xC0E6: case 0xC0E7:
	case 0xC0E8: case 0xC0E9: case 0xC0EA: case 0xC0EB:
	case 0xC0EC: case 0xC0ED: case 0xC0EE: case 0xC0EF:
		disk_ref(a, n);
		break;

	default:
		mem[a] = n;
	}
}


