/*
 *  sc.c -- sc61860 emulator
 *
 *  Copyright (C) 1994 by Dai Ishijima
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>	/* usleep(3) */
#include "pocket.h"

#define YES 1
#define NO 0

#define HALFSEC 500000	 /* 500,000us */

/* sc61860 registers */
static unsigned short pc, dp;
static unsigned char  preg, qreg, rreg, dreg;

static unsigned short alu;
static unsigned char cflag, zflag;
static unsigned char xin = 0;
static unsigned char opcode;
static unsigned short  ticks, ticks2;
static unsigned char div500, div2;
unsigned char power_on;
unsigned char disp_on;

static int keychar;
static FILE *ofp = NULL;
static FILE *ifp = NULL;
static unsigned char iochar = 0;

#define hi(x) ((x) >> 8)
#define lo(x) ((x) & 0xff)
#define pair(h,l) (((h) << 8) | (l))
#define pairload(h,l,v) h=hi(v); l=lo(v)

#define XTICKS 500
#define XTICK2 500

#define IRAMSIZ (0x5f+1)

static unsigned char iram[IRAMSIZ];
#define ireg  iram[0]
#define jreg  iram[1]
#define areg  iram[2]
#define breg  iram[3]
#define xlreg iram[4]
#define xhreg iram[5]
#define ylreg iram[6]
#define yhreg iram[7]
#define kreg  iram[8]
#define lreg  iram[9]
#define mreg  iram[10]
#define nreg  iram[11]
#define bareg (pair(iram[3], iram[2]))
#define xreg  (pair(iram[5], iram[4]))
#define yreg  (pair(iram[7], iram[6]))

#define ia iram[0x5c]
#define ib iram[0x5d]
#define fo iram[0x5e]
#define ctrl iram[0x5f]
static unsigned char iaport, ibport, foport; /*  ctrlport; */
static unsigned char testport;

#define PCEOF 032 /* ^Z */

#define BYTEOUT 1
#define BYTEIN  2
#define ESCAPE  0x3f
#define RTN 0x37

#define KEY_RELEASE 100000	/* 100,000 [us] -> 100 [ms] -> 1/10 [sec] */
#define KEY_WAIT    10000	/* 10,000 [us] -> 10 [ms] -> 1/100 [sec] */
int key_wait = KEY_WAIT;

#define KBDESCAPE 27

#ifdef FREQ
static unsigned int opfreq[256];
static FILE *fp;
#endif

#define swap(a,b) {unsigned char _swpc; _swpc=a; a=b; b=_swpc;}
/*#define memr(adr) ((adr < 0x2000) ? (hi(adr)) : mainram[adr])*/
#define memr(adr) (mainram[adr])

/*
 * memory write
 */
void memw(int adr, int dat)
{
#ifndef NO_KEYPORT_HACK
    if ((0x2000 <= adr) && (adr <= 0x7dff)) {
	mainram[adr] = dat;
    }
    else if ((0x7e00 <= adr) && (adr <= 0x7fff)) {
	mainram[0x7e00] = dat;
    }
#else
    if ((0x2000 <= adr) && (adr <= 0x7fff)) {
	mainram[adr] = dat;
    }
#endif
#ifndef EXIT_ON_WRITE_ROM
    else {
	disprefresh();	/* show LCD, scstatus */
	fprintf(stderr, "%04x <= %02x\n", adr, dat);
	usleep(HALFSEC);
    }
#else
    else {
	fprintf(stderr, "???\n");
	power_on = NO;
    }
#endif
#if 0
/*    if (a & 0x7000 == 0x7000) {
	if ((0x7000 <= adr) && (adr <= 0x703b)) {
	    display(adr);
	}
	if ((0x7068 <= adr) && (adr <= 0x707b)) {
	    display(adr);
	}
	if (adr == 0x703c || adr == 0x703d || adr == 0x707c) {
	    symbol(adr);
	}
    }
*/
#endif
}


#ifdef DPQUIRK
/*
 * DP registor quirks / dirty trick (2017-03-16)
 */
int inc8dp(int dp)
{
    unsigned char dphi, dplo;

    dphi = hi(dp);
    dplo = lo(dp);
    ++dplo;
    return(pair(dphi, dplo));
}


int dec8dp(int dp)
{
    unsigned char dphi, dplo;

    dphi = hi(dp);
    dplo = lo(dp);
    --dplo;
    return(pair(dphi, dplo));
}
#else
int inc8dp(int dp)
{
    return(++dp);
}
int dec8dp(int dp)
{
    return(--dp);
}
#endif


/*
 * operations
 */

/* secret */
static void mvwp()	/* 35 */
{
    --pc ;
    dreg = ireg+ 1;
    iram[--rreg] = hi(pc);
    iram[--rreg] = lo(pc);
    pc = bareg;
    do {
	iram[preg++] = mainram[pc++];
	--dreg;
    } while (dreg != 0);
    pc = pair(iram[rreg + 1], iram[rreg]) + 1;
    rreg += 2;
}


static void tsma()	/* C6 */
{
    zflag = (iram[preg] & areg) == 0;
}

static void clra()	/* 23 */
{
    areg = 0;
}

static void mvmp()	/* 54 */
{
    iram[preg] = mainram[pc];
}

static void ldpc() 	/* 56 */
{
    areg = mainram[pc];
}

static void sz()	/* D7 */
{
    iram[rreg - 1] = memr(dp);
    ++pc;
    zflag = YES;
}

static void rz()	/* 72 73 76 77 */
{
    ++pc;
    zflag = NO;
}

/* 8bits load */
static void ldp()
{
    areg = preg;
}

static void ldq()
{
    areg = qreg;
}

static void ldr()
{
    areg = rreg;
}

static void ldm()
{
    areg = iram[preg];
}

static void ldd()
{
    areg = memr(dp);
}

static void lia()
{
    areg = mainram[pc++];
}

static void lib()
{
    breg= mainram[pc++];
}

static void lij()
{
    jreg= mainram[pc++];
}

static void lii()
{
    ireg= mainram[pc++];
}

static void lip()
{
    preg = mainram[pc++];
}

static void lp()
{
    preg = opcode & 0x3f;
}

static void stp()
{
    preg = areg;
}

static void stq()
{
    qreg = areg;
}

static void liq()
{
    qreg = mainram[pc++];
}

static void str()
{
    rreg = areg;
}

static void lidl()
{
    dp = (dp & 0xff00) |  mainram[pc++];
}

static void std()
{
    memw(dp, areg);
}

static void mvdm()
{
    memw(dp, iram[preg]);
}

static void mvmd()
{
    iram[preg] = memr(dp);
}

static void leave()
{
    iram[rreg] = 0;
}

static void pop()
{
    areg = iram[rreg++];
}

static void push()
{
    iram[--rreg] = areg;
}


/* 16bits load */
static void lidp()
{
    dp = pair(mainram[pc], mainram[pc + 1]);
    pc += 2;
}


/* block load, exchange */
static void mvb()
{
    dreg = jreg+ 1;
    do {
	iram[preg++] = iram[qreg++];
	--dreg;
    } while (dreg != 0);
}

static void mvw()
{
    dreg = ireg+ 1;
    do {
	iram[preg++] = iram[qreg++];
	--dreg;
    } while (dreg != 0);
}


static void mvbd()
{
    dreg = jreg+ 1;
    do {
	iram[preg++] = memr(dp);
	dp = inc8dp(dp);
	--dreg;
    } while (dreg != 0);
}

static void mvwd()
{
    dreg = ireg+ 1;
    do {
	iram[preg++] = memr(dp);
	dp = inc8dp(dp);
	--dreg;
    } while (dreg != 0);
}

static void film()
{
    dreg = ireg+ 1;
    do {
	iram[preg++] = areg;
	--dreg;
    } while (dreg != 0);
}


static void fild()
{
    dreg = ireg+ 1;
    do {
	memw(dp, areg);
	dp = inc8dp(dp);
	--dreg;
    } while (dreg != 0);
}


static void exab()
{
    swap(areg, breg);
}

static void exam()
{
    swap(areg, iram[preg]);
}

static void exb()
{
    dreg = jreg+ 1;
    do {
	swap(iram[preg], iram[qreg]);
	preg++;
	qreg++;
	--dreg;
    } while (dreg != 0);
}

static void exw()
{
    dreg = ireg+ 1;
    do {
	swap(iram[preg], iram[qreg]);
	preg++;
	qreg++;
	--dreg;
    } while (dreg != 0);
}

static void exwd()
{
    unsigned char tmp;

    dreg = ireg+ 1;
    do {
	tmp = memr(dp);
	memw(dp, iram[preg]);
	iram[preg] = tmp;
	preg++;
	dp = inc8dp(dp);
	--dreg;
    } while (dreg != 0);
}

static void exbd()
{
    int tmp;

    dreg = jreg+ 1;
    do {
	tmp = memr(dp);
	memw(dp, iram[preg]);
	iram[preg] = tmp;
	preg++;
	dp = inc8dp(dp);
	--dreg;
    } while (dreg!= 0);
}

static void swp()
{
    areg  = (areg << 4) | (areg >> 4);
}


static void chkcz()
{
    zflag = (alu & 0x00ff) == 0;
    cflag = (alu & 0xff00) != 0;
}



/* 8bits */
static void adia()
{
    alu = areg + mainram[pc++];
    areg = alu;
    chkcz();
}

static void adim()
{
    alu = iram[preg] + mainram[pc++];
    iram[preg] = alu;
    chkcz();
}

static void adm()
{
    alu = iram[preg] + areg;
    iram[preg] = alu;
    chkcz();
}

static void adcm()
{
    alu = iram[preg] + areg + cflag;
    iram[preg] = alu;
    chkcz();
}

static void sbia()
{
    alu = areg - mainram[pc++];
    areg = alu;
    chkcz();
}

static void sbim()
{
    alu = iram[preg] - mainram[pc++];
    iram[preg] = alu;
    chkcz();
}

static void sbm()
{
    alu = iram[preg] - areg;
    iram[preg] = alu;
    chkcz();
}


static void sbcm()
{
    alu = iram[preg] - areg - cflag;
    iram[preg] = alu;
    chkcz();
}

static void inca()
{
    qreg= 2;
    alu = areg + 1;
    areg = alu;
    chkcz();
}

static void incb()
{
    qreg= 3;
    alu = breg+ 1;
    breg= alu;
    chkcz();
}

static void inci()
{
    qreg= 0;
    alu = ireg+ 1;
    ireg= alu;
    chkcz();
}

static void incj()
{
    qreg= 1;
    alu = jreg+ 1;
    jreg= alu;
    chkcz();
}

static void inck()
{
    qreg= 8;
    alu = kreg + 1;
    kreg = alu;
    chkcz();
}

static void incl()
{
    qreg= 9;
    alu = lreg + 1;
    lreg = alu;
    chkcz();
}

static void incm()
{
    qreg= 10;
    alu = mreg+ 1;
    mreg= alu;
    chkcz();
}

static void incn()
{
    qreg= 11;
    alu = nreg + 1;
    nreg = alu;
    chkcz();
}

static void incp()
{
    ++preg;
}

static void deca()
{
    qreg= 2;
    alu = areg - 1;
    areg = alu;
    chkcz();
}

static void decb()
{
    qreg= 3;
    alu = breg- 1;
    breg= alu;
    chkcz();
}

static void deci()
{
    qreg= 0;
    alu = ireg- 1;
    ireg= alu;
    chkcz();
}

static void decj()
{
    qreg= 1;
    alu = jreg- 1;
    jreg= alu;
    chkcz();
}

static void deck()
{
    qreg= 8;
    alu = kreg - 1;
    kreg = alu;
    chkcz();
}

static void decl()
{
    qreg= 9;
    alu = lreg - 1;
    lreg = alu;
    chkcz();
}

static void decm()
{
    qreg= 10;
    alu = mreg - 1;
    mreg = alu;
    chkcz();
}

static void decn()
{
    qreg= 11;
    alu = nreg - 1;
    nreg = alu;
    chkcz();
}

static void decp()
{
    --preg;
}

static void ania()
{
    zflag = (areg &= mainram[pc++]) == 0;
}

static void anma()
{
    zflag = (iram[preg] &= areg) == 0;
}

static void anim()
{
    zflag = (iram[preg] &= mainram[pc++]) == 0;
}

static void anid()
{
    iram[rreg - 1] = memr(dp);
    memw(dp, memr(dp) & mainram[pc++]);
    zflag = (memr(dp) == 0);
}

static void oria()
{
    zflag = (areg |= mainram[pc++]) == 0;
}

static void orma()
{
    zflag = (iram[preg] |= areg) == 0;
}

static void orim()
{
    zflag = (iram[preg] |= mainram[pc++]) == 0;
}

static void orid()
{
    iram[rreg - 1] = memr(dp);
    memw(dp, memr(dp) | mainram[pc++]);
    zflag = (memr(dp) == 0);
}

static void cpia()
{
    alu = areg - mainram[pc++];
    chkcz();
}

static void cpma()
{
    alu = iram[preg] - areg;
    chkcz();
}

static void cpim()
{
    alu = iram[preg] - mainram[pc++];
    chkcz();
}

/* 16bits */

static void adb()
{
    unsigned long xx;

    xx = iram[preg] | (iram[preg + 1] << 8);
    xx += bareg;
    iram[preg] = xx;
#ifndef NO_FIX_BY_CHAME
    iram[++preg] = xx >> 8;
#else
    iram[preg + 1] = xx >> 8;
#endif
    zflag = (xx & 0x0000ffffL) == 0;
    cflag = (xx & 0xffff0000L) != 0;
}

static void sbb()
{
    unsigned long xx;

    xx = iram[preg] | (iram[preg + 1] << 8);
    xx -= bareg;
    iram[preg] = xx;
#ifndef NO_FIX_BY_CHAME
    iram[++preg] = xx >> 8;
#else
    iram[preg + 1] = xx >> 8;
#endif
    zflag = (xx & 0x0000ffffL) == 0;
    cflag = (xx & 0xffff0000L) != 0;
}

/* inc/dec load/store */
static void ix()
{
    qreg= 5;
    dp = xreg + 1;
    pairload(xhreg, xlreg, dp);
}

static void iy()
{
    qreg= 7;
    dp = yreg + 1;
    pairload(yhreg, ylreg, dp);
}

static void dx()
{
    qreg= 5;
    dp = xreg - 1;
    pairload(xhreg, xlreg, dp);
}

static void dy()
{
    qreg= 7;
    dp = yreg - 1;
    pairload(yhreg, ylreg, dp);
}

static void ixl()
{
    qreg= 5;
    dp  = xreg + 1;
    areg = memr(dp);
    pairload(xhreg, xlreg, dp);
}

static void iys()
{
    qreg= 7;
    memw(dp = yreg + 1, areg);
    pairload(yhreg, ylreg, dp);
}

static void dxl()
{
    qreg= 5;
    dp = xreg - 1;
    areg = memr(dp);
    pairload(xhreg, xlreg, dp);
}

static void dys()
{
    qreg= 7;
    memw(dp = yreg - 1, areg);
    pairload(yhreg, ylreg, dp);
}

/* rotate / shift */
static void sr()
{
    alu = areg | (cflag << 8);
    areg = alu >> 1;
    cflag = alu & 1;
}

static void sl()
{
    alu = (areg << 1) | cflag;
    areg = alu;
    cflag = (alu & 0x0100) != 0;
}

static void srw()
{
    dreg= ireg+ 1;
    alu = 0;
    do {
	alu = (alu << 8) | iram[preg];
	iram[preg] = alu >> 4;
	++preg;
	--dreg;
    } while (dreg!= 0);
}

static void slw()
{
    dreg= ireg+ 1;
    alu = 0;
    do {
	alu = (alu >> 8) | (iram[preg] << 8);
	iram[preg] = (alu >> 4);
	--preg;
	--dreg;
    } while (dreg!= 0);
}

/* bit test */
static void tsia()
{
    zflag = (areg & mainram[pc++]) == 0;
}

static void tsim()
{
    zflag = (iram[preg] & mainram[pc++]) == 0;
}

static void tsid()
{
    iram[rreg - 1] = memr(dp);
    zflag = (memr(dp) & mainram[pc++]) == 0;
}

static void cup()
{
    dreg= ireg+ 1;
    zflag = NO;
    if (xin == 0) {
	do {
	    ++preg;
	    --dreg;
	} while ((!xin) && (dreg!= 0xff));
	if (xin == 0) {
	    zflag = YES;
	}
    }
}

static void cdn()
{
    dreg= ireg+ 1;
    zflag = NO;
    if (xin) {
	do {
	    ++preg;
	    --dreg;
	} while (xin && (dreg!= 0xff));
	if (xin) {
	    zflag = YES;
	}
    }
}


unsigned int deciadd(unsigned int p, unsigned int q)
{
    unsigned int ans;

    ans = (p & 0x0f) + (q & 0x0f);
    if (ans > 0x09) {
	ans += 6;
    }
    ans += (p & 0xf0) + (q & 0xf0);
    if ((ans & 0xfff0) > 0x90) {
	ans += 0x60;
    }
    return(ans & 0x1ff);
}


#ifndef NO_HACK_BCD
unsigned int decisub(unsigned int p, unsigned int q)
{
    unsigned int ans;

    if (p >= q) {
	ans = (p & 0x0f) - (q & 0x0f);
	if (ans > 0x09) {
	    ans -= 0x06;
	}
	ans += (p & 0xf0) - (q & 0xf0);
	if ((ans & 0xfff0)  > 0x90) {
	    ans -= 0x60;
	}
    }
    else {
	ans = (q & 0x0f) - (p & 0x0f);
	if (ans > 0x09) {
	    ans -= 0x06;
	}
	ans += (q & 0xf0) - (p & 0xf0);
	if ((ans & 0xfff0)  > 0x90) {
	    ans -= 0x60;
	}
	ans |= 0x100;
    }
    return(ans & 0x1ff);
}
#else
unsigned int decisub(unsigned int p, unsigned int q)
{
    unsigned int ans;

    ans = (p & 0x0f) - (q & 0x0f);
    if (ans > 0x09) {
	ans -= 0x06;
    }
    ans += (p & 0xf0) - (q & 0xf0);
    if ((ans & 0xfff0)  > 0x90) {
	ans -= 0x60;
    }
    return(ans & 0x1ff);
}
#endif


static void adn()
{
    dreg = ireg+ 1;
    alu = areg;
    zflag = YES;
    do {
	alu = deciadd(iram[preg], alu);
	iram[preg] = alu;
	zflag = zflag && (iram[preg] == 0);
	--preg;
	--dreg;
	alu >>= 8;
    } while (dreg != 0);
    cflag = (alu != 0);
}

static void sbn()
{
    dreg = ireg+ 1;
    alu = areg;
    zflag = YES;
    do {
	alu = decisub(iram[preg], alu);
	iram[preg] = alu;
	zflag = zflag && (iram[preg] == 0);
	--preg;
	--dreg;
	alu >>= 8;
    } while (dreg != 0);
    cflag = (alu != 0);
}

static void adw()
{
    dreg = ireg+ 1;
    alu = 0;
    zflag = YES;
    do {
	alu = deciadd(iram[preg], alu);
	alu = (alu & 0x100) | deciadd(alu & 0xff, iram[qreg]);
	iram[preg] = alu;
	zflag = zflag && (iram[preg] == 0);
	--preg;
	--qreg;
	--dreg;
	alu >>= 8;
    } while (dreg != 0);
    cflag = (alu != 0);
}

static void sbw()
{
    dreg = ireg+ 1;
    alu = 0;
    zflag = YES;
    do {
	alu = decisub(iram[preg], alu);
	alu = (alu & 0x100) | decisub(alu & 0xff, iram[qreg]);
	iram[preg] = alu;
	zflag = zflag && (iram[preg] == 0);
	--preg;
	--qreg;
	--dreg;
	alu >>= 8;
    } while (dreg != 0);
    cflag = (alu != 0);
}

/* jumps */
static void jrp()
{
    pc += mainram[pc];
}

static void jrm()
{
    pc -= mainram[pc];
}

static void jrzp()
{
    pc += (zflag) ? mainram[pc] : 1;
}

static void jrzm()
{
    pc += (zflag) ? -mainram[pc] : 1;
}

static void jrnzp()
{
    pc += (!zflag) ? mainram[pc] : 1;
}

static void jrnzm()
{
    pc += (!zflag) ? -mainram[pc] : 1;
}

static void jrcp()
{
    pc += (cflag) ? mainram[pc] : 1;
}

static void jrcm()
{
    pc += (cflag) ? -mainram[pc] : 1;
}

static void jrncp()
{
    pc += (!cflag) ? mainram[pc] : 1;
}

static void jrncm()
{
    pc += (!cflag) ? -mainram[pc] : 1;
}

static void jp()
{
    pc = (mainram[pc] << 8) | mainram[pc + 1];
}

static void jpz()
{
    pc = (zflag) ? ((mainram[pc] << 8) | mainram[pc + 1])
		 : pc + 2;
}

static void jpnz()
{
    pc = (!zflag) ? ((mainram[pc] << 8) | mainram[pc + 1])
		  : pc + 2;
}

static void jpc()
{
    pc = (cflag) ? ((mainram[pc] << 8) | mainram[pc + 1])
		 : pc + 2;
}

static void jpnc()
{
    pc = (!cflag) ? ((mainram[pc] << 8) | mainram[pc + 1])
		  : pc + 2;
}

static void loop()
{
    alu = iram[rreg];
    --alu;
    iram[rreg] = alu;
    chkcz();
    if (cflag) {
	++pc;
    }
    else {
	pc -= mainram[pc];
    }
}


static void case1()
{
    dreg = mainram[pc++];
    iram[--rreg] = mainram[pc++];
    iram[--rreg] = mainram[pc++];
}

static void case2()
{
/*    zflag = NO;*/
    do {
	if (areg == mainram[pc++]) {
	    pc = (mainram[pc] << 8) + mainram[pc + 1];
/*	    zflag = YES;*/
	    break;
	}
	else {
	    pc += 2;
	}
	--dreg;
    } while (dreg != 0);
    if (dreg == 0) {
	pc = (mainram[pc] << 8) | mainram[pc + 1];
    }
}

static void cal()
{
    ++pc;
    iram[--rreg] = hi(pc);
    iram[--rreg] = lo(pc);
    pc = ((opcode & 0x1F) << 8) | mainram[pc - 1];
}

static void call()
{
    pc += 2;
    iram[--rreg] = hi(pc);
    iram[--rreg] = lo(pc);
    pc = (mainram[pc - 2] << 8) | mainram[pc - 1];
}

static void rtn()
{
    pc = pair(iram[rreg + 1], iram[rreg]);
    rreg += 2;
}

static void outa()
{
    static int cnt = 0;
    static int ch;
    struct timeval lastscan;
    struct timeval now;
    unsigned long interval;

    lastscan.tv_sec = lastscan.tv_usec = 0;
    now.tv_sec = now.tv_usec = 0;
    if ((iaport != 0) && (ia == 0)) {
	++cnt;
	if (cnt > 2) {
	    keychar = ch = ttyin(key_wait);
	    if (ch == KBDESCAPE) {
		kbdescape();
	    }
	    if (ch > 0) {
		keyscan(ch);
		gettimeofday(&lastscan, NULL);
	    }
	    else {
		gettimeofday(&now, NULL);
		interval
		    = 1000000 * (now.tv_sec - lastscan.tv_sec)
		    + now.tv_usec - lastscan.tv_usec;
		if (interval > KEY_RELEASE) {
		    keyscan(0);
		}
	    }
	    cnt = 0;
	}
    }
    qreg= 0x5c;
    iaport = ia;
}

static void outb()
{
    qreg= 0x5d;
    ibport = ib;
}

static void outf()
{
    qreg= 0x5e;
    foport = fo;
}

static void outc()
{
    qreg= 0x5f;
    disp_on = (ctrl & 1);
    if (!disp_on) {
	busy();
    }
    if (ctrl & 2) {
	ticks = ticks2 = div500 = div2 = 0;
    }
    power_on = !(ctrl & 8);
}


int bit(int ii)
{
    int jj;

    for (jj= 0; jj < 8; jj++) {
	if (ii & 1) {
	    break;
	}
	ii >>= 1;
    }
    return(jj);
}


static void ina()
{
    int jj;

    areg = 0;
    if ((iaport == 0) && (mainram[0x7e00] != 0)) {
	jj = bit(mainram[0x7e00]);
	if (jj < 7) {
	    areg = keym[jj];
	}
    }
    else {
	jj = bit(iaport);
	if (jj < 7) {
	    areg = keym[jj + 7];
	}
    }
    zflag = (areg == 0);
}

static void inb()
{
    areg = 0x38;
    zflag = (areg == 0);
}

static void sc()
{
    cflag = zflag = YES;
}

static void rc()
{
    cflag = NO;
    zflag = YES;
}

static void nop()
{
}

static void scwait()
{
    ++pc;
}

static void test()
{
    testport = 0;
    if (div2) {
	testport |= 1;
    }
    if (div500) {
	testport |= 2;
    }
    if (kon) {
	testport |= 8;
    }
    zflag = (testport & mainram[pc++]) == 0;
}


/* undefined code */
static void undef()
{
    fprintf(stderr, "Undefined code: %04x %02x\n", pc - 1, opcode);
    exit(1);
}


static void escape()
{
    int code;
    int ch;

    code = mainram[pc++];
    if (code == BYTEOUT) {
	if (ofp == NULL) {
	    ofp = fopen("sc.out", "w");
	}
	ch = areg;
	iochar = ch;
	if (ch == '\r') {
	    ch = '\n';
	}
	if (ch == PCEOF) {
	    fclose(ofp);
	    ofp = NULL;
	}
	else {
	    putc(ch, ofp);
	}
    }
    else if (code == BYTEIN) {
	if (ifp == NULL) {
	    ifp = fopen("sc.inp", "r");
	}
	if (ifp != NULL) {
	    if ((ch = getc(ifp)) == EOF) {
		fclose(ifp);
		ifp = NULL;
		areg = PCEOF;
		iram[0x0e] |= 0x80;
	    }
	    else if (ch == '\n') {
		areg = '\r';
		iram[0x0e] |= 0x40;
	    }
	    else {
		areg = ch;
		iram[0x0e] &= 0x3f;
	    }
	    iochar = ch;
	}
	else {
	    areg = PCEOF;
	    iram[0x0e] |= 0x80;
	}
/*	putchar(areg);*/
	cflag = NO; zflag = NO;
	preg = 0x0e;
    }
    else {
	fprintf(stderr, "escape: %04x %02x\n", pc - 1, code);
    }
}


typedef void (*op)();

static op ops[] = {
/*	+0	+1	+2	+3	+4	+5	+6	+7	*/
	lii,	lij,	lia,	lib,	ix,	dx,	iy,	dy,	/* 0 */
	mvw,	exw,	mvb,	exb,	adn,	sbn,	adw,	sbw,

	lidp,	lidl,	lip,	liq,	adb,	sbb,	undef,	undef,	/* 1 */
	mvwd,	exwd,	mvbd,	exbd,	srw,	slw,	film,	fild,

	ldp,	ldq,	ldr,	clra,	ixl,	dxl,	iys,	dys,	/* 2 */
	jrnzp,	jrnzm,	jrncp,	jrncm,	jrp,	jrm,	undef,	loop,

	stp,	stq,	str,	nop,	push,	mvwp,	undef,	rtn,	/* 3 */
	jrzp,	jrzm,	jrcp,	jrcm,	undef,	undef,	undef,	escape,

	inci,	deci,	inca,	deca,	adm,	sbm,	anma,	orma,	/* 4 */
	inck,	deck,	incm,	decm,	ina,	nop,	scwait,	cup,

	incp,	decp,	std,	mvdm,	mvmp,	mvmd,	ldpc,	ldd,	/* 5 */
	swp,	ldm,	sl,	pop,	undef,	outa,	undef,	outf,

	anim,	orim,	tsim,	cpim,	ania,	oria,	tsia,	cpia,	/* 6 */
	nop,	case2,	nop,	test,	undef,	undef,	undef,	cdn,

	adim,	sbim,	rz,	rz,	adia,	sbia,	rz,	rz,	/* 7 */
	call,	jp,	case1,	undef,	jpnz,	jpnc,	jpz,	jpc,

/*	+0	+1	+2	+3	+4	+5	+6	+7	*/
	lp,	lp,	lp,	lp,	lp,	lp,	lp,	lp,	/* 8 */
	lp,	lp,	lp,	lp,	lp,	lp,	lp,	lp,

	lp,	lp,	lp,	lp,	lp,	lp,	lp,	lp,	/* 9 */
	lp,	lp,	lp,	lp,	lp,	lp,	lp,	lp,

	lp,	lp,	lp,	lp,	lp,	lp,	lp,	lp,	/* A */
	lp,	lp,	lp,	lp,	lp,	lp,	lp,	lp,

	lp,	lp,	lp,	lp,	lp,	lp,	lp,	lp,	/* B */
	lp,	lp,	lp,	lp,	lp,	lp,	lp,	lp,

	incj,	decj,	incb,	decb,	adcm,	sbcm,	tsma,	cpma,	/* C */
	incl,	decl,	incn,	decn,	inb,	nop,	nop,	undef,

	sc,	rc,	sr,	nop,	anid,	orid,	tsid,	sz,	/* D */
	leave,	nop,	exab,	exam,	undef,	outb,	undef,	outc,

	cal,	cal,	cal,	cal,	cal,	cal,	cal,	cal,	/* E */
	cal,	cal,	cal,	cal,	cal,	cal,	cal,	cal,

	cal,	cal,	cal,	cal,	cal,	cal,	cal,	cal,	/* F */
	cal,	cal,	cal,	cal,	cal,	cal,	cal,	cal,
/*	+0	+1	+2	+3	+4	+5	+6	+7	*/
};


/* mnemonics */
static char *mne[] = {
/*	+0	+1	+2	+3	+4	+5	+6	+7	*/
	"lii",	"lij",	"lia",	"lib",	"ix",	"dx",	"iy",	"dy",	/* 0 */
	"mvw",	"exw",	"mvb",	"exb",	"adn",	"sbn",	"adw",	"sbw",
	"lidp",	"lidl",	"lip",	"liq",	"adb",	"sbb",	"U16",	"U17",	/* 1 */
	"mvwd",	"exwd",	"mvbd",	"exbd",	"srw",	"slw",	"film",	"fild",
	"ldp",	"ldq",	"ldr",	"clra",	"ixl",	"dxl",	"iys",	"dys",	/* 2 */
	"jrnzp","jrnzm","jrncp","jrncm","jrp",	"jrm",	"U2E",	"loop",
	"stp",	"stq",	"str",	"nop33","push",	"mvwp",	"U36",	"rtn",	/* 3 */
	"jrzp",	"jrzm",	"jrcp",	"jrcm",	"U3c",	"U3d",	"U3e",	"escape",
	"inci",	"deci",	"inca",	"deca",	"adm",	"sbm",	"anma",	"orma",	/* 4 */
	"inck",	"deck",	"incm",	"decm",	"ina",	"nop4d"	"scwait","cup",
	"incp",	"decp",	"std",	"mvdm",	"mvmp",	"mvmd",	"ldpc",	"ldd",	/* 5 */
	"swp",	"ldm",	"sl",	"pop",	"U5c",	"outa",	"U5e",	"outf",
	"anim",	"orim",	"tsim",	"cpim",	"ania",	"oria",	"tsia",	"cpia",	/* 6 */
	"nop68","case2","nop6a","test",	"U6c",	"U6d",	"U6e",	"cdn",
	"adim",	"sbim",	"rz",	"rz",	"adia",	"sbia",	"rz",	"rz",	/* 7 */
	"call",	"jp",	"case1","U7b",	"jpnz",	"jpnc",	"jpz",	"jpc",
/*	"+0	"+1	"+2	"+3	"+4	"+5	"+6	"+7	"*/
	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	/* 8 */
	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",
	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	/* 9 */
	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",
	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	/* A */
	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",
	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	/* B */
	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",	"lp",
	"incj",	"decj",	"incb",	"decb",	"adcm",	"sbcm",	"tsma",	"cpma",	/* C */
	"incl",	"decl",	"incn",	"decn",	"inb",	"nopcd"	"nopce","Ucf",
	"sc",	"rc",	"sr",	"nopd3","anid",	"orid",	"tsid",	"sz",	/* D */
	"leave","nopd9","exab",	"exam",	"Udc",	"outb",	"Ude",	"outc",
	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	/* E */
	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",
	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	/* F */
	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",	"cal",
/*	+0	+1	+2	+3	+4	+5	+6	+7	*/
};


void tictac()
{
    if (++ticks >= XTICKS) {
	div500 = !div500;
	if (++ticks2 >= XTICK2) {
	    div2 = !div2;
	    ticks2 = 0;
	}
	ticks = 0;
    }
}


int interpret()
{
    tictac();
    opcode = mainram[pc++];
#ifdef FREQ
    ++opfreq[opcode];
#endif
    (*ops[opcode])();
    return(pc);
}


int islcd()
{
    return(hi(dp) == 0x70);
}


void initsc()
{
    pc = dp = 0;
    preg = qreg= 0;
    rreg = IRAMSIZ;
    cflag = zflag = NO;
    ticks = 0;
    power_on = YES;
    kon = 0;
    iaport = ibport = foport = 0;
#ifdef PC1450_SIO_HACK
    mainram[0xed34] = ESCAPE;
    mainram[0xed35] = BYTEOUT;
    mainram[0xed36] = 0x37;
    mainram[0xec2e] = ESCAPE;
    mainram[0xec2f] = BYTEIN;
    mainram[0xec30] = 0x37;
    mainram[0xeaa8] = 0xd0;
    mainram[0xeaa9] = 0x37;
#endif
#ifdef FREQ
    {
	int i;
	for (i = 0; i < 256; i++) {
	    opfreq[i] = 0;
	}
    }
#endif
}


void closesc()
{
#ifdef FREQ
    int i;

    fp = fopen("opfreq", "w");
    for (i = 0; i < 256; i++) {
	fprintf(fp, "%d\n", opfreq[i]);
    }
#endif
}




void memdump(unsigned char mem[], int start, int len)
{
    int i, j;

    for (i = start; i < start + len; i += 16) {
	printf("%04x ", i);
	for (j = 0; j < 16; j++) {
	    printf("%02x ", mem[i + j]);
	    if ((j % 8) == 7) {
		printf(" ");
	    }
	}
	for (j = 0; j < 16; j++) {
	    if ((' ' <= mem[i + j]) && (mem[i + j] <= '~')) {
		printf("%c", mem[i + j]);
	    }
	    else {
		printf(".");
	    }
	    if ((j % 8) == 7) {
		printf(" ");
	    }
	}
	printf("\r\n");
    }
    printf("\r\n");
}


void memdump_short(unsigned char mem[], int start, int len)
{
    int i, j;

    for (i = start; i < start + len; i += 16) {
	printf("%02x: ", i);
	for (j = 0; j < 16; j++) {
	    printf("%02x ", mem[i + j]);
	    if ((j % 8) == 7) {
		printf(" ");
	    }
	}
	for (j = 0; j < 16; j++) {
	    if ((' ' <= mem[i + j]) && (mem[i + j] <= '~')) {
		printf("%c", mem[i + j]);
	    }
	    else {
		printf(".");
	    }
	    if ((j % 8) == 7) {
		printf(" ");
	    }
	}
	printf("\r\n");
    }
    printf("\r\n");
}


void dumpiram()
{
    memdump(iram, 0, IRAMSIZ);
}


void dumpmainram(int start)
{
    memdump(mainram, start, 256);
}


void scstatus()
{
    printf("PC   P  Q  R  DP     ");
    printf("C  Z  Pow kb    I/O portA portB portF ctrl 7e00\r\n");
    printf("%04x %02x %02x %02x %04x  ", pc, preg, qreg, rreg, dp);
    printf("%2x %2x", cflag, zflag);
    printf("  %s ", (power_on) ? "ON " : "OFF");
    printf("%c", (('!' <= keychar) && (keychar <= '~')) ? keychar : '.');
    if ((0 < keychar) && (keychar < 256)) {
	printf("(%02x)", keychar);
    }
    else {
	printf("(##)");
    }
    printf(" ");
    printf("%c", (ifp != NULL) ? 'I' : '-');
    printf("%c", (ofp != NULL) ? 'O' : '-');
    printf("%c", (('!' <= iochar) && (iochar <= '~')) ? iochar : '.');
    printf(" ");
    printf("%02x/%02x ", ia, iaport);
    printf("%02x/%02x ", ib, ibport);
    printf("%02x/%02x ", fo, foport);
    printf("%02x   ", ctrl);
    printf("%02x", memr(0x7e00));
    printf("\r\n");
    printf("%04x %-6s %02x %02x %02x\r\n",
	   pc, mne[memr(pc)], memr(pc), memr(pc + 1), memr(pc + 2));
    printf("    I  J  A  B  XL XH YL YH  K  L  M  N\r\n");
    memdump_short(iram, 0, 0x40);
}

/* EOF */
