/* use shared memory */
#define USE_SHM

/*
 * 6845 module
 *
 * Created 14/03/94 by James Bonfield
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <setjmp.h>

#include <X11/Xlib.h>

#include <sys/param.h>
#ifdef USE_SHM
#    include <sys/ipc.h>
#    include <sys/shm.h>
#    include <X11/extensions/XShm.h>
#endif

#include "6845.h"
#include "ula.h"
#include "../defs.h"
#include "../timer.h"
#include "../memory.h"

#ifdef notdef
/*
 * The 6845 with default values for mode 7
 */

static byte r0 = 0x3f;	/* horiz total */
static byte r1 = 40;	/* char per line */
static byte r2 = 51 ;	/* horiz sync */
static byte r3 = 0x24;	/* bits 0-3 horiz sync width */
			/* bits 4-7 vert sync time */
static byte r4 = 30;	/* vert total */
static byte r5 = 2;	/* vert total adjust */
static byte r6 = 25;	/* vert displayed char */
static byte r7 = 27;	/* very sync pos */
static byte r8 = 0x93;	/* bits 0/1 interlace */
			/*      4/5 display delay */
			/*      6/7 cursor delay */
static byte r9 = 18;	/* scan lines per char */
static byte r10 = 0x72;	/* bits 0-4 cursor start */
			/*      5   cursor type */
			/*      6   cursor blink */
static byte r11 = 19;	/* cursor end */
static byte r12 = 0x7c;	/* screen start addr hi */
static byte r13 = 0x00;	/* screen start addr lo */
static byte r14 = 0;	/* cursor pos hi */
static byte r15 = 0;	/* cursor pos lo */
static byte r16 = 0;	/* light pen pos lo */
static byte r17 = 0;	/* light pen pos hi */

#endif

/*
 * mode 0 defaults
 */

static byte r0 = 0x7f;	/* horiz total */
static byte r1 = 80;	/* char per line */
static byte r2 = 98;	/* horiz sync */
static byte r3 = 0x28;	/* bits 0-3 horiz sync width */
			/* bits 4-7 vert sync time */
static byte r4 = 38;	/* vert total */
static byte r5 = 0;	/* vert total adjust */
static byte r6 = 32;	/* vert displayed char */
static byte r7 = 34;	/* very sync pos */
static byte r8 = 0x01;	/* bits 0/1 interlace */
			/*      4/5 display delay */
			/*      6/7 cursor delay */
static byte r9 = 7;	/* scan lines per char */
static byte r10 = 67;	/* bits 0-4 cursor start */
			/*      5   cursor type */
			/*      6   cursor blink */
static byte r11 = 8;	/* cursor end */
static byte r12 = 0x30;	/* screen start addr hi */
static byte r13 = 0x00;	/* screen start addr lo */
static byte r14 = 0;	/* cursor pos hi */
static byte r15 = 0;	/* cursor pos lo */
static byte r16 = 0;	/* light pen pos lo */
static byte r17 = 0;	/* light pen pos hi */

static int ula_bit_4 = 1;
static int current_reg = 0;

static Window w;
static int scrn;
static Display *d;
static GC gc;
static GC gc_set;
static GC gc_clear;
static int refresh_timer;
static int start_addr = 0x3000;

#ifdef USE_SHM
XShmSegmentInfo xshmi;
static XImage *xim;

/* ARGSUSED */
int refresh(int x) {
    XShmPutImage(d, w, gc, xim, 0, 0, 0, 0, 640, 256, False);
    
    XFlush(d);
}
#endif

static int trans[0x5000];
static char col[256][8];

/*
 * FIXME - use colour palette and XLookupColour() - A ULA thing...
 */
static int col_2[] = {1,2,3,4};
static int col_4[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

byte write_screen(word addr, byte val);
byte write_fe00(word addr, byte val);
byte read_fe00(word addr);
byte write_fe01(word addr, byte val);
byte read_fe01(word addr);

static void init_mode() {
    static int hack = 0;
    int i, j;
    int y, c, l, addr;

    /*
     * [  fe40 = Input reg B of system via which address an 8 bit latch.
     *    bits 4-5 represent the number to be added to the start of screen
     *    address in hardware to control hardware scrolling. ]
     *
     * scrn_size[4] = {0x5000, 0x4000, 0x2800, 0x1000};
     *
     * for (i = 0; i < scrn_size[(readb(0xfe40) >> 4) & 0x3]; i++)
     */

    printf("%d x %d\n", r6, r1);
    addr = 0;
    for (y = 0; y < r6; y++) {
	for (l = 0; l < 8; l++) {
	    for (c = 0; c < r1; c++) {
		trans[l + 8*(c + r1*y)] = addr;
		addr += 8;
	    }
	}
    }

    /* tmp hack - FIXME */
    switch (hack & 3) {
    case 0:
    case 3:
	for (i = 0; i < 255; i++) {
	    for (j = 7; j >=0; j--) {
		col[i][7-j] = colours[(i >> j) & 0x01];
	    }
	}
	break;
    case 1:
	for (i = 0; i < 255; i++) {
	    for (j = 3; j >= 0; j--) {
		col[i][6-j*2] = col[i][7-j*2] =
		    colours[(((i >> j) & 0x01) >> 0) +
			    (((i >> j) & 0x10) >> 3)];
	    }
	}
	break;
    case 2:
	for (i = 0; i < 255; i++) {
	    for (j = 1; j >= 0; j--) {
		col[i][4-j*4] = col[i][5-j*4] = col[i][6-j*4] = col[i][7-j*4] =
		    colours[(((i >> j) & 0x01) >> 0) +
			    (((i >> j) & 0x04) >> 1) + 
			    (((i >> j) & 0x10) >> 2) + 
			    (((i >> j) & 0x40) >> 3)];
	    }
	}
	break;
    }
    hack++;
	

    /*
     * ULA colour code table:
     *
     * bits               pixels and bits used to determine colour
     * ----               ----------------------------------------
     *
     * mode 0 (2 colour)
     * 7 6 5 4 3 2 1 0       7    6    5    4    3    2    1    0
     *
     * mode 1 (4 colour)
     * 7 6 5 4 3 2 1 0      73   73   62   62   51   51   40   40
     *
     * mode 2 (16 colour)
     * 7 6 5 4 3 2 1 0    7531 7531 7531 7531 6420 6420 6420 6420
     *
     * mode 3
     * Colours as mode 0, except skip 4 lines every 8 (yuk?)
     *
     * mode 4
     * mode 5
     * as modes 1 and 2 except double width pixels.
     *
     * mode 6
     * as mode 3 except double width pixels.
     *
     * mode 7
     * leave to teletext controller
     */
}

void init_screen() {
    int i,j;
    Window root;
    unsigned long valuemask;
    XGCValues values;
    XEvent event;

    /* add memory hooks */
    for (i = 0x3000; i <= 0x8000; i++) {
	special_mem_w[i] = write_screen;
    }

    special_mem_r[0xfe00] = read_fe00;
    special_mem_w[0xfe00] = write_fe00;
    special_mem_r[0xfe01] = read_fe01;
    special_mem_w[0xfe01] = write_fe01;

    if (NULL == (d = XOpenDisplay(NULL))) {
	fprintf(stderr, "Cannot open display.\n");
	return;
    }
    
    root = DefaultRootWindow(d);
    scrn = DefaultScreen(d);

    /* create window */
    w = XCreateSimpleWindow(d, root, 0, 0, 640, 256, 0,
			    BlackPixel(d, scrn), WhitePixel(d, scrn));
    XMapRaised(d, w);

    values.foreground = WhitePixel(d, scrn);
    values.background = BlackPixel(d, scrn);
    valuemask = GCForeground | GCBackground | GCFunction;

    values.function   = GXset;
    gc_set = XCreateGC(d, w, valuemask, &values);

    values.function   = GXclear;
    gc_clear = XCreateGC(d, w, valuemask, &values);

    values.function = GXcopy;
    gc = XCreateGC(d, w, valuemask, &values);

    XSelectInput(d, w, KeyPressMask);

    XSync(d, False);

#ifdef USE_SHM
    /* create XImage - its 'data' is the shared memory segment,
     * As this isn't yet created we pass NULL over. */
    xim = XShmCreateImage(d, DefaultVisual(d, scrn), DefaultDepth(d, scrn),
			  ZPixmap, NULL, &xshmi, 640, 256);

    /* create shared memory */
    xshmi.shmid = shmget(IPC_PRIVATE, xim->bytes_per_line * xim->height,
			 IPC_CREAT | 0777);
    xim->data = xshmi.shmaddr = shmat(xshmi.shmid, 0, 0);
    xshmi.readOnly = False;

    XShmAttach(d, &xshmi);
    
    /* remove shared memory - it'll still be available as we've got access to
     * it. But upon exit the system will tidy up for us.
     */
    /* commented out due to race condition */
    /* shmctl(xshmi.shmid, IPC_RMID, NULL); */
#endif

    init_mode();
    init_ula(d, scrn);
    refresh_timer = add_timer(1000000/HZ_6845, 1000000/HZ_6845, refresh);

    return;
}

byte write_screen(word addr, byte val) {
    register int x, y, i, j;
/*    static int ddd = 0; */

#ifdef USE_SHM
    addr -= 0x3000; /* start_addr; */

    /*
     * Two int writes, or a single long write (on the Alpha) will be far
     * faster. Must be careful with alignment boundaries though.
     */
#ifdef __alpha
    *(long *)(&xshmi.shmaddr[trans[addr]]) = *(long *)(&col[val]);
#else
    memcpy(&xshmi.shmaddr[trans[addr]], col[val], 8);
#endif

/*
    if (ddd) {
	printf("memcpy %x, [%d,%d,%d,%d,%d,%d,%d,%d]\n",
	       &xshmi.shmaddr[trans[addr]],
	       col[val][0],
	       col[val][1],
	       col[val][2],
	       col[val][3],
	       col[val][4],
	       col[val][5],
	       col[val][6],
	       col[val][7]);
    }
*/
#else
    /*
     * XPoint set[8];
     * XPoint clr[8];
     * int nset = 0, nclr = 0;
     */

    /* mode 0 translations */
    addr -= 0x3000;
    x = (addr % 640) & 0xfff8;
    y = (addr % 8) + ((addr / 80) & 0xfff8);

    /* fixme - use xdrawpoints */
    for (i = 0x80; i > 0; i >>= 1) {
	if (val & i) {
	    /*
	     * clr[nclr].x = x;
	     * clr[nclr++].y = y;
	     */
	    XDrawPoint(d, w, gc_clear, x, y);
	} else {
	    /*
	     * set[nset].x = x;
	     * set[nset++].y = y;
	     */
	    XDrawPoint(d, w, gc_set, x, y);
	}

	x++;
    }

    /*
     * if (nclr)
     *     XDrawPoints(d, w, gc_clear, clr, nclr, CoordModeOrigin);
     * if (nset)
     *     XDrawPoints(d, w, gc_set,   set, nset, CoordModeOrigin);
     */

    XFlush(d);
#endif

    return 0;
}

void write_char(char c) {
    fputc(c, stdout);
    fflush(stdout);
}

int read_char() {
    /* return fgetc(stdin); */

    XEvent xev;
    char str[10];

    do {
	do {
	    XNextEvent(d, &xev);
	} while (xev.type != KeyPress);
    } while (XLookupString(&xev, str, 10, NULL, NULL) < 1);

    /* FIXME - should use all characters with a buffering system */
    return *str;
}

/*
 * memory hooks
 */
byte write_fe00(word addr, byte val) {
    current_reg = val & 0x1f;

    return 0;
}

byte read_fe00(word addr) {
    return 0xff;
}

byte write_fe01(word addr, byte val) {
    switch (current_reg) {
    case 0:
	r0 = val;
	init_mode();
	break;
    case 1:
	r1 = val;
	break;
    case 2:
	r2 = val;
	break;
    case 3:
	r3 = val;
	break;
    case 4:
	r4 = val;
	break;
    case 5:
	r5 = val;
	break;
    case 6:
	r6 = val;
	break;
    case 7:
	r7 = val;
	break;
    case 8:
	r8 = val;
	break;
    case 9:
	r9 = val;
	break;
    case 10:
	r10 = val;
	break;
    case 11:
	r11 = val;
	break;
    case 12: {
	int i;

	r12 = val;
	printf("r12=%d\n", r12);

	if (0) /* Please don't optimise me out! :-) */ {
	case 13:
	    r13 = val;
	    printf("r13=%d\n", r13);
	}

	/* FIXME */
	break;

	start_addr = 8 * (r12 * 0x100 + r13);
	printf("Start address = %x\n", start_addr);
	/* FIXME - hardly the fastest method, but it's working anyhow */
	for (i = 0; i < 0x5000 /* FIXME - screen size */; i++) {
#ifdef USE_SHM
	    memcpy(&xshmi.shmaddr[trans[start_addr + i]],
		   col[readbq(start_addr + i)], 8);
#endif
	}
	break;
    }
    case 14:
	r14 = val;
	break;
    case 15:
	r15 = val;
	break;
    default:
	/* 16-17 are read only */
	break;
    }

    return 0;
}

byte read_fe01(word addr) {
    switch (current_reg) {
    case 14:
	return r14;
    case 15:
	return r15;
    case 16:
	return r16;
    case 17:
	return r17;
    default:
	/* 0-13 write only */
	return 0xff;
    }
}
