/*
 *  pocket - SHARP PC-1450エミュレータ
 *
 *  Copyright (C) 1994, 1995 by Dai Ishijima
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <ctype.h>
#include "pocket.h"

#define YES 1
#define NO  0

#define EOS '\0'
#define NEWLINE '\n'

#define NOAUTOPOWOFF
/* #define KBDEOF '\004' */

#define CPUROM "cpu-1450.rom"
#define CPUADR 0x0000
#define CPUROMSIZ 0x2000
#define BASROM "bas-1450.rom"
#define BASADR 0x8000
#define BASROMSIZ 0x8000
#define RAMCARD "bas-1450.ram"
#define RAMADR 0x2000
#define RAMSIZ 0x5000


#define CORESIZE 65536
/*extern unsigned char *mainram;*/
unsigned char mainram[CORESIZE];

char *prog;
#define shift --argc; ++argv

int appendlog = NO;	/* ログファイルに動作を追加 */
FILE *afp = NULL;
int dumpram = NO;	/* 終了時にRAMの内容を書き出す */
float sctiming = 0.0;	/* 動作速度タイミング */
int disprate = (100 * 1000);	/* 100ms ごとに表示 */

#define MAXLOGLEN 65536
int loglen = 0;

#define NLOOP 1000	/* 動作速度微調整を何命令実行するごとに入れるか */

#define DUMPSIZ 256


void dumpmem(int x)
{
    int i;

    for (i = 0; i < DUMPSIZ; i++) {
	if ((i % 16) == 0) {
	    printf("%04x ", x + i);
	}
	printf("%02x ", mainram[x + i]);
	if ((i % 16) == 15) {
            printf("\r\n");
	}
    }
    printf("\r\n");
}


void disprefresh()
{
    clearall();
    displayall();
    scstatus();
    showkeytab();
}


void writeramf(char *name)
{
    FILE *fp;

    if ((fp = fopen(name, "w")) != NULL) {
	if (fwrite(&mainram[RAMADR], 1, RAMSIZ, fp) < RAMSIZ) {
	    fprintf(stderr, "%s: can't write %s\n", prog, name);
	    exit(1);
	}
    }
    else {
	fprintf(stderr, "%s: can't open %s\n", prog, name);
    }
}


void readramf(char *name)
{
    FILE *fp;

    if ((fp = fopen(name, "r")) != NULL) {
	if (fread(&mainram[RAMADR], 1, RAMSIZ, fp) < RAMSIZ) {
	    fprintf(stderr, "%s: can't read %s\n", prog, name);
	    exit(1);
	}
    }
    else {
	fprintf(stderr, "%s: can't open %s\n", prog, name);
    }
}


void kbdescape()
{
    char *p;
    char s[BUFSIZ];
    int i;
    float f;
    static int addr = 0;

    disprefresh();
    kbdrestore();
    fprintf(stderr, "%s> ", prog);
    while ((p = fgets(s, BUFSIZ, stdin)) != NULL) {
	if ((s[0] == NEWLINE) || (s[0] == EOS)) {
	    break;
	}
	if ((s[0] == 'h') || (s[0] == '?')) {
	    fprintf(stderr, "h, ?     show help\n");
	    fprintf(stderr, "q        quit\n");
	    fprintf(stderr, "i        dump iram\n");
	    fprintf(stderr, "d [addr] dump main memory\n");
	    fprintf(stderr, "t #.#    set speed parameter\n");
	    fprintf(stderr, "v        show fixed variables\n");
	    fprintf(stderr, "r [file] read RAM from file\n");
	    fprintf(stderr, "w [file] write RAM to file\n");
	    fprintf(stderr, "s        show parameter\n");
	}
	else if (s[0] == 'q') {		/* q は quit */
	    kbdpowoff = YES;
	    break;
	}
	else if (s[0] == 'i') {
	    dumpiram();
	}
	else if (s[0] == 'd') {
	    if ((s[1] == EOS) || (s[1] != NEWLINE)) {
		addr = strtol(&s[1], (char **)NULL, 16);
	    }
	    dumpmainram(addr);
	    addr += 256;
	}
	else if (s[0] == 't') {
	    if ((s[1] == EOS) || (s[1] != NEWLINE)) {
		printf("%.1f\n", sctiming);
	    }
	    else {
		f = atof(&s[1]);
		if (f >= 0.0) {
		    sctiming = f;
		}
		else {
		    printf("%.1f\n", sctiming);
		}
	    }
	}
	else if (s[0] == 'k') {
	    if ((s[1] == EOS) || (s[1] != NEWLINE)) {
		printf("%d\n", key_wait / 1000);
	    }
	    else {
		i = atoi(&s[2]);
		if (i > 0) {
		    key_wait = i * 1000;
		}
		else {
		    printf("%d\n", key_wait / 1000);
		}
	    }
	}
	else if (s[0] == 's') {
	    printf("k %d (key select [ms])\n", key_wait / 1000);
	    printf("t %.1f (sctiming [ms] / %d instructions)\n",
		   sctiming, NLOOP);
	    printf("d %04x (dump address)\n", addr);
	}
	else if (s[0] == 'r') {
	    p = &s[1];
	    while (isspace(*p)) {
		++p;
	    }
	    if (*p == EOF) {
		readramf(RAMCARD);
	    }
	    else {
		readramf(p);
	    }
	}
	else if (s[0] == 'w') {
	    p = &s[1];
	    while (isspace(*p)) {
		++p;
	    }
	    if (*p == EOF) {
		writeramf(RAMCARD);
	    }
	    else {
		writeramf(p);
	    }
	}
	fprintf(stderr, "%s> ", prog);
    }
    if (p == NULL) {
	fprintf(stderr, "\n%s: EOF encountered\n", prog);
	kbdpowoff = YES;
    }
    if (kbdpowoff) {
	printf("\r\nPower off by user.\r\n");
	exit(0);
    }
    disprefresh();
    kbdinit();
}


void clearmem()
{
    int i;

    for (i = 0; i < CORESIZE; i++) {
	mainram[i] = 0;
    }
}


void readmemf()
{
    FILE *fp;
    if ((fp = fopen(CPUROM, "r")) == NULL) {
	fprintf(stderr, "%s: can't open\n", CPUROM);
	exit(1);
    }
    if (fread(&mainram[CPUADR], 1, CPUROMSIZ, fp) < CPUROMSIZ) {
	fprintf(stderr, "%s: read failed\n", CPUROM);
	exit(1);
    }
    fclose(fp);
    if ((fp = fopen(BASROM, "r")) == NULL) {
	fprintf(stderr, "%s: can't open\n", BASROM);
	exit(1);
    }
    if (fread(&mainram[BASADR], 1, BASROMSIZ, fp) < BASROMSIZ) {
	fprintf(stderr, "%s: read failed\n", BASROM);
	exit(1);
    }
    fclose(fp);
    readramf(RAMCARD);
}


void usage()
{
    fprintf(stderr, "Usage: %s [options]\n", prog);
}


int interval(struct timeval *tnow, struct timeval *tlast)
{
    int sec, usec;

    sec = tnow->tv_sec - tlast->tv_sec;
    usec =  tnow->tv_usec - tlast->tv_usec;
    return(sec * 1000000 + usec);
}


void mainloop()
{
    int pc;
#ifdef USE_DISP
    int disp;
    int lastdisp;
#endif
    int nloop = 0;
    struct timeval tnow;
    struct timeval tdisp;
    struct timeval tslow;
    int nsleep;

#ifdef USE_DISP
    disp = 0;
    lastdisp = disp_on;
#endif
    gettimeofday(&tnow, NULL);
    tslow.tv_sec  = tdisp.tv_sec  = tnow.tv_sec - 1;
    tslow.tv_usec = tdisp.tv_usec = 0;
    pc = 0;
    while (1) {
	pc = interpret();
	gettimeofday(&tnow, NULL);
	if ((appendlog) && (afp != NULL)) {
	    fprintf(afp, "%04x\n", pc);
	    ++loglen;
	    if (loglen > MAXLOGLEN) {
		fclose(afp);
	    }
	}
	if (interval(&tnow, &tdisp) > disprate) {
	    displayall();
	    kon = NO;
	    scstatus();
	    if (!disp_on) {
		busy();
	    }
	    fflush(stdout);
	    tdisp.tv_sec = tnow.tv_sec;
	    tdisp.tv_usec = tnow.tv_usec;
	}
#ifndef NOAUTOPOWOFF
	if (!power_on) {
	    printf("Auto Power off\r\n");
	    break;
	}
#endif
	if (!power_on) {
	    ungetc(getchar(), stdin);
	}
	if (kbdpowoff) {
	    printf("\r\nPower off by user\r\n");
	    break;
	}
	if ((sctiming > 0.0) && (++nloop > NLOOP)) {
	    nsleep = sctiming * 1000 - interval(&tnow, &tslow);
	    if (nsleep > 0) {
		usleep(nsleep);	/* マイクロ秒単位で停止 */
	    }
	    nloop = 0;
	    tslow.tv_sec = tnow.tv_sec;
	    tslow.tv_usec = tnow.tv_usec;
	}
    }
}


int main(int argc, char *argv[])
{
    prog = *argv;
    shift;
    while ((argc > 0) && (argv[0][0] == '-')) {
	if (argv[0][1] == 'l') {			/* 動作ログ */
	    appendlog = YES;
	    if ((argv[0][2] != EOS) && ((argv[0][2] & 1) == 0)) {
		appendlog = NO;
	    }
	    else if ((argv[0][2] != EOS) && ((argv[0][2] & 1) != 0)) {
		appendlog = YES;
	    }
	    else {
		appendlog = !appendlog;
	    }
	}
	else if ((argv[0][1] == 't') && (argc > 1)) {
	    /* 動作速度タイミング調整 */
	    shift;
	    sctiming = atof(*argv);
	    if (sctiming < 0.0) {
		usage();
		exit(1);
	    }
	}
	else if ((argv[0][1] == 'k') && (argc > 1)) {
	    /* キー入力 select(2) 待ち [ms] */
	    shift;
	    key_wait = atoi(*argv) * 1000;
	    if (key_wait < 0) {
		usage();
		exit(1);
	    }
	}
	else if (argv[0][1] == 'd') {
	    /* 終了時にダンプ */
	    dumpram = !dumpram;
	}
	else {
	    usage();
	    exit(1);
	}
	shift;
    }
    clearmem();
    readmemf();
    lcdinit();
    kbdinit();
    initsc();
    disprefresh();
    if (appendlog) {
	afp = fopen("logfile", "a");
	loglen = 0;
    }
    mainloop();
    if (dumpram) {
	writeramf(RAMCARD);
    }
    closesc();
    if ((appendlog) && (afp != NULL)) {
	fclose(afp);
    }
    disprefresh();
    if (kbdpowoff) {
	printf("Power off by user\r\n");
    }
    kbdrestore();
    exit(0);
}
