#include "cpmemu.h"
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/mman.h>
#include <fcntl.h>

#define HZ 13	/* frequency of screen updates */

/* Note: Signal handling is POSIX, not ANSI */

static unsigned char *cptr;
static int OFF = 16;
static int WIDTH = 2*80;

int slowdown = 0;
int tickercnt = 0;
extern int hardware_access;

static void tickerint(int arg) {
    int i, j;
    static int inited = 0;
    unsigned char *r, *w;
    if (z80mem[4] != 0x4f) {
	inited = 0;
	return;
    }
    if (!inited) {
	inited = 1;
	for (i = 0; i < 18; ++i)
	    for (j = 0; j < 66; ++j)
		cptr[WIDTH*i+OFF+2*j-1] = 7;
	for (i = 0; i < 66; ++i) {
	    cptr[2*i + OFF-2] = 0xb0;
	    cptr[2*i + OFF-2+17*WIDTH] = 0x83;
	}
	for (i = 1; i < 17; ++i) {
	    cptr[WIDTH*i + OFF-2] = 0xff;
	    cptr[WIDTH*i + OFF-2+2*65] = 0xff;
	}
    }

    r = z80mem + 0xcc00;
    w = cptr + OFF + WIDTH;
    for (i = 0; i < 16; ++i) {
	for (j = 0; j < 64; ++j) {
	    *w = *r++;
	    w += 2;
	}
	w += 2*OFF;
    }
#if HZ == 1
    alarm(1);
#endif
}

static int childpid;
static void kill_child(void) {
    kill(childpid, SIGTERM);
}

void lowlevel_init(void) {
    struct sigaction sa;
    int memfd;

    /* get access to sound port */
    cptr = NULL;
    if (ioperm(0x61, 1, 1)) {
	hardware_access = 0;	/* no access to speaker port */
	return;
    }

    /* install signal handler */
    sa.sa_handler = tickerint;
    sa.sa_mask = 0;
    sa.sa_flags = 0;
    sigaction(SIGALRM, &sa, NULL);

    /* make video memory accessible */
    memfd = open("/dev/mem", O_RDWR);
    if (memfd > 0)
	cptr = mmap((caddr_t)0, (size_t) 2048, PROT_READ|PROT_WRITE,  
		   MAP_SHARED, memfd, (off_t) 0xb8000);
    /* printf("fd=%d, ptr=%p\n", memfd, cptr); */

    {   char *s;
	if (!(s = getenv("COLUMNS")) || !(WIDTH = 2 * atoi(s)))
	    WIDTH = 160;
	OFF = (WIDTH/2-64);	/* horizontally center a 64x16 screen */
    }

    /* benchmark */
    {   int i, usecspercall;
	struct timeval tv1, tv2;
	struct timezone tz;
	gettimeofday(&tv1, &tz);
	for (i = 0; i < 100; ++i)
	    gettimeofday(&tv2, &tz);
	usecspercall = ((tv2.tv_usec - tv1.tv_usec) + 1000000 * (tv2.tv_sec-tv1.tv_sec))
	    / 100;
	if (usecspercall < 3 || usecspercall > 1000000)
	    printf("Time for gettimeofday() cannot be determined\n");
	else {
	    /* tune down to 2 MHz Z80 */
	    slowdown = usecspercall / 4;
	    tickercnt = 70 / usecspercall;	/* 10 KHz interrupt */
	    printf("Time for gettimeofday() is %d us\n", usecspercall);
	    printf("INT performed every %d times\n", tickercnt);
	}
    }
#if HZ == 1
    alarm(1);
#else
    {   pid_t galpid;
	galpid = getpid();
	if (!(childpid = fork())) {
	    int usecs = 1000000 / HZ;
	    do {
		usleep(usecs);
	    } while (!kill(galpid, SIGALRM));
	} else
	    atexit(kill_child);
    }
#endif
}
