#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "p88common.h"
#include "p88symboltable.h"
#include "p88analyze.h"
#include "p88simulator.h"

static const char *const *mapfile(const char *filename, long *linep)
{
	struct stat sb;
	char *map, **prog_lines;
	long i, x, fsize, lines;
	int ch;
	FILE *fp;

	if (stat(filename, &sb) < 0 || sb.st_size <= 0)
		return NULL; /* Error */
	fsize = sb.st_size;
	if ((map = malloc(sizeof(char) * (fsize+1))) == NULL)
		return NULL; /* Error */
	if ((fp = fopen(filename, "r")) == NULL)
		return NULL; /* Error */
	lines = 1;
	for (i = x = 0; i < fsize; i++, x++) {
		ch = getc(fp);
		if (ch == '\r') { /* cr */
			map[x] = 0;
			lines++;
			if ((ch = getc(fp)) != '\n' && ch != EOF)
				map[++x] = ch;
			i++;
		}if (ch == '\n') { /* nl */
			map[x] = 0;
			lines++;
		}else
			map[x] = ch;
	}
	map[fsize] = 0; /* if the last char is not NL */
	fclose(fp);

	prog_lines = malloc(sizeof(char *) * lines);
	prog_lines[0] = map;
	for (i = 0, x = 1; ; i++) {
		if (map[i] == 0) {
			prog_lines[x] = &map[i+1];
			if (++x >= lines)
				break;
		}
	}
	/* Each element of prog_lines[] has a line of input file */

	*linep = lines;
	return (const char *const *)prog_lines;
}

static void core_dump(const unsigned char *core, long bytecount)
{
	long i;
	int u, neednl = NO;

	printf("----- MEMORY DUMP -----\n");
	for (i = 0; i < bytecount; i++) {
		printf(" %02x", core[i]);
		neednl = YES;
		if ((u = i & 0x0f) == 0x07)
			putchar(' ');
		else if (u == 0x0f) {
			putchar('\n');
			neednl = NO;
		}
	}
	if (neednl)
		putchar('\n');
	printf("-----\n");
}

static void help(const char *prog)
{
	fprintf(stderr, "P88 Simulator [version " VERSION "]  "
		COPYRIGHT "\n");
	fprintf(stderr, "Usage: %s [options] source\n", prog);
	fprintf(stderr, "Options:\n");
	fprintf(stderr, "\t-t    trace\n");
	fprintf(stderr, "\t-c    Syntax check only\n");
	fprintf(stderr, "\t-d    Dump machine code\n");
	fprintf(stderr, "\t-h    Show this help\n");
}

int main(int argc, char **argv)
{
	int ac = 1;
	int checkflag = NO;
	int dumpflag = NO;
	int traceflag = NO;
	const char *const *asmsrc;
	long lines, bytes_needed = 0;
	int errcnt;
	unsigned char *core = NULL;	/* core memory */

	while (ac < argc && argv[ac][0] == '-') {
		switch (argv[ac][1]) {
		case 't':
			traceflag = YES;
			break;
		case 'c':
			checkflag = YES;
			break;
		case 'd':
			dumpflag = YES;
			break;
		case 'h':
		default:
			help(argv[0]);
			return 1;
		}
		ac++;
	}
	if (ac >= argc) {
		fprintf(stderr, "Error: No source file is specified.\n");
		return 1;
	}
	if ((asmsrc = mapfile(argv[ac], &lines)) == NULL) {
		fprintf(stderr, "Error: Can't read source file.\n");
		return 1;
	}

	init_table();
	/* Pass 1 */
	errcnt = analyze(asmsrc, lines, 1, NULL);
	if (errcnt == 0 && bytecount <= 0) {
		errcnt = 1;
		fprintf(stderr, " ... No code generated.\n");
	}
	if (errcnt == 0) {
		bytes_needed = undef_words(bytecount);
		if (bytes_needed >= STACK_BASE) { /* STACK_BASE is special */
			errcnt = 1;
			fprintf(stderr, " ... Memory area is too large.\n");
		}
	}

	if (errcnt > 0) {
		fprintf(stderr, "*** pass=1 ... %d error%s found.\n",
			errcnt, (errcnt < 2) ? "" : "s");
		return 1;
	}
	if (checkflag)
		return 0;

	/* Pass 2 */
	core = malloc(sizeof(unsigned char) * 0x1000);
	errcnt = analyze(asmsrc, lines, 2, core);
	if (errcnt > 0) {
		fprintf(stderr, "*** pass=2 ... %d error%s found.\n",
			errcnt, (errcnt < 2) ? "" : "s");
		return 1;
	}
	if (dumpflag)
		core_dump(core, bytes_needed);
	simulate(core, bytes_needed, traceflag);

	return 0;
}
