#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include "memtype.h"
#include "mem.h"
#include "6502.h"
#include "atari.h"
#include "atarimap.h"
#include "argparse.h"


/* Monitor/debugger for Atari Emulator
 *  by Frank Barrus
 */

extern char errmsg[];


static int getnum(char *s, char **sp, int base)
{
int n,neg;
int x;
int okay;
	while(*s == ' ' || *s == '\t')
		s++;
	if(s[0] == '0') {
		switch(s[1]) {
		case 't': case 'T':
			base = 10;
			s += 2;
			break;
		case 'x': case 'X':
			base = 16;
			s += 2;
			break;
		case 'o': case 'O':
			base = 8;
			s += 2;
			break;
		case 'y': case 'Y':
			base = 2;
			s += 2;
		}
	}
	n = 0;
	if(*s == '-') {
		neg = -1;
		s++;
	} else
		neg = 1;
	okay = 0;
	while(1) {
		x = -1;
		if(*s >= '0' && *s <= '9')
			x = *s-'0';
		else if(*s >= 'a' && *s <= 'z')
			x = *s-'a'+10;
		else if(*s >= 'A' && *s <= 'Z')
			x = *s-'A'+10;
		if(x < 0 || x >= base)
			break;
		okay++;
		n = n*base + x;
		s++;
	}
	if(okay)
		*sp = s;
	return(n*neg);
}



void cpu_monitor(void)
{
char buf[256];
char fmt[80];
char *str,*s1;
int n;
word adr, hasadr;
word len, haslen;
char area;
char cmd;
int x;
byte v1,v2;
int radix;
	printf("type $h for help\n");
	area = cmd = 0;
	adr = 0;
	len = 0;
	radix = 16;
	while(1) {
		hasadr = False;
		haslen = False;
		printf("> ");
		if(!fgets(buf,sizeof(buf),stdin))
			return;
		buf[strlen(buf)-1] = '\0';
		str = buf;
		n = getnum(s1 = str,&str,radix);	
		if(str != s1) {
			hasadr = True;
			adr = n;
		}
		if(*str == ',') {
			n = getnum(s1 = (str+1),&str,radix);
			if(str != s1) {
				haslen = True;
				len = n;
			}
		}
		while(*str == ' ' || *str == '\t')
			str++;
		if(*str) 
			area = *str++;
		if(*str) {
			strcpy(fmt,str);
			cmd = *str++;
		}
		x = 0;
		if(area == '>') {
			switch(cmd) {
			case 'a':  cpu.a = LO(adr); break;
			case 'x':  cpu.x = LO(adr); break;
			case 'y':  cpu.y = LO(adr); break;
			case 'f':  cpu.flags = LO(adr); break;
			case 's':  cpu.sp = LO(adr); break;
			case 'p':  cpu.pc = adr&0xffff; break;
			case 'm':  sys.flags = adr; break;
			case 'c':  cpu.tcycles = adr; break;
			}
			continue;
		}
		if(area == ':') {
			n = getnum(str,&str,radix);
			if(hasadr)
				cpu.pc = adr;
			switch(cmd) {
			case 's':
				if(!n)
					n = 1;
				cpu.sync = False;
				cpu.halt = False;
				TRACE_CPU(n);
				break;
			case 't':
				if(!n)
					n = 1;
				SHOWTRACE_CPU(n);
				break;
			case 'r':
				if(!n)
					n = 0x7fffffff;
				cpu.sync = False;
				cpu.halt = False;
				RUN_CPU(n);
				break;
			case 'g':
				if(!n)
					n = 0x7fffffff;
				cpu.sync = False;
				cpu.halt = False;
				atari_run(n);
				break;
			}
			continue;
		}
		if(area == '$') {
			switch(cmd) {
			case 'h':
				system("more monhelp.txt");
				break;
			case 'D':
				system("ls -s bin");
				break;
			case 'd':
				radix = adr;
				break;
			case 'r':
				printf(
	"pc=%04x sp=%02x a=%02x x=%02x y=%02x %s  m=%04x count=%d\n",
				cpu.pc, cpu.sp, cpu.a, cpu.x, cpu.y,
				flagstr(cpu.flags),sys.flags,cpu.tcycles);
				break;
			case 'q':
				return;
			case 'f': 
				n = getnum(str,&str,radix);
			 	for(x=0; x<len; x++) {
					word w;
					MEM_WREAD(adr+x, w);
					if(w == n)
						printf("%04x ", adr+x);
				}
				printf("\n");
				break;
			case 'l': case 'L': {
				FILE *f;
				byte buf[65536];
				word i,start,end;

				if(!(f = fopen(str,"r"))) {
					printf("load '%s' failed\n", str);
					break;
				}
				if(haslen)
					n = len;
				else
					n = 0xffff;
				n = fread(buf,1,n,f);
				fclose(f);	
				if(cmd == 'l') {
					for(x=0; x<len; x++)
						MEM_WRITE(adr+x,buf[x]);
					break;
				}
				if(buf[0] != 0xff || buf[1] != 0xff) {
					printf("Invalid file format\n");
					break;
				}	
				i = 2;
				while(i < n) {
					MEM_WWRITE(W_INITAD,0);
					start = WORD(buf[i],buf[i+1]);
					if(start == 0xffff) {
						i += 2;
						start = WORD(buf[i],buf[i+1]);
					}
					end = WORD(buf[i+2],buf[i+3]);
				 	i += 4;
					printf("%04x-%04x: %04x bytes\n",
						start, end, end-start+1);
					while(start <= end) {
						if(i >= n) {
							printf(
							"Unexpected EOF\n");
							break;
						}
						MEM_WRITE(start,buf[i]);
						start++; i++;
					}
					MEM_WREAD(W_INITAD, start);
					if(start) {
						printf(
						"init = (0x02e2) = %04x\n",
								start);
						MEM_SWRITE(cpu.sp,0);
						cpu.sp--;
						MEM_SWRITE(cpu.sp,0);
						cpu.sp--;
						cpu.pc = start;
						atari_run(0x7fffffff);
					}
				}
				MEM_WREAD(W_RUNAD, end);
				printf("run  = (0x02e0) = %04x\n", end);
				MEM_SWRITE(cpu.sp,0); cpu.sp--;
				MEM_SWRITE(cpu.sp,0); cpu.sp--;
				cpu.pc = end;
				atari_run(0x7fffffff);
				break;
			 }	
			case 's': case 'S': {
				FILE *f;
				byte buf[65536];

				if(!(f = fopen(str,"w"))) {
					printf("save '%s' failed\n", str);
					break;
				}
				x = 0;
				for(n=0; n<len; n++,x++) {
				    MEM_READ(adr+x, buf[n]);
				    if(cmd=='S' && (n%125==0))
					x += 4;
				}
				fwrite(buf,1,len,f);
				fclose(f);
				break;
			}
			case 'v':
				atari_video_refresh();
				break;
			}
			continue;
		}
		if(area == '=') {
			printf("\t%04x (%d)\n", adr, adr);
			continue;
		}
		if(area == '!') {
			system(--str);
			continue;
		}
		if(area == '@') {
			if(!parse_options(--str)) 
				printf("%s\n", errmsg);
			continue;
		}
		if(cmd == 'w') {
			while(1) {
				n = getnum(s1 = str,&str,radix);
				if(str == s1)
					break;
				MEM_WRITE(adr,n); adr++;
			}
			continue;
		}
		if(cmd == 'W') {
			while(1) {
				n = getnum(s1 = str,&str,radix);
				if(s1 == str)
					break;
				MEM_WWRITE(adr,n);
				adr += 2;
			}
			continue;
		}
		for(n=0; n<len; n++) {
		    if(!x)
			printf("%04x: ", adr);
	  	    s1 = fmt;
		    while(*s1) {
			word w;
			byte b;
		        switch(*s1++) {
			case 'i':
				adr = DISASSEMBLE(adr);
				printf("\n");
				x = 0;
				continue;
			case 'x':
				MEM_READ(adr, b); adr++;
				printf("%02x ", b);
				break;
			case 'X':
				MEM_WREAD(adr, w); adr += 2;
				printf("%04x ", w);
				x++;
				break;
			case 'd':
				MEM_READ(adr, b); adr++;
				printf("%02d ", b);
				break;
			case 'D':
				MEM_WREAD(adr, w); adr += 2;
				printf("%04d ", w);
				x++;
				break;
			case 'c': case 'C':
				MEM_READ(adr, v1); adr++;
				v2 = v1&0x7f;
				if(cmd == 'C') {
					if(v2 < 64)
						v2 += 32;
					else if(v2 < 96)
						v2 -= 64;
				}
				if(v2 < 32)
					v2 = '@';
				if(v1 >= 128) 
					printf("\033[7m%c\033[0m", v2);
				else
					printf("%c",v2);
				break;
			}
			x++;
			if(cmd == 'c' || cmd == 'C') {
				if(x>=64) {
					printf("\n");
					x = 0;
				}
			} else {
				if(x>=16) {
					printf("\n");
					x = 0;
				}
			}
		    }
		}
		printf("\n");
	}
}

