/***************************************************************
 *  Alcuin: A HP48 emulator for Amiga Computers
 * 
 *  (C) 1995 Stephan Schupfer (schupfer@sbox.tu-graz.ac.at)
 *
 */

#include <stdio.h>
#include <graphics/gfx.h>
#include <intuition/intuition.h>
#include <exec/memory.h>

#include "A48.h"
#include "main.h"
#include "hp48.h"
#include "annunc.h"
#include "device.h"

#define DISP_ROWS			64
#define ALL_DISP			2112
#define NIBS_PER_BUFFER_ROW	36
#define EIGHT_ROW			288
#define NPR					33

extern void read_disp (long addr,unsigned char *val);

struct {
	long r,g,b;
	} 
HP_Colors[13] = {
	{0x90ffffff,0x90ffffff,0x90ffffff},
	{0x84ffffff,0x84ffffff,0x84ffffff},
	{0x78ffffff,0x78ffffff,0x78ffffff},
	{0x6cffffff,0x6cffffff,0x6cffffff},
	{0x60ffffff,0x60ffffff,0x60ffffff},
	{0x54ffffff,0x54ffffff,0x54ffffff},
	{0x48ffffff,0x48ffffff,0x48ffffff},
	{0x3cffffff,0x3cffffff,0x3cffffff},
	{0x30ffffff,0x30ffffff,0x30ffffff},
	{0x24ffffff,0x24ffffff,0x24ffffff},
	{0x18ffffff,0x18ffffff,0x18ffffff},
	{0x0cffffff,0x0cffffff,0x0cffffff},
	{0x00ffffff,0x00ffffff,0x00ffffff}
};

UBYTE farbe[16][2]= {
	{0,1},{0,2},{0,4},{0,6},
	{0,8},{1,10},{2,12},{3,12},
	{4,12},{5,12},{6,12},{7,12},
	{8,12},{9,12},{10,12},{11,12}
	};

UBYTE Palette[13]= {1,2,3,4,5,6,7,8,9,10,11,12,13};

typedef struct ann_struct {
int		bit;
int		x;
UBYTE	*pixmap;
} ann_struct;


ann_struct ann_tbl[] = {
	{ ANN_LEFT, 16 },
	{ ANN_RIGHT, 39 },
	{ ANN_ALPHA, 63 },
	{ ANN_BATTERY, 85 },
	{ ANN_BUSY, 109 },
	{ ANN_IO, 132 },
	{ 0 }
	};

ann_struct ann_tbl_big[] = {
	{ ANN_LEFT, 36 },
	{ ANN_RIGHT, 81 },
	{ ANN_ALPHA, 126 },
	{ ANN_BATTERY, 171 },
	{ ANN_BUSY, 216 },
	{ ANN_IO, 261 },
	{ 0 }
};

UBYTE nibbles_ch[16][4] = {
	{0x00,0x00,0x00,0x00}, /* ---- */
	{0x01,0x00,0x00,0x00}, /* *--- */
	{0x00,0x01,0x00,0x00}, /* -*-- */
	{0x01,0x01,0x00,0x00}, /* **-- */
	{0x00,0x00,0x01,0x00}, /* --*- */
	{0x01,0x00,0x01,0x00}, /* *-*- */
	{0x00,0x01,0x01,0x00}, /* -**- */
	{0x01,0x01,0x01,0x00}, /* ***- */
	{0x00,0x00,0x00,0x01}, /* ---* */
	{0x01,0x00,0x00,0x01}, /* *--* */
	{0x00,0x01,0x00,0x01}, /* -*-* */
	{0x01,0x01,0x00,0x01}, /* **-* */
	{0x00,0x00,0x01,0x01}, /* --** */
	{0x01,0x00,0x01,0x01}, /* *-** */
	{0x00,0x01,0x01,0x01}, /* -*** */
	{0x01,0x01,0x01,0x01}, /* **** */
	};

UBYTE nibbles_big_ch[16][16] = {
	{ 0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* ---- */
	 0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00 },
	{ 0x01,0x01,0x00,0x00,  0x00,0x00,0x00,0x00,	/* *--- */
	 0x01,0x01,0x00,0x00,  0x00,0x00,0x00,0x00 },
	{ 0x00,0x00,0x01,0x01,  0x00,0x00,0x00,0x00,	/* -*-- */
	 0x00,0x00,0x01,0x01,  0x00,0x00,0x00,0x00 },
	{ 0x01,0x01,0x01,0x01,  0x00,0x00,0x00,0x00,	/* **-- */
	 0x01,0x01,0x01,0x01,  0x00,0x00,0x00,0x00 },
	{ 0x00,0x00,0x00,0x00,  0x01,0x01,0x00,0x00,	/* --*- */
	 0x00,0x00,0x00,0x00,  0x01,0x01,0x00,0x00 },
	{ 0x01,0x01,0x00,0x00,  0x01,0x01,0x00,0x00,	/* *-*- */
	 0x01,0x01,0x00,0x00,  0x01,0x01,0x00,0x00 },
	{ 0x00,0x00,0x01,0x01,  0x01,0x01,0x00,0x00,	/* -**- */
	 0x00,0x00,0x01,0x01,  0x01,0x01,0x00,0x00 },
	{ 0x01,0x01,0x01,0x01,  0x01,0x01,0x00,0x00,	/* ***- */
	 0x01,0x01,0x01,0x01,  0x01,0x01,0x00,0x00 },
	{ 0x00,0x00,0x00,0x00,  0x00,0x00,0x01,0x01,	/* ---* */
	 0x00,0x00,0x00,0x00,  0x00,0x00,0x01,0x01 },
	{ 0x01,0x01,0x00,0x00,  0x00,0x00,0x01,0x01,	/* *--* */
	 0x01,0x01,0x00,0x00,  0x00,0x00,0x01,0x01 },
	{ 0x00,0x00,0x01,0x01,  0x00,0x00,0x01,0x01,	/* -*-* */
	 0x00,0x00,0x01,0x01,  0x00,0x00,0x01,0x01 },
	{ 0x01,0x01,0x01,0x01,  0x00,0x00,0x01,0x01,	/* **-* */
	 0x01,0x01,0x01,0x01,  0x00,0x00,0x01,0x01 },
	{ 0x00,0x00,0x00,0x00,  0x01,0x01,0x01,0x01,	/* --** */
	 0x00,0x00,0x00,0x00,  0x01,0x01,0x01,0x01 },
	{ 0x01,0x01,0x00,0x00,  0x01,0x01,0x01,0x01,	/* *-** */
	 0x01,0x01,0x00,0x00,  0x01,0x01,0x01,0x01 },
	{ 0x00,0x00,0x01,0x01,  0x01,0x01,0x01,0x01,	/* -*** */
	 0x00,0x00,0x01,0x01,  0x01,0x01,0x01,0x01 },
	{ 0x01,0x01,0x01,0x01,  0x01,0x01,0x01,0x01,	/* **** */
	 0x01,0x01,0x01,0x01,  0x01,0x01,0x01,0x01 }
};

static int menu_x[272]={
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
	18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
	18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
	18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
	18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
	18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
	18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
	18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,
	0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
	18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,
	};

static int menu_y[272]={
	56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,
	56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,56,
	57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,
	57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,57,
	58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,
	58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,58,
	59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,
	59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,59,
	60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,
	60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,60,
	61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,
	61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,
	62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,
	62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,62,
	63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,
	63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63,63
	};

struct RastPort Trp,*tmprp;
struct BitMap Tbm;
UBYTE nibble_maps[16][32];
UBYTE empty[192];
unsigned char disp_buf[64][33];
static int last_annunc_state = -1;
unsigned char	display_on;
long			display_disp_start;
long			display_disp_end;
char			display_offset;
char			disp_offset;
int				display_lines;
int				display_nibs_per_line;
unsigned char	display_contrast;
long			display_menu_start;
long			display_menu_end;
int oy,ay,aye;
int allocpens=0;
int Tmem=0;
int column[33];

void	(*draw_nibble) (int c, int r, unsigned char val);
void	(*draw_menu_nibble) (int c, int r, unsigned char val);
void	(*draw_annunc) (void);
void    (*draw_whole) (int len);
void    (*draw_rest) (int len);


/************************************************************/
void AllocatePens()
{
int i;

if (FastGraph) {
	for(i=0; i<13; i++) {
		Palette[i] = (UBYTE)(ObtainBestPenA(scr->ViewPort.ColorMap,HP_Colors[i].r,HP_Colors[i].g,HP_Colors[i].b,NULL));
		}
	allocpens=1;
	}
}

/************************************************************/
void FreePens()
{
int i;

for(i=0; i<13; i++) ReleasePen(scr->ViewPort.ColorMap,Palette[i]);
}

/***********************************************************************/
inline void redraw_display(void)
{
memset(disp_buf,0xf0,ALL_DISP);
update_display();
}

/************************************************************/
inline void adjust_contrast(int contrast)
{
UBYTE Color[2];
int c,i,k;

if (FastGraph) {
	c=contrast-9;
	for (i=0;i<16;i++) {
		Color[0]=Palette[farbe[c][1]];
		Color[1]=Palette[farbe[c][0]];
		if (double_res) {
			for (k=0;k<16;k++) 
				if (nibbles_big_ch[i][k]) nibble_maps[i][k]=Color[0];
				else nibble_maps[i][k]=Color[1];
			}
		else {
			for (k=0;k<4;k++)
				if (nibbles_ch[i][k]) nibble_maps[i][k]=Color[0];
				else nibble_maps[i][k]=Color[1];
			}
		}
	}
redraw_display();
}

/***********************************************************************/
void init_nibble_maps(void)
{
int i,j;

for (i = 0; i < 16; i++) {
	if (double_res) {
		if (FastGraph) {
			for (j=0;j<16;j++)
				nibble_maps[i][j]=nibbles_big_ch[i][j];
			}
		else {
			for (j=0;j<8;j++)
				nibble_maps[i][j]=nibbles_big_ch[i][j];
			for (j=8;j<16;j++)
				nibble_maps[i][j+8]=nibbles_big_ch[i][j];
			}
		}
	else
		for (j=0;j<4;j++)
			nibble_maps[i][j]=nibbles_ch[i][j];
	}
}

/***********************************************************************/
void init_annunc(void)
{
int i,j,k;

ay=offy+8;
if (double_res) {
	aye=offy+19;
 	for (i=0;i<6;i++)
		ann_tbl_big[i].pixmap=ann_bits_big_ch[i];
	}
else {
	aye=offy+14;
	for (i=0;i<6;i++)
		if (FastGraph)
			ann_tbl[i].pixmap=ann_bits_ch_f[i];
		else 
			ann_tbl[i].pixmap=ann_bits_ch[i];
	}
}

/***********************************************************************/
static void draw_annunc_small(void)
{
int i,x;
UBYTE nm[112];

if (saturn_annunc == last_annunc_state) return;
last_annunc_state = saturn_annunc;
for (i = 0; ann_tbl[i].bit; i++) {
	if ((ann_tbl[i].bit & saturn_annunc) == ann_tbl[i].bit) {
		x=ann_tbl[i].x+offx;
		memcpy(nm,ann_tbl[i].pixmap,112);
		WritePixelArray8(rp,x,ay,x+8,aye,nm,tmprp);
		}
	else {
		x=ann_tbl[i].x+offx;  
		memcpy(nm,empty,112);
		WritePixelArray8(rp,x,ay,x+8,aye,nm,tmprp);
		}
	}
}

/***********************************************************************/
static void draw_annunc_small_f(void)
{
int i,x;

if (saturn_annunc == last_annunc_state) return;
last_annunc_state = saturn_annunc;
for (i = 0; ann_tbl[i].bit; i++) {
	if ((ann_tbl[i].bit & saturn_annunc) == ann_tbl[i].bit) {
		x=ann_tbl[i].x+offx;
		WriteChunkyPixels(rp,x,ay,x+8,aye,ann_tbl[i].pixmap,9);
		}
	else {
		x=ann_tbl[i].x+offx;  
		WriteChunkyPixels(rp,x,ay,x+8,aye,empty,9);
		}
	}
}

/***********************************************************************/
static void draw_annunc_big(void)
{
int i,x;
UBYTE nm[192];

if (saturn_annunc == last_annunc_state) return;
last_annunc_state = saturn_annunc;
for (i = 0; ann_tbl_big[i].bit; i++) {
	if ((ann_tbl_big[i].bit & saturn_annunc) == ann_tbl_big[i].bit) {
		x=ann_tbl_big[i].x+offx;
		memcpy(nm,ann_tbl_big[i].pixmap,192);
		WritePixelArray8(rp,x,ay,x+15,aye,nm,tmprp);
		}
	else {
		x=ann_tbl_big[i].x+offx;  
		memcpy(nm,empty,192);
		WritePixelArray8(rp,x,ay,x+15,aye,nm,tmprp);
		}
	}
}

/***********************************************************************/
static void draw_annunc_big_f(void)
{
int i,x;

if (saturn_annunc == last_annunc_state) return;
last_annunc_state = saturn_annunc;
for (i = 0; ann_tbl_big[i].bit; i++) {
	if ((ann_tbl_big[i].bit & saturn_annunc) == ann_tbl_big[i].bit) {
		x=ann_tbl_big[i].x+offx;
		WriteChunkyPixels(rp,x,ay,x+15,aye,ann_tbl_big[i].pixmap,16);
		}
	else {
		x=ann_tbl_big[i].x+offx;  
		WriteChunkyPixels(rp,x,ay,x+15,aye,empty,16);
		}
	}
}

/***********************************************************************/
static void draw_nibble_small(int c, int r, unsigned char val)
{
UBYTE nm[16];

memcpy(nm,nibble_maps[val],4);
WritePixelLine8(rp,column[c]-disp_offset,r+oy,4,nm,tmprp);
}

/***********************************************************************/
static void draw_nibble_small_f(int c, int r, unsigned char val)
{
int x,y;

x=column[c]-disp_offset;
y=r+oy;
WriteChunkyPixels(rp,x,y,x+3,y,nibble_maps[val],4);
}

/***********************************************************************/
static void draw_nibble_big(int c, int r, unsigned char val)
{
int x,y;
UBYTE nm[32];

memcpy(nm,nibble_maps[val],24);
x=column[c]-disp_offset;
y=(r<<1)+oy;
WritePixelArray8(rp,x,y,x+7,y+1,nm,tmprp);
}

/***********************************************************************/
static void draw_nibble_big_f(int c, int r, unsigned char val)
{
int x,y;

x=column[c]-disp_offset;
y=(r<<1)+oy;
WriteChunkyPixels(rp,x,y,x+7,y+1,nibble_maps[val],8);
}

/***********************************************************************/
static void draw_menu_nibble_small(int c, int r, unsigned char val)
{
UBYTE nm[16];

memcpy(nm,nibble_maps[val],4);
WritePixelLine8(rp,column[c],r+oy,4,nm,tmprp);
}

/***********************************************************************/
static void draw_menu_nibble_small_f(int c, int r, unsigned char val)
{
int x,y;

x=column[c];
y=r+oy;
WriteChunkyPixels(rp,x,y,x+3,y,nibble_maps[val],4);
}

/***********************************************************************/
static void draw_menu_nibble_big(int c, int r, unsigned char val)
{
int x,y;
UBYTE nm[32];

memcpy(nm,nibble_maps[val],24);
x=column[c];
y=(r<<1)+oy;
WritePixelArray8(rp,x,y,x+7,y+1,nm,tmprp);
}

/***********************************************************************/
static void draw_menu_nibble_big_f(int c, int r, unsigned char val)
{
int x,y;

x=column[c];
y=(r<<1)+oy;
WriteChunkyPixels(rp,x,y,x+7,y+1,nibble_maps[val],8);
}

/***********************************************************************/
void init_display(void)
{
int i,j;
unsigned char *mybuf;

memset(disp_buf,0xf0,ALL_DISP);
display_on = saturn_disp_io >> 3;
display_disp_start = (saturn_disp_addr & 0xffffe);
display_offset = saturn_disp_io & 0x7;
disp_offset = 2 * display_offset;
display_lines = (saturn_line_count & 0x3f)+1;
if (!display_lines) display_lines = 64;
/*disp_lines = 2 * display_lines;
if (disp_lines < 110) disp_lines = 110;*/
if (display_offset > 3)
	display_nibs_per_line = (NIBBLES_PER_ROW+saturn_line_offset+2) & 0xfff;
else
	display_nibs_per_line = (NIBBLES_PER_ROW+saturn_line_offset) & 0xfff;
display_disp_end = display_nibs_per_line * display_lines;
/*
display_menu_start = saturn_menu_addr;
display_menu_end = saturn_menu_addr + 0x110;
*/
display_contrast = saturn_contrast_ctrl;
display_contrast |= ((saturn_disp_test & 0x1) << 4);
/*display_annunc = saturn_annunc;*/
/*BltClear(disp_buf, sizeof(disp_buf),0);*/
/*mybuf=db;
for (i=0;i<DISP_ROWS;i++)
	for (j=0;j<2*NIBS_PER_BUFFER_ROW;j++) {
		*mybuf=0xf0;
		mybuf++;
		}
*/
AllocatePens();
if (!FastGraph) {
	Trp=*rp;
	Trp.Layer=NULL;
	InitBitMap(&Tbm,rp->BitMap->Depth,16,1);
	Trp.BitMap=&Tbm;
	for (i=0;i<Tbm.Depth;i++) {
		if ((Tbm.Planes[i]=(UBYTE *)AllocRaster(16,1))==NULL)
			stirb("Can't alloc mem for %s.",102,"TmpRastPort");
		Tmem++;
		}
	tmprp=&Trp;
	}
init_nibble_maps();
init_annunc();
if (double_res) {
	oy=offy+24;
	for (i=0;i<33;i++) column[i]=(i<<3)+offx+24;
	if (FastGraph) {
		draw_nibble = draw_nibble_big_f;
		draw_menu_nibble = draw_menu_nibble_big_f;
		draw_annunc = draw_annunc_big_f;
		}
	else {
		draw_nibble = draw_nibble_big;
		draw_menu_nibble = draw_menu_nibble_big;
		draw_annunc = draw_annunc_big;
		}
	}
else {
	oy=offy+19;
	for (i=0;i<33;i++) column[i]=(i<<2)+offx+11;
	if (FastGraph) {
		draw_nibble = draw_nibble_small_f;
		draw_menu_nibble = draw_menu_nibble_small_f;
		draw_annunc = draw_annunc_small_f;
		}
	else {
		draw_nibble = draw_nibble_small;
		draw_menu_nibble = draw_menu_nibble_small;
		draw_annunc = draw_annunc_small;
		}
	}
}

/***********************************************************************/
static inline void draw_row(long addr, int row)
{
int i=NPR,v;
unsigned char *mybuf;
unsigned char rrow[NPR];

read_disp(addr,rrow);
mybuf=&(disp_buf[row][32]);
while (i--) {
	v=rrow[i];
	if(*mybuf != v) {
		*mybuf=v;
		draw_nibble(i,row,v);
		}
	mybuf--;
	}
}

/***********************************************************************/
void update_display(void)
{
int i, j;
long addr;
static char old_offset = -1;
static int old_lines = -1;

if (display_on) {
	addr = display_disp_start;
	if (display_offset != old_offset) {
		memset(disp_buf,0xf0,display_lines*33);
        old_offset = display_offset;
		}
	if (display_lines != old_lines) {
		memset(&(disp_buf[56][0]),0xf0,264);
		old_lines = display_lines;
		}
	for (i = 0; i < display_lines; i++) {
		draw_row(addr, i);
		addr += display_nibs_per_line;
		}
	if (i < DISP_ROWS) {
		addr = saturn_menu_addr;
		for (; i < DISP_ROWS; i++) {
			draw_row(addr, i);
			addr += NIBBLES_PER_ROW;
			}
		}
	}
else {
	/*memset(disp_buf,0,ALL_DISP);*/
	}
}

/**********************************************************************/
inline void disp_draw_nibble(int offset, unsigned char val)
{
int x, y;
unsigned char *mybuf;

x=offset%display_nibs_per_line;
if (x>32) return;
if (display_nibs_per_line) {
	y=offset/display_nibs_per_line;
	mybuf=&(disp_buf[y][x]);
	if (*mybuf != val) {
		*mybuf=val;
		draw_nibble(x,y,val);
		}
	}
else {
	mybuf=&(disp_buf[0][x]);
	for (y = 0; y < display_lines; y++) {
		if (*mybuf != val) {
			*mybuf =val;
			draw_nibble(x, y, val);
			}
		mybuf+=33;
		}
	}
}

/***********************************************************************/
inline void menu_draw_nibble(int offset, unsigned char val)
{
int x, y;
unsigned char *mybuf;

x=menu_x[offset];
if (x>32) return;
y=menu_y[offset];
mybuf=&(disp_buf[y][x]);
if (*mybuf != val) {
	*mybuf = val;
	draw_menu_nibble(x,y,val);
	}
}

/***********************************************************************/
inline void redraw_annunc(void)
{
last_annunc_state = -1;
draw_annunc();
}

/***********************************************************************/
void free_gfx()
{
int i;

while (Tmem--) FreeRaster(Tbm.Planes[Tmem],16,1);
if (allocpens) FreePens();
}

/***********************************************************************/
