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

Version: @(#)main.c	1.16	01/19/98

This file is based on sim_io.c from the Sim68 package.
Extensive modifications and additions were made by
Brian L. Evans (bevans@ece.utexas.edu) and
Saleem K. Marwat (marwat@vision.ece.utexas.edu).
******************************************************/

#ifndef INLINE
#define	INLINE
#endif

#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "symbols.h"
#include "foreign.h"
#include "sim.h"
#include "commands.h"
#include "main.h"

/* globals */
char msg[BUFFER_LENGTH];        /* char buffer for text-handling operations */
long n_chars = BUFFER_LENGTH;

long caught_signal = 0;
int debug = FALSE;
char *VersionString = "1.0.6";


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);
	}
}

void showBreaks(m6811* state, short* breaks)
{
  long addr;

  printf("Breakpoints (at cycles=%ld)\n", state->t);
  for (addr = 0; addr < M68HC11_MEMSIZE; addr++) {
    short breakValue = *breaks++;
    if (breakValue) {
      char* symbol = lookup_val(addr);
      if (symbol) {
	if (breakValue < 0)
	  printf("%s (%04lx)\n", symbol, 0xffff&addr);
	else 
	  printf("%s (%04lx) [%d]\n ", symbol, 0xffff&addr, breakValue);
      }
      else {
	if (breakValue < 0)
	  printf("%04lx\n", 0xffff&addr);
	else 
	  printf("%04lx [%d]\n", 0xffff&addr, breakValue);
      }
    }
  }
  fflush(stdout);
}


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

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

/*
Load an S19 file.  An S19 file is an ASCII file.  A checksum
value is given at the end of each line.
It returns the number of bytes loaded on a successful load,
and zero on an error.
*/
long load_code(FILE *f, byte *memory)
{
  char key = '9';
  int  byte_count = 0;
  long tot_bytes = 0;
  int  line = 0;
  word add = 0;

  while (fscanf(f, "S%c%2x%4lx", &key, &byte_count, &add) == 3) {
    int byte = 0;
    int i = 0;
    int bytesToRead = byte_count - 3;
    int add_hi = ((add & 0xFF00) >> 8);
    int add_lo = (add & 0x00FF);
    int checkSum = byte_count + add_lo + add_hi;

    line++;

    /* Check to make sure that we have read a valid S record */
    if (key != '1') break;

    tot_bytes += bytesToRead;

    /* Each byte is represented in ASCII as two hex digits */
    for (i = 0; i < bytesToRead; i++) {
      if ( fscanf(f, "%2x", &byte) != 1 ) {
        fprintf(stderr,
	     "error reading two digits in line %d at character position %d\n",
	     line,
	     8 + i*2);
	fprintf(stderr, "skipping the rest of the line.\n");
        fflush(stderr);
	break;
      }
      checkSum += byte;
      memory[add+i] = byte;
    }

    /* Read the next byte which holds the checksum value and check it */
    if ( fscanf(f, "%2x", &byte) != 1 ) {
      fprintf(stderr, "cannot read the checksum value at the end of line %d\n",
              line);
      fflush(stderr);
    }
    else if ((checkSum & 0x00FF) != (~byte & 0x00FF)) {
      fprintf(stderr, "checksum failed at line %d: %x != %x\n",
              line, checkSum & 0x00FF, ~byte & 0x00FF);
      fflush(stderr);
    }

    byte = getc(f);
    if (byte != '\r' && byte != '\n') {
      fprintf(stderr, "file may be corrupted at line %d\n", line);
      fflush(stderr);
    }
    while (byte != '\r' && byte != '\n') byte = getc(f);
  }

  if (line == 0) {
    fprintf(stderr, "file is not in S19 format.\n");
    fflush(stderr);
  }

  return tot_bytes;
}

void interrupt()
{
    caught_signal++;
    (void) signal(SIGINT, interrupt);
}

/* 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 j;

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

    /* put distinctive stuff in page zero */
    for (j = 0; j < M68HC11_PAGELEN; j++) {
	memory[j] = M68HC11_PAGE0_VAL;
    }

    state->t = 0;		/* have to start time somewhere */
    set_ccw(state, 0);		/* initialize condition bits*/
    set_pc(state,		/* and get initial pc */
    get_mem16(memory, 0xfffe));

    set_x(state, 0);
    set_y(state, 0);
    set_d(state, 0);
    set_s(state, 0);	
}


/* display machine state */
void show_state(m6811 *state, byte memory[], short breaks[])
{
    long i;
    int j, k;

    /* registers */
    printf("A %02X B %02X    t=%ld\nD  %04lX\nX  %04lX Y  %04lX ",
	   get_a(state), get_b(state), state->t,
	   get_d(state),
	   get_x(state), get_y(state));
    printf("	breakpoints -> ");
    for (i = 0; i < M68HC11_MEMSIZE; i++) {
	if ( breaks[i] < 0) {
	    printf("%04x ", (int)(0xffff&i));
	}
	else if (breaks[i]>0) {
	    printf("%04x[%d] ", (int)(0xffff&i), breaks[i]);
	}
    }
    printf("SP %04lX PC %04lX ", get_s(state), get_pc(state));

    /* and next insruction */
    (void) disassemble(stdout, memory, get_pc(state));

    /* followed by condition codes */
    printf("    ");
    printf("%c %c %c %c  %c %c %c %c\n",
	   get_stop_disable(state)?'S':'.', get_x_interrupt(state)?'X':'.',
	   get_half_carry(state)?'H':'.', get_idisable(state)?'I':'.',
	   get_negative(state)?'N':'.', get_zero(state)?'Z':'.',
	   get_overflow(state)?'V':'.', get_carry(state)?'C':'.');

    /* and a quick memory dump */
    printf("\n      ");
    for (j = 0; j < 16; j++) {
	printf("%2x ", j);
    }
    printf("\n");

    for (k = 0; k < M68HC11_PAGELEN; k += 16) {
	int j;
	printf("%04x  ", k);
	for (j=0;j<16;j++) {
	    printf("%02x ", memory[k+j]);
	}
	for (j=0;j<16;j++) {
	    if (isprint(memory[k+j])) putchar(memory[k+j]);
	    else putchar('.');
	}
	printf("\n");
    }

    fflush(stdout);
}

/*
Display the contents of registers as one line register.
*/
void registers(m6811 *state) {
  printf("A=%02X\nB=%02X\nD=%04lX\nt=%ld\nX=%04lX\nY=%04lX\n",
	 get_a(state), get_b(state),
	 get_d(state), state->t,
	 get_x(state), get_y(state));
  printf("SP=%04lX\nPC=%04lX\n",
	 get_s(state), get_pc(state));

  /* followed by condition codes */
  printf("CCR=%c%c%c%c%c%c%c%c\n",
	 get_stop_disable(state)?'S':'.', get_x_interrupt(state)?'X':'.',
	 get_half_carry(state)?'H':'.', get_idisable(state)?'I':'.',
	 get_negative(state)?'N':'.', get_zero(state)?'Z':'.',
	 get_overflow(state)?'V':'.', get_carry(state)?'C':'.');
  fflush(stdout);
}


void debug_multi_step(int n, m6811* state, byte* memory, short* breaks) {
  caught_signal = 0;
  if (n > 0) {
    (void) disassemble(stdout, memory, get_pc(state));
    single_step(state, memory);
    n--;
    while (n>0 && !caught_signal && check_break(state, breaks)) {
      (void) disassemble(stdout, memory, get_pc(state));  
      single_step(state, memory);
      n--;
    }
    }
  else if (n < 0) {
    (void) disassemble(stdout, memory, get_pc(state));
    single_step(state, memory);
    while (!caught_signal && check_break(state, breaks)) {
      (void) disassemble(stdout, memory, get_pc(state));
      single_step(state, memory);
    }
  }
}


void mem_dump(m6811* state, byte* memory, word add, short n) 
{
  unsigned short i,j;
  char c;
  
  if (n<=0) n = M68HC11_PAGELEN;

  if ((n*16) + add <= M68HC11_MEMSIZE) {

    printf("          Memory-dump from %04lx (%d locations) at t=%ld.\n\n      ", add, n, state->t);
    for (i=0;i<16;i++) {
      printf("%2lx ", (add+i)%16);
    }

    printf("\n");

    for (i=0;i<n;i+=16) {
      
      printf("%04lx  ", add+i);
      for (j=0;j<16;j++) {
	printf("%02x ", memory[add+i+j]);
      }
      for (j=0;j<16;j++) {
	if (isalnum(c = memory[add+i+j])) printf("%c", c);
	else printf("%c", '.');
      }
      printf("\n");
    }
    fflush(stdout);
  }
  else {
    fprintf(stderr, "Error: memory locations to list exceed 0xffff\n");
    fflush(stderr);
  }
}


void MC6811state(m6811 *state, byte* memory,
		 short* breaks, const char* programName)
{
  int i, j;

  printf("Memory Dump\n");
  printf("      ");
  for (i=0;i<16;i++) {
    printf("%2x ", i);
  }
  printf("\n");
  for (i = 0; i < M68HC11_PAGELEN; i += 16) {
    printf("%04x  ", i);
    for (j = 0; j < 16; j++) {
      printf("%02x ", memory[i+j]);
    }
    for (j=0;j<16;j++) {
      if (isprint(memory[i+j])) putchar(memory[i+j]);
      else putchar('.');
    }
    printf("\n");
  }
  showBreaks(state, breaks);
  printf("program name = %s\n", programName);
  fflush(stdout);
}


void setReg(m6811* state, char ch, int num) {
  switch(toupper(ch)) {
    case 'D':
      set_d(state, num);
      break;
    case 'X':
      set_x(state, num); 
      break;
    case 'Y':
      set_y(state, num);
      break;
  }
}


char* readStringFromStream(char* buffer, int len, FILE* streamptr) {
    buffer[0] = 0;
    if ( ! feof(streamptr) ) {
      int slen;
      fgets(buffer, len, streamptr);
      buffer[len - 1] = 0;
      slen = strlen(buffer);
      if ( buffer[--slen] == '\n' )
        buffer[slen] = 0;
    }
    return buffer;
}

/* Main routine.
Break counts are interpreted as follows:
0 -> no break
-1 -> permanent breakpoint
+n -> temporary breakpoint that will evaporate after being hit n times 
*/
int main(int argc, char *argv[])
{
  m6811 state;
  byte memory[M68HC11_MEMSIZE];
  short breaks[M68HC11_MEMSIZE];
  char filename[BUFFER_LENGTH];

  MCSimState mcsimstate;

  /* Initialize the MC68HC11 simulator state data structure */
  mcsimstate.state = &state;
  mcsimstate.memory = memory;
  mcsimstate.breaks = breaks;
  mcsimstate.filename = filename;
  mcsimstate.load = FALSE;

  /* Initialize global variables */
  msg[0] = 0;
  (void) signal(SIGINT, interrupt);
  caught_signal = 0;

  reset(&state, memory, breaks);
  init_dis();
  
  /* Print welcome message. */
  MCSimVersionCommand("", &mcsimstate);

  /* Primary command loop: read user command, process it, and repeat */
  while ( !feof(stdin) ) {
    char command[BUFFER_LENGTH];
    char* input = 0;

    /* Display the simulator prompt, and flush standard output */
    printf(M68HC11_PROMPT);
    fflush(stdout);

    /* Read input from the user */
    input = readStringFromStream(command, BUFFER_LENGTH, stdin);

    /* Ignore empty lines */
    if (*input) {
      char* lowerCasePtr = 0;
      int status;

      while (*input && isspace(*input)) input++;
      lowerCasePtr = input;
      while (*lowerCasePtr && !isspace(*lowerCasePtr))
        *lowerCasePtr++ = tolower(*lowerCasePtr);

      status = MCSimEvaluateCommand(input, &mcsimstate);
      if ( status < 0) {                                  /* quit requested */
        break;
      }
      else if ( status > 0 ) {
        fprintf(stderr, MCSimCommandErrorMessage(status), input);
        fprintf(stderr, "\n");
        fflush(stderr);
      }
    }
  }

  exit(0);
}
