/***********************************************
Low level I/O to the simulator
 
	PORTABLE STD. ANSI
************************************************/

#ifndef INLINE
#define	INLINE
#endif

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifdef THINK_C
#include <pascal.h>
#endif

#include "sim_io.h"
#include "sim_irq.h"
#include "symbols.h"
#include "options.h"

/** local functions **/
void str_limit(char *dst,char *src,long n_chars);

/* globals */
m6811	state;
byte *	memory;
short * breaks;

char *msg;    /* char buffer for text-handling operations */
long n_chars;

Boolean Loaded = FALSE;
long caught_signal = 0;




/*
**  by Mark Coniglio (troika@panix.com) */
short next_goes_into_subroutine(short *oplen)
{
    Boolean result = FALSE;
    byte    opcode = memory[get_pc(&state)];

    if (opcode == 0x8D                          /* bsr */
        || opcode == 0x9D                       /* jsr direct */
        || opcode == 0xAD                       /* jsr ind,x */
        || opcode == 0xBD                       /* jsr extended */

        /* jsr ind,y */
        || (opcode == 0x18 && memory[get_pc(&state) + 1] == 0xAD) )  {

            *oplen = (opcode == 0xBD) ? 3 : 2;
            result = TRUE;
        }

    return result;
}


Boolean hexScan(char *str,short *val) /* my fast hex conversion */
{
register char c1 = *str;
register char c2 = *(str+1);
register short t = 0;

	if (c1>='0' && c1<='9') t += (c1-'0')<<4;
	else if (c1>='a' && c1<='f') t += (c1-'a'+10)<<4;
	else if (c1>='A' && c1<='F') t += (c1-'A'+10)<<4;
	else return FALSE;
	if (c2>='0' && c2<='9') t += c2-'0';
	else if (c2>='a' && c2<='f') t += c2-'a'+10;
	else if (c2>='A' && c2<='F') t += c2-'A'+10;
	else return FALSE;
	*val = t;
	return TRUE;
}

Boolean longHexScan(char *s,long *val)
{
int		n = 0;
long	tmp = 0;
	
	if (sscanf(s,"%4lx%n",&tmp,&n)) {
		s += n;
		if (strlen(s)==0) { 
			*val = tmp;
			return TRUE;
		}
	}
	return FALSE;
}

void str_limit(char *dst,char *src,long n_chars)
{
	n_chars -= 2;
	if (strlen(src)>n_chars) {
		strncpy(dst,src,n_chars);
		*(dst + n_chars++) = '.';
		*(dst + n_chars++) = '.';
		*(dst + n_chars) = '\0';
	} else strcpy(dst,src);
}


char * show_breaks()
{
register long addr;
register short b;
register char *tmp = msg;
char * s;

	tmp += sprintf(tmp,"Breakpoints (at t=%ld)",state.t);
    for (addr=0;addr<65536;addr++) {
    	b = *(breaks+addr);
		if (b)
		{	if ((s = lookup_val(addr)))
			{
				if (b<0)
					tmp += sprintf(tmp,"\r   %s (%04lx) ", s, 0xffff&addr);
				else 
					tmp += sprintf(tmp,"\r   %s (%04lx) [%d] ", s, 0xffff&addr, b);
			}
			else
			{
				if (b<0)
					tmp += sprintf(tmp,"\r   %04lx ", 0xffff&addr);
				else 
					tmp += sprintf(tmp,"\r   %04lx [%d] ", 0xffff&addr, b);
			}
		}
    }
    tmp += sprintf(tmp,"\r");
    return msg;
}

char * multi_disassemble(m6811 *state,byte *memory, word add,long n)
{
char * tmp = msg;
char * s, c1, c2;

	tmp += sprintf(tmp," Disassebly from %04lx (%ld lines) at t=%ld.\r",add,n,state->t);
	while (n--) 
	{
	    if (state->pc == add)		/* PC marker */
		{	c1='-'; c2='>'; }
		else
		{   c1=' '; c2=' '; }

		if (breaks[add]) c1='*';	/* breakpoint marker */
		
		if (Options.symbols && (s = lookup_val(add))) {
	    	/* both label and address */
		/* FIXME: in format, 0 and - appear and 0 is used with %s */
			tmp += sprintf(tmp,"\r   %-08s ", s);
			tmp += sprintf(tmp,"\r%c%c %04lx     ", c1,c2 , add);    
		}
		else
			tmp += sprintf(tmp,"\r%c%c %04lx     ", c1,c2 , add);
		
		add += disassemble(&tmp, memory, add);
	    if (add> 0xffff)
			add-= 0x10000;	/* don't show loacations over $FFFF */

	    if (tmp-msg+60 > n_chars) 
		{	Error("too many lines to disassemble."); return NULL; }
	}
	tmp += sprintf(tmp,"\r");

	return msg;
}



#ifndef USE_INTERRUPTS
void bad_op(m6811 state)
{
    Error("Bad op at pc=%04lx", state.pc&0xffff);
}
#endif

/* what happens with unimplemented instructions */
void not_done(m6811 state, byte *memory)
{
    Error("Encountered unimplemented instruction at %04lx (%02x)", 
	    state.pc-1, memory[state.pc-1]);
}

/* load S19 file */
/** ## supports checksum and EOL warning 
 returns: 	>0  successful load (num. of bytes)
			0	unspecified error 				***/
long load_code(char *buf, byte *memory, char *fname)
{
short cnt = 0;

char  key;
int byte_count, add_hi, add_lo;

short val;
unsigned short add; /** ## int **/
short csum;
long tot_bytes = 0;

#ifdef MAC
long ticks;
	ticks = TickCount();
#endif
	if (sscanf(buf, "S%c%2x%2x%2x", &key, &byte_count, &add_hi, &add_lo) != 4) {
		key = '9';
		Error("%s is not a S19 file",fname);
		return 0;
    }
    buf += 8; 
	while (key=='1')
    {
    short i;
    	cnt++; tot_bytes += byte_count-3;
    	add = (add_lo&0xff) + (add_hi<<8);

		csum = byte_count+add_lo+add_hi;
		
		for (i=0;i<byte_count-3;i++) {
	    	hexScan(buf,&val);  /*sscanf(buf, "%2x", &val);*/ 
	    	buf += 2;
	    	csum += val;
	    	memory[add+i] = val;
		}
		
		hexScan(buf,&val);  /*sscanf(buf, "%2x", &val); */
		buf+=2;
		csum = (csum - ~val)&0xff; 
		if (csum) Error("checksum failed at line %d of file %s",cnt,fname); 
		val = *buf++; /* equivalent to sscanf(buf,"%c",..) followed by buf++ */
		if (val!='\r' && val!='\n') Error("file %s may be corrupted",fname); 
		
		while (val!='\r' && val!='\n') { val=*buf++; }
		/* line-feed stripper */
		if (*buf=='\n') buf++;
		
		if (sscanf(buf, "S%c%2x%2x%2x", &key, &byte_count, &add_hi, &add_lo) != 4) {
	    	key = '9'; 
		}
		buf+=8;
    }
#ifdef MAC
    ticks = TickCount()-ticks;
#endif
	Loaded = TRUE;
    return tot_bytes;
}

/* ## memory dump */
char * mem_dump(m6811 *state, byte *memory, word add, short n) 
{
unsigned short i,j;
char * tmp = msg, c;

	tmp += sprintf(tmp,"          Memory-dump from %04lx (%d locations) at t=%ld.\r\r      ",add,n,state->t);
	for (i=0;i<16;i++) {
		tmp += sprintf(tmp,"%2lx ", (add+i)%16);
    }
    *tmp++ = '\r';
    if (n<=0) n=256;
    for (i=0;i<n;i+=16) {
		tmp += sprintf(tmp,"%04lx  ", add+i);
		for (j=0;j<16;j++) {
	    	tmp += sprintf(tmp,"%02x ", memory[add+i+j]);
		}
		for (j=0;j<16;j++) {
	    	if (isprint(c = memory[add+i+j])) *tmp++ = c;
	    	else *tmp++ = '.';
		}
		*tmp++ = '\r';
		if (tmp-msg+200 > n_chars) 
		{	Error("too many locations to dump."); 
			return NULL; }
    }
    *tmp++ = '\0'; /* to terminate string correctly
    			      (because of the dirty access to tmp) */
    return msg;
}

#ifdef MAC
PRIVATE
void interrupt() 
{
    caught_signal++;
}
#else
/*
PRIVATE
void interrupt(int sig)
{
    (void) signal(SIGINT, interrupt);
}
*/
#endif

/* return 1 if we should continue executing here, 0 if we should stop */
INLINE short check_break(m6811 *state, short breaks[])
{
    unsigned short pc; /** ## int **/

    pc = get_pc(state);
    if (breaks[pc] > 0) {
	breaks[pc]--;
	return 0;
    }
    else return !breaks[pc];
}
	
short multi_step(short n, m6811 *state, byte memory[], short breaks[])
{
    caught_signal = 0;
    if (n > 0) {
	single_step(state, memory);
	n--;
	while (n>0 && !caught_signal && check_break(state, breaks)) {
	    single_step(state, memory);
	    n--;
	}
    }
    else if (n < 0) {
	single_step(state, memory);
	while (!caught_signal && check_break(state, breaks)) {
	    single_step(state, memory);
	}
    }
	return n;
}

void reset(m6811 *state, byte memory[], short breaks[])
{
long i;  /** ## int i; **/

    for (i=0;i<65536;i++) {
	breaks[i] = 0;
	memory[i] = 0xff;
    }

    for (i=0;i<256;i++) {	/* put distinctive stuff in page zero */
	memory[i] = 0xaa;
    }
	
    state->t = 0;			/* have to start time somewhere */
    set_ccw(state, 0xD0);	/* initialize condition bits (set S, X & I) */
    set_pc(state,			/* and get initial pc */
	   get_mem16(memory, 0xfffe));
	
	/** ## just to cut garbage **/
	set_x(state,get_x(state));
	set_y(state,get_y(state));
	set_d(state,get_d(state));
	set_s(state,get_s(state));	

#ifdef USE_INTERRUPTS
	reset_interrupts(state, memory);
#endif

	Loaded = FALSE;
}



int main(int argc, char *argv[])
{
    int command;
    m6811 state;
    byte memory[65536];
    short breaks[65536];	/* break counts.  0 -> no break,
				   -1 -> permanent breakpoint
				   +n -> temporary breakpoint that will
				   evaporate after being hit n times */

    if (argc < 2) {
	fprintf(stderr, "usage: sim68 prog.s19\n");
	fflush(stderr);
	exit(1);
    }

/*
    (void) signal(SIGINT, interrupt);
    caught_signal = 0;

    file_count = argc;
    files = argv;
*/

    reset(&state, memory, breaks);

    init_dis();			/* set up the disassembler */
    show_state(&state, memory, breaks);
    fflush(stdout);
    command = getchar();
    while (do_command(command, &state, memory, breaks)) {
	/* show_state(&state, memory, breaks); */
	fflush(stdout);
	command = getchar();
    }
    return 0;
}



