#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <SDL.h>

#include "gr-sim.h"
#include "tfv_zp.h"
#include "6502_emulate.h"

#include "apple2_font.h"

/* 280x192 hi-res mode	*/
#define HGR_XSIZE	280
#define HGR_YSIZE	192
#define HGR_X_SCALE	2
#define HGR_Y_SCALE	2

/* 40x48 low-res mode	*/
#define GR_XSIZE	40
#define GR_YSIZE	48
#define GR_X_SCALE	14
#define GR_Y_SCALE	8

/* 40 column only for now */
#define TEXT_XSIZE	40
#define TEXT_YSIZE	24
#define TEXT_X_SCALE	14
#define TEXT_Y_SCALE	16

static int xsize=GR_XSIZE*GR_X_SCALE;
static int ysize=GR_YSIZE*GR_Y_SCALE;

static int debug=0;

/* Zero page addresses */
#define WNDLFT	0x20
#define WNDWDTH	0x21
#define WNDTOP	0x22
#define WNDBTM	0x23
#define	CH	0x24
#define CV	0x25
#define GBASL	0x26
#define GBASH	0x27
#define BASL	0x28
#define BASH	0x29
#define	BAS2L	0x2A
#define BAS2H	0x2B
#define H2	0x2C
#define V2	0x2D
#define MASK	0x2E
#define COLOR	0x30
#define INVFLG	0x32
#define YSAV	0x34
#define YSAV1	0x35
#define CSWL	0x36
#define CSWH	0x37
#define FIRST	0xF0
#define SPEEDZ	0xF1
#define FLASH	0xF3
#define TEMP	0xFA

static int text_mode=0xff;
static int text_page_1=0x00;
static int hires_on=0x00;
static int mixed_graphics=0xff;

static int plaid_mode=0;

void set_plaid(void) {
	plaid_mode=1;
}

void clear_plaid(void) {
	plaid_mode=0;
}

void soft_switch(unsigned short address) {

	switch(address) {
		case TXTCLR:	// $c050
			text_mode=0x00;
			break;
		case TXTSET:	// $c051
			text_mode=0xff;
			break;
		case MIXCLR:	// $c052
			mixed_graphics=0x00;
			break;
		case MIXSET:	// $c053
			mixed_graphics=0xff;
			break;
		case LOWSCR:	// $c054
			text_page_1=0x00;
			break;
		case HISCR:	// $c055
			text_page_1=0xff;
			break;
		case LORES:	// $c056
			hires_on=0x00;
			break;
		case HIRES:	// $c057
			hires_on=0xff;
			break;
		default:
			fprintf(stderr,"Unknown soft switch %x\n",address);
			break;
	}
}

int soft_switch_read(unsigned short address) {

	switch(address) {
		case TEXT_RD:	// $c01a
			return text_mode;
		case MIXED_RD:	// $c01b
			return mixed_graphics;
		case PAGE2_RD:	// $c01c
			return text_page_1;
		case HIRES_RD:	// $c01d
			return hires_on;
		default:
			fprintf(stderr,"Unknown soft switch read %x\n",address);
			break;
	}
	return 0;
}

static SDL_Surface *sdl_screen=NULL;

int grsim_input(void) {

	SDL_Event event;
	int keypressed;


	while ( SDL_PollEvent(&event)) {

		switch(event.type) {

		case SDL_KEYDOWN:
			keypressed=event.key.keysym.sym;
			switch (keypressed) {

			case SDLK_ESCAPE:
				return 27;
			case 'a'...'z':
			case 'A'...'Z':
				return keypressed;
			case SDLK_UP:
				return 11;
			case SDLK_DOWN:
				return 10;
			case SDLK_RIGHT:
				return 21;
			case SDLK_LEFT:
				return 8;
			default:
				printf("Unknown %d\n",keypressed);
				return keypressed;
			}
			break;


		case SDL_JOYBUTTONDOWN:
		case SDL_JOYAXISMOTION:
			printf("Joystick!\n");
			break;

		default:
			printf("Unknown input action!\n");
			break;

		}
	}

	return 0;
}

static unsigned int color[16]={
	0,		/*  0 black */
	0xe31e60,	/*  1 magenta */
	0x604ebd,	/*  2 dark blue */
	0xff44fd,	/*  3 purple */
	0x00a360,	/*  4 dark green */
	0x9c9c9c,	/*  5 grey 1 */
	0x14cffd,	/*  6 medium blue */
	0xd0c3ff,	/*  7 light blue */
	0x607203,	/*  8 brown */
	0xff6a3c,	/*  9 orange */
	0x9d9d9d,	/* 10 grey 2 */
	0xffa0d0,	/* 11 pink */
	0x14f53c,	/* 12 bright green */
	0xd0dd8d,	/* 13 yellow */
	0x72ffd0,	/* 14 aqua */
	0xffffff,	/* 15 white */
};

static unsigned int hcolor[8]={
	0,		/*  0 black */
	0x2ad600,
//	0x14f53c,	/*  1 bright green */
//	0xff44fd,	/*  2 purple */
	0xc530ff,
	0xffffff,	/*  3 white */
	0,		/*  4 black */
	0xff6a3c,	/*  5 orange */
	0x14cffd,	/*  6 medium blue */
	0xffffff,	/*  7 white */
};

static unsigned int hcolor_hack[8][2]={
	{0,0},		// 0000 KK
	{0,0},		// 0010 KK
	{1,1},		// 0100 OO
	{1,3},		// 0110 OW
	{2,0},		// 1000 BK
	{2,2},		// 1010 BB
	{3,3},		// 1100 WW
	{3,3},		// 1110 WW
};



	/* a = ycoord */
static int gbascalc(unsigned char a) {

	unsigned char s,c;

			/* input ABCD EFGH */
	s=a;		/* store a on stack */
	c=a&1;
	a=a>>1;		/* lsr */
	a=a&0x3;	/* mask */
	a=a|0x4;	/* 00001FG */
	ram[GBASH]=a;
	a=s;

	a=a&0x18;	/* 000D E000 */

	/* if odd */
	if (c) {
		a=a+0x7f+1;
	}
	ram[GBASL]=a;
	a=a<<2;
	a=a|ram[GBASL];
	ram[GBASL]=a;

	if (debug) printf("GBAS=%02X%02X\n",ram[GBASH],ram[GBASL]);

	return 0;
}

int scrn(unsigned char xcoord, unsigned char ycoord) {

	unsigned char a,y,c;

	a=ycoord;
	y=xcoord;

	c=a&1;
	a=a>>1;
	gbascalc(a);
	a=ram[y_indirect(GBASL,y)];

	if (c) {
		return a>>4;
	}
	else {
		return a&0xf;
	}

	return 0;
}

int scrn_page(unsigned char xcoord, unsigned char ycoord, int page) {

	unsigned char a,y,c;

	a=ycoord;
	y=xcoord;

	c=a&1;
	a=a>>1;
	gbascalc(a);
	ram[GBASH]+=page;
	a=ram[y_indirect(GBASL,y)];

	if (c) {
		return a>>4;
	}
	else {
		return a&0xf;

	}

	return 0;
}

static short gr_addr_lookup[24]={
	0x400,0x480,0x500,0x580,0x600,0x680,0x700,0x780,
	0x428,0x4a8,0x528,0x5a8,0x628,0x6a8,0x728,0x7a8,
	0x450,0x4d0,0x550,0x5d0,0x650,0x6d0,0x750,0x7d0,
};



static void draw_lowres(unsigned int *out_pointer,int gr_start, int gr_end) {

	int i,j,yy,xx;
	int gr_addr,gr_addr_hi;
	int temp_col;
	unsigned int *t_pointer;

	t_pointer=out_pointer+(gr_start*40*GR_X_SCALE*GR_Y_SCALE);

	/* do the top 40/48 if in graphics mode */
	for(yy=gr_start;yy<gr_end;yy++) {

		for(j=0;j<GR_Y_SCALE;j++) {

			gr_addr=gr_addr_lookup[yy/2];
			gr_addr_hi=yy%2;

			/* adjust for page */
			if (text_page_1) {
				gr_addr+=0x400;
			}

			for(xx=0;xx<GR_XSIZE;xx++) {

				if (gr_addr_hi) {
					temp_col=(ram[gr_addr]&0xf0)>>4;
				}
				else {
					temp_col=ram[gr_addr]&0x0f;
				}

				for(i=0;i<GR_X_SCALE;i++) {
					*t_pointer=color[temp_col];
					t_pointer++;
				}
				gr_addr++;
			}
		}
	}
}

void draw_text(unsigned int *out_pointer,int text_start, int text_end) {

	int bit_set,ch,inverse,flash;
	int gr_addr;
	int xx,yy,i,j;
	unsigned int *t_pointer;

	t_pointer=out_pointer+(text_start*40*TEXT_Y_SCALE*TEXT_X_SCALE);

	for(yy=text_start;yy<text_end;yy++) {
		for(j=0;j<TEXT_Y_SCALE;j++) {
			for(xx=0;xx<TEXT_XSIZE;xx++) {

				gr_addr=gr_addr_lookup[yy];

				/* adjust for page */
				if (text_page_1) {
					gr_addr+=0x400;
				}

				ch=ram[gr_addr+xx];

				if (ch&0x80) {
					flash=0;
					inverse=0;
					ch=ch&0x7f;
				}
				else if (ch&0x40) {
					flash=1;
					inverse=0;
					ch=ch&0x3f;
				}
				else {
					inverse=1;
					flash=0;
					ch=ch&0x3f;
				}

				for(i=0;i<TEXT_X_SCALE;i++) {

		/* 14 x 16 */
		/* but font is 5x7 */

		if (j>13) bit_set=0;
		else if (i>11) bit_set=0;
		else bit_set=(a2_font[ch][j/2])&(1<<(5-(i/2)));

		if (inverse) {
			if (bit_set) *t_pointer=color[0];
			else *t_pointer=color[15];
		}
		else if (flash) {
			if (bit_set) *t_pointer=color[9];
			else *t_pointer=color[0];
		}
		else {
			if (bit_set) *t_pointer=color[15];
			else *t_pointer=color[0];
		}

		t_pointer++;

				}
			}
		}
	}
}



void draw_hires(unsigned int *out_pointer,int y_start, int y_end) {

	int i,j,yy,xx;
	int gr_addr;
	int temp_col;
	unsigned int *t_pointer;
	int last_last_pixel,last_pixel,current_byte,current_pixel,odd=0;
	int old_high=0,current_high=0;

	t_pointer=out_pointer+(y_start*280*HGR_X_SCALE*HGR_Y_SCALE);

	/* do the hires graphics */
	for(yy=y_start;yy<y_end;yy++) {

		for(j=0;j<HGR_Y_SCALE;j++) {

			gr_addr=gr_addr_lookup[yy/8]+0x1c00;
			gr_addr+=0x400*(yy&0x7);

			/* adjust for page */
			if (text_page_1) {
				gr_addr+=0x2000;
			}
			last_pixel=0;
			last_last_pixel=0;
			odd=0;

			for(xx=0;xx<HGR_XSIZE/7;xx++) {
//				printf("HGR ADDR=%x\n",gr_addr);
				current_byte=ram[gr_addr];


// BBBBBBBB     OOOOOOOO   OO?WW?BB  BB?KK?OO
// 10101010     01010101   01011010  10100101
// 1 1 010101   1 0 101010
// d5           aa         


				for(i=0;i<7;i++) {
					old_high=current_high;
					current_high=!!(current_byte&0x80);
					current_pixel=!!(current_byte&(1<<i));

					if (!odd) {
					int pattern;
					pattern=last_last_pixel<<2 |
						last_pixel<<1|
						current_pixel;

					temp_col=(old_high<<2)|
						hcolor_hack[pattern][0];

//					printf("Temp col=%d\n",temp_col);
//					for(k=0;k<HGR_X_SCALE;k++) {

					*t_pointer=hcolor[temp_col];
					t_pointer++;
					*t_pointer=hcolor[temp_col];
					t_pointer++;

					temp_col=(current_high<<2)|
						hcolor_hack[pattern][1];

					*t_pointer=hcolor[temp_col];
					t_pointer++;
					*t_pointer=hcolor[temp_col];
					t_pointer++;
					}
					odd=!odd;
					last_last_pixel=last_pixel;
					last_pixel=current_pixel;
				}

				gr_addr++;
			}
		}
	}

}


int grsim_update(void) {

	unsigned int *t_pointer;

	/* point to SDL output pixels */
	t_pointer=((Uint32 *)sdl_screen->pixels);

	/* get the proper modes */

	if (plaid_mode) {
		draw_text(t_pointer,0,6);
		draw_hires(t_pointer,48,112);
		draw_lowres(t_pointer,28,48);
	} else if (text_mode) {
		draw_text(t_pointer,0,TEXT_YSIZE);
	}
	else {
		if (hires_on) {
			draw_hires(t_pointer,0,192);
		}
		else {
			draw_lowres(t_pointer,0,48);
		}

		if (mixed_graphics) {
			draw_text(t_pointer,20,TEXT_YSIZE);
		}
	}

	SDL_UpdateRect(sdl_screen, 0, 0, xsize, ysize);

	return 0;
}


void setnorm(void) {

	y=0xff;
	ram[INVFLG]=y;

}

int grsim_init(void) {

	int mode;
	int x;

	mode=SDL_SWSURFACE|SDL_HWPALETTE|SDL_HWSURFACE;

	if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
		fprintf(stderr,
			"Couldn't initialize SDL: %s\n", SDL_GetError());
		return -1;
	}

	/* Clean up on exit */
	atexit(SDL_Quit);

	/* assume 32-bit color */
	sdl_screen = SDL_SetVideoMode(xsize, ysize, 32, mode);

	if ( sdl_screen == NULL ) {
		fprintf(stderr, "ERROR!  Couldn't set %dx%d video mode: %s\n",
			xsize,ysize,SDL_GetError());
		return -1;
	}

	SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);

	/* Init screen */
	for(x=0x400;x<0x800;x++) ram[x]=0;

	/* Set up some zero page values */
	ram[WNDLFT]=0x00;
	ram[WNDWDTH]=0x28;
	ram[WNDTOP]=0x00;
	ram[WNDBTM]=0x18;

	a=0; y=0; x=0;

	//FA62 RESET

	setnorm();

	return 0;
}

int color_equals(int new_color) {

	/* Top and Bottom both have color */
	ram[COLOR]=((new_color%16)<<4)|(new_color%16);

	return 0;
}



static void monitor_plot(void) {

	unsigned char c;

	/* Call into Monitor $F800 */

	c=a&1;	/* save LSB in carry	*/
	a=a>>1;	/* lsr A */
	gbascalc(a);

	if (c) {
		/* If odd, mask is 0xf0 */
		ram[MASK]=0xf0;
	}
	else {
		/* If even, mask is 0x0f */
		ram[MASK]=0x0f;
	}

	a=ram[y_indirect(GBASL,y)];

	a=a^ram[COLOR];

	a=a&ram[MASK];

	a=a^ram[y_indirect(GBASL,y)];

	ram[y_indirect(GBASL,y)]=a;

}


int basic_plot(unsigned char xcoord, unsigned char ycoord) {

	if (ycoord>47) {
		printf("Y too big %d\n",ycoord);
		return -1;
	}

	/* Applesoft Source Code	*/
	/* F225	GET X,Y Values		*/
	/* Y-coord in A			*/
	/* X-coord in Y			*/
	/* Check that X-coord<40	*/
	a=ycoord;
	y=xcoord;

	if (y>=40) {
		printf("X too big %d\n",y);
		return -1;
	}

	monitor_plot();

	return 0;
}


int basic_hlin(int x1, int x2, int at) {

	int i;

	for(i=x1;i<=x2;i++) basic_plot(i,at);

	return 0;
}


static void bascalc(void) {
	// FBC1

	unsigned char s,c;

	s=a;
	c=a&0x1;

	a=a>>1;
	a=a&0x3;
	a=a|0x4;
	ram[BASH]=a;
	a=s;
	a=a&0x18;
	if (c!=0) {
		a=a+0x80;
	}
// BSCLC2
	ram[BASL]=a;
	a=a<<2;
	a=a|ram[BASL];
	ram[BASL]=a;

}

static void vtabz(void) {

	bascalc();

	a+=ram[WNDLFT];
	ram[BASL]=a;

}

static void rom_vtab(void) {
	/* fb5b */
	a=ram[CV];
	vtabz();
}

static void setwnd(void) {

	ram[WNDTOP]=a;
	a=0x0;
	ram[WNDLFT]=a;
	a=0x28;
	ram[WNDWDTH]=a;
	a=0x18;
	ram[WNDBTM]=a;
	a=0x17;
// TABV
	ram[CV]=a;
	rom_vtab();
}

static void vline(void) {

	unsigned char s;

	// f828
vline_loop:
	s=a;
	monitor_plot();
	a=s;
	if (a<ram[V2]) {
		a++;
		goto vline_loop;
	}
}

static void clrtop(void) {

	// f836
	y=0x27;
	ram[V2]=y;
	y=0x27;
clrsc3:
	a=0x0;
	ram[COLOR]=a;
	vline();
	y--;
	if (y<=0x80) goto clrsc3;
}

static void setgr(void) {

	// FB40
	// SETGR
	soft_switch(TXTCLR);	// LDA	TXTCLR
	soft_switch(MIXSET);	// LDA	MIXSET

	clrtop();

	a=0x14;
	setwnd();

}

int gr(void) {

	// F390
	soft_switch(LORES);	// LDA SW.LORES
	soft_switch(MIXSET);	// LDA SW.MIXSET
	//JMP MON.SETGR

	setgr();

	return 0;
}

int bload(char *filename, int address) {

	FILE *fff;
	int count=0,ch=0;

	fff=fopen(filename,"r");
	if (fff==NULL) {
		fprintf(stderr,"Could not open %s\n",filename);
		return -1;
	}

	while(1) {

		if ((address+count)>RAMSIZE) {
			fprintf(stderr,"ERROR ram too high\n");
			return -1;
		}


		ch=fgetc(fff);
		if (ch<0) break;

		ram[address+count]=ch;
		count++;
	}
	fclose(fff);

	return 0;
}


static int cleolz(void) {
	// FC9E

	a=0xa0;
clreol2:
	ram[y_indirect(BASL,y)]=a;
	y++;

	if (y<ram[WNDWDTH]) goto clreol2;

	return 0;
}

static int cleop1(void) {

	unsigned char s;

cleop1_begin:
	s=a;
	vtabz();
	cleolz();
	y=0x00;
	a=s;
	a++;
	if (a<=ram[WNDBTM]) goto cleop1_begin;
	rom_vtab();

	return 0;
}

int home(void) {

	/* FC58 */
	a=ram[WNDTOP];
	ram[CV]=a;
	y=0x00;
	ram[CH]=y;
	cleop1();

	return 0;
}

int grsim_unrle_original(unsigned char *rle_data, int address) {

	unsigned char s;

	ram[GBASL]=0;			/* input address */
	ram[GBASH]=0;			/* we fake this in this environment */

	x=0;				/* Set X and Y registers to 0 */
	y=0;

	ram[BASL]=address&0xff;		/* output address? */
	ram[BASH]=(address>>8)&0xff;

	ram[CV]=0;

	/* Read xsize, put in CH */
	ram[CH]=rle_data[y_indirect(GBASL,y)];
	y++;

	/* Skip ysize, we won't need it */
	y++;

	while(1) {
		/* Get run length into a */
		a=rle_data[y_indirect(GBASL,y)];

		/* 0xff is a special value meaning end */
		if (a==0xff) break;

		/* Store run length into TEMP */
		ram[TEMP]=a;

		/* 16-bit increment of GBASL:GBASH */
		y++;
		if (y==0) ram[GBASH]++;

		/* Get the color into A */

		a=rle_data[y_indirect(GBASL,y)];

		/* 16-bit increment of GBASL:GBASH */
		y++;
		if (y==0) ram[GBASH]++;

		/* Push y on stack */
		s=y;
		y=0;

		while(1) {
			/* store out color */
			ram[y_indirect(BASL,y)]=a;

			/* 16-bit increment of output pointer */
			ram[BASL]++;
			if (ram[BASL]==0) ram[BASH]++;

			/* increment size */
			x++;

			/* if size longer than width, adjust */
			if (x>=ram[CH]) {
				if (ram[BASL]>0xa7) ram[BASH]++;
				ram[BASL]+=0x58;
				ram[CV]+=2;
				if (ram[CV]>14) {
					ram[CV]=0;
					if (ram[BASL]<0xd8) {
						ram[BASL]=ram[BASL]-0xd8;
						ram[BASH]=ram[BASH]-0x4;
					}
					else {
						ram[BASL]=ram[BASL]-0xd8;
						ram[BASH]=ram[BASH]-0x3;
					}
				}
				x=0;
			}

			/* repeat until use up all of run length */
			ram[TEMP]--;
			if (ram[TEMP]==0) break;
		}
		/* restore y from stack */
		y=s;
	}

	return 0;
}

int grsim_unrle(unsigned char *rle_data, int address) {

	unsigned char s;

	ram[GBASL]=0;			/* input address */
	ram[GBASH]=0;			/* we fake this in this environment */

	x=0;				/* Set X and Y registers to 0 */
	y=0;

	ram[BASL]=address&0xff;		/* output address? */
	ram[BASH]=(address>>8)&0xff;

	ram[CV]=0;

	/* Read xsize, put in CH */
	ram[CH]=rle_data[y_indirect(GBASL,y)];
	y++;

	/* Skip ysize, we won't need it */
//	y++;

	while(1) {

		/* Get byte into A */
		a=rle_data[y_indirect(GBASL,y)];

		/* 0xa1 is a special value meaning end */
		if (a==0xa1) break;

		/* Store run length into TEMP */
		if ((a&0xf0)==0xa0) {
			if ((a&0xf)==0) {
				/* 16-bit increment of GBASL:GBASH */
				y++;
				if (y==0) ram[GBASH]++;

				a=rle_data[y_indirect(GBASL,y)];
				ram[TEMP]=a;

			}
			else {
				ram[TEMP]=a&0xf;
			}

			/* 16-bit increment of GBASL:GBASH */
			y++;
			if (y==0) ram[GBASH]++;

			/* Get the color into A */
			a=rle_data[y_indirect(GBASL,y)];

		}
		else {
			ram[TEMP]=1;
		}

		/* 16-bit increment of GBASL:GBASH */
		y++;
		if (y==0) ram[GBASH]++;

		/* Push y on stack */
		s=y;
		y=0;

#if 0
	{
		printf("Run=%d Color=%x\n",ram[TEMP],a);
	}
#endif
		while(1) {
			/* store out color */
			ram[y_indirect(BASL,y)]=a;

			/* 16-bit increment of output pointer */
			ram[BASL]++;
			if (ram[BASL]==0) ram[BASH]++;

			/* increment size */
			x++;

			/* if size longer than width, adjust */
			if (x>=ram[CH]) {
				if (ram[BASL]>0xa7) ram[BASH]++;
				ram[BASL]+=0x58;
				ram[CV]+=2;
				if (ram[CV]>14) {
					ram[CV]=0;
					if (ram[BASL]<0xd8) {
						ram[BASL]=ram[BASL]-0xd8;
						ram[BASH]=ram[BASH]-0x4;
					}
					else {
						ram[BASL]=ram[BASL]-0xd8;
						ram[BASH]=ram[BASH]-0x3;
					}
				}
				x=0;
			}

			/* repeat until use up all of run length */
			ram[TEMP]--;
			if (ram[TEMP]==0) break;
		}
		/* restore y from stack */
		y=s;

#if 0
	{
		grsim_update();
		int ch;
		ch=repeat_until_keypressed();
		grsim_update();
		if (ch=='q') break;
	}
#endif


	}

	return 0;
}

int basic_vlin(int y1, int y2, int at) {

	// f244

	// LINCOOR
	// GET "A,B AT C"
	// PUT SMALLER OF (A,B) IN FIRST,
        // AND LARGER  OF (A,B) IN H2 AND V2.
        // RETURN WITH (X) = C-VALUE.

	if (y1>y2) { ram[H2]=y1; ram[V2]=y1; ram[FIRST]=y2; }
	else       { ram[H2]=y2; ram[V2]=y2; ram[FIRST]=y1; }
	x=at;

	if (x>40) {
		fprintf(stderr,"Error!  AT too large %d!\n",x);
	}

//VLIN  JSR LINCOOR
//F244- 8A       2050        TXA          X-COORD IN Y-REG
//F245- A8       2060        TAY
//F246- C0 28    2070        CPY #40      X-COORD MUST BE < 40
//F248- B0 BC    2080        BCS GOERR    TOO LARGE
//F24A- A5 F0    2090        LDA FIRST    TOP END OF LINE IN A-REG
//F24C- 4C 28 F8 2100        JMP MON.VLINE     LET MONITOR DRAW LINE

	y=x;
	if (y>=40) {
		fprintf(stderr,"X value to big in vline %d\n",y);
		return -1;
	}
	a=ram[FIRST];

	vline();

//	for(i=y1;i<y2;i++) basic_plot(at,i);

	return 0;
}



int grsim_put_sprite_page(int page, unsigned char *sprite_data, int xpos, int ypos) {

	unsigned char i;
	unsigned char *ptr;
	short address;
	int cycles=0;

	ptr=sprite_data;
	x=*ptr;
	ptr++;
	ram[CV]=*ptr;
	ptr++;

	ypos=ypos&0xfe;

							cycles+=28;

	while(1) {
		address=gr_addr_lookup[ypos/2];
		address+=(page)<<8;
		address+=xpos;
							cycles+=36;
		for(i=0;i<x;i++) {
			a=*ptr;
							cycles+=17;
			// all transparent, skip
			if (a==0xaa) {
			}
			// bottom transparent
			else if ((a&0xf0)==0xa0) {
							cycles+=8;
				ram[address]&=0xf0;
				ram[address]|=(a&0xf);
							cycles+=19;
			}
			// top transparent
			else if ((a&0x0f)==0xa) {
							cycles+=8;
				ram[address]&=0x0f;
				ram[address]|=(a&0xf0);
							cycles+=19;
			}
			else {
							cycles+=8;
				ram[address]=a;
							cycles+=19;
			}
			ptr++;
			address++;
							cycles+=13;
		}
		ypos+=2;
		ram[CV]--;
		if (ram[CV]==0) break;
							cycles+=18;
	}
							cycles+=6;
	return cycles;
}

int grsim_put_sprite(unsigned char *sprite_data, int xpos, int ypos) {

	int cycles;

	cycles=grsim_put_sprite_page(ram[DRAW_PAGE],sprite_data,xpos,ypos);

	return cycles;
}


int gr_copy(short source, short dest) {

	short dest_addr,source_addr;
	int i,j,l;

	for(i=0;i<8;i++) {
		source_addr=gr_addr_lookup[i]+(source-0x400);
		dest_addr=gr_addr_lookup[i]+(dest-0x400);

		if (i<4) l=120;
		else l=80;

		for(j=0;j<l;j++) {
			ram[dest_addr+j]=ram[source_addr+j];
		}
	}

	return 0;
}

int gr_copy_to_current(short source) {

	short dest_addr,source_addr;
	int i,j,l;

	for(i=0;i<8;i++) {
		source_addr=gr_addr_lookup[i]+(source-0x400);
		dest_addr=gr_addr_lookup[i]+(ram[DRAW_PAGE]<<8);

//		if (i<4) l=120;	// only copy 40 lines
//		else l=80;

		l=120;	// copy 48 lines

		for(j=0;j<l;j++) {
			ram[dest_addr+j]=ram[source_addr+j];
		}
	}

	return 0;
}

int gr_copy48(short source, short dest) {

	short dest_addr,source_addr;
	int i,j;

	for(i=0;i<8;i++) {
		source_addr=gr_addr_lookup[i]+0x400;
		dest_addr=gr_addr_lookup[i];

		for(j=0;j<120;j++) {
			ram[dest_addr+j]=ram[source_addr+j];
		}
	}

	return 0;
}


int text(void) {
	// FB36

	soft_switch(LOWSCR);	// LDA LOWSCR ($c054)
	soft_switch(TXTSET);	// LDA TXTSET ($c051);
	a=0;

	setwnd();

	return 0;
}

static void scroll(void) {
	unsigned char s;

	// fc70

	a=ram[WNDTOP];
	s=a;
	vtabz();

	// SCRL1
scrl1:
	a=ram[BASL];
	ram[BAS2L]=a;
	a=ram[BASH];
	ram[BAS2H]=a;
	y=ram[WNDWDTH];
	y--;
	a=s;
	a+=1;
	if (a>=ram[WNDBTM]) {
		// SCRL3
		y=0;
		cleolz();
		rom_vtab();
		return;
	}
	s=a;
	vtabz();
	// SCRL2
scrl2:
	a=ram[y_indirect(BASL,y)];
	ram[y_indirect(BAS2L,y)]=a;
	y--;
	if (y<0x80) goto scrl2;
	goto scrl1;
}

static void lf(void) {
	ram[CV]=ram[CV]+1;
	a=ram[CV];
	if (a<ram[WNDBTM]) {
		vtabz();
		return;
	}
	ram[CV]=ram[CV]-1;
	scroll();
}

static void cr(void) {
	a=0x00;
	ram[CH]=a;
	lf();
}



static void bell1(void) {
}

static void up(void) {

	a=ram[WNDTOP];
	if (a>ram[CV]) return;

	ram[CV]=ram[CV]-1;
	rom_vtab();
}

static void bs(void) {

	ram[CH]=ram[CH]-1;

	/* still positive */
	if (ram[CH]<0x80) return;

	a=ram[WNDWDTH];
	ram[CH]=a;
	ram[CH]=ram[CH]-1;

	up();
}

static void storadv(void) {

	// fbf0

	y=ram[CH];
	ram[y_indirect(BASL,y)]=a;

	// advance

	ram[CH]=ram[CH]+1;
	a=ram[CH];
	if (a>=ram[WNDWDTH]) {
		cr();
	}

}

static void vidout(void) {
	// fbfd

	if (a>=0xa0) {
		storadv();
		return;
	}

	/* Control Characters */
	y=a;
	// if bit 7 is set then we set negative flag
	// BPL storadv
	if (a<0x80) {
		storadv();
		return;
	}

	/* carriage return */
	if (a==0x8d) {
		cr();
		return;
	}

	/* linefeed */
	if (a==0x8a) {
		lf();
		return;
	}

	/* backspace */
	if (a==0x88) {
		bs();
		return;
	}

	/* any other control code, beep */
	bell1();
	return;

}

static void vidwait(void) {
	// check if control-S being pressed

	vidout();
}

static void cout1(void) {

	unsigned char s;

	if (a<0xa0) {
	}
	else {
		a=a&ram[INVFLG];
	}
	// coutz
	ram[YSAV1]=y;
	s=a;

	vidwait();

	a=s;
	y=ram[YSAV1];

}

static void cout(void) {
	// FDED
	//	jmp (cswl)	custom handler
	cout1();
}

static void wait(void) {
}

static void outdo(void) {

	unsigned char s;

	/* Print char in the accumulator */
	a=a|0x80;		/* raw ascii has high bit on Apple II */
	if (a<0xa0) {
		/* skip if control char? */
	}
	else {
		a=a|ram[FLASH];
	}
	cout();
	a=a&0x7f;	// ?
	s=a;		// pha
	a=ram[SPEEDZ];
	wait(); 	// this is BASIC, slow down if speed set
	a=s;
}

static void crdo(void) {
	// DAFB
	a=13;	// carriage return
	outdo();
	a=a^0xff;		/* negate for some reason? */
}

void basic_htab(int xpos) {

	unsigned char s;

	// F7E7


	x=xpos;	// JSR GETBYT
	x--;	// DEX
	a=x;	// TXA
	while(a>=40) {
		s=a;	// PHA
		crdo();
		a=s;	// PLA
		a-=40;
	}
	ram[CH]=a;	// STA MON.CH

	// KRW for the win!

}

static void tabv(void) {

	// TABV
	// fb5b
	ram[CV]=a;
	rom_vtab();
}

void basic_vtab(int ypos) {
	// f256
	x=ypos;
	x--;		/* base on zero */
	a=x;

	if (a>23) {
		fprintf(stderr,"Error, vtab %d too big\n",ypos);
		return;
	}
	tabv();
}

void basic_print(char *string) {

	int i;

	for(i=0;i<strlen(string);i++) {
		a=string[i];
		outdo();
	}

}

void basic_inverse(void) {
	// F277
	a=0x3f;
	x=0;
	ram[INVFLG]=a;
	ram[FLASH]=x;

	return;
}

void basic_normal(void) {
	// F273
	a=0xff;
	x=0;
	ram[INVFLG]=a;
	ram[FLASH]=x;

	return;
}

static unsigned short hlin_addr;
static unsigned short hlin_hi;


int hlin_continue(int width) {

	int i;

	for(i=0;i<width;i++) {
		if (hlin_hi) {
			ram[hlin_addr]=ram[hlin_addr]&0x0f;
			ram[hlin_addr]|=ram[COLOR]&0xf0;
		}
		else {
			ram[hlin_addr]=ram[hlin_addr]&0xf0;
			ram[hlin_addr]|=ram[COLOR]&0x0f;
		}
		hlin_addr++;
	}

	return 0;
}


int plot(unsigned char xcoord, unsigned char ycoord) {

	unsigned char c;

	hlin_addr=gr_addr_lookup[ycoord/2];
	hlin_addr+=(ram[DRAW_PAGE])<<8;
	hlin_addr+=xcoord;

	hlin_hi=ycoord&1;

	if (hlin_hi) {
		/* If odd, mask is 0xf0 */
		ram[MASK]=0xf0;
	}
	else {
		/* If even, mask is 0x0f */
		ram[MASK]=0x0f;
	}

	c=ram[COLOR]&ram[MASK];
	ram[hlin_addr]&=~ram[MASK];
	ram[hlin_addr]|=c;

	return 0;
}



int hlin(int page, int x1, int x2, int at) {

	hlin_addr=gr_addr_lookup[at/2];
	hlin_hi=at&1;

	hlin_addr+=(page)<<8;

	hlin_addr+=x1;
	hlin_continue(x2-x1);

	return 0;
}

static unsigned short vlin_hi;

/* TODO: optimize */
/* Could make twice as fast if draw neighboring two in one write */
/* instead of two */

int vlin(int y1, int y2, int at) {

	x=y1;
	ram[V2]=y2;
	y=at;

vlin_loop:
//	for(a=y1;a<y2;a++) {

		ram[TEMPY]=y;
		a=x;
		y=a/2;

		ram[OUTL]=gr_addr_lookup[y]&0xff;
		ram[OUTH]=(gr_addr_lookup[y]>>8)&0xff;

		ram[OUTH]+=ram[DRAW_PAGE];

		vlin_hi=x&1;

		y=ram[TEMPY];	// y=at;

		if (vlin_hi) {
			ram[y_indirect(OUTL,y)]=ram[y_indirect(OUTL,y)]&0x0f;
			ram[y_indirect(OUTL,y)]|=ram[COLOR]&0xf0;
		}
		else {
			ram[y_indirect(OUTL,y)]=ram[y_indirect(OUTL,y)]&0xf0;
			ram[y_indirect(OUTL,y)]|=ram[COLOR]&0x0f;
		}
	x++;
	if (x<ram[V2]) goto vlin_loop;

	return 0;
}

int hlin_double_continue(int width) {

	x=width;

hlin_loop:
	y=0;
	ram[y_indirect(GBASL,y)]=ram[COLOR];
	ram[GBASL]++;
	x--;
	if (x!=0) goto hlin_loop;

//	ram[GBASL]+=width;

//	}

	return 0;
}

int hlin_setup(int page, int x1, int x2, int at) {

	// page, y, V2, A
	a=at;
	y=at/2;

	ram[GBASL]=(gr_addr_lookup[y])&0xff;
	ram[GBASH]=(gr_addr_lookup[y]>>8);

	ram[GBASH]+=(page);

	ram[GBASL]+=x1;

	return 0;

}

int hlin_double(int page, int x1, int x2, int at) {

	hlin_setup(page,x1,x2,at);

	hlin_double_continue(x2-x1+1);

	return 0;
}

#if 0
int collision(int xx, int yy, int ground_color) {

	int page=0;
	int beach_color;
	int collide=1;

	hlin_addr=gr_addr_lookup[yy/2];

	hlin_addr+=(page)<<8;

	hlin_addr+=xx;

	beach_color=COLOR_YELLOW|(COLOR_YELLOW<<4);

	if ((ram[hlin_addr]==ground_color) &&
		(ram[hlin_addr+1]==ground_color) &&
		(ram[hlin_addr+2]==ground_color)) {

		printf("NOCOLLIDE %x!\n",ground_color);
		collide=0;
	}

	if ((ram[hlin_addr]==beach_color) &&
		(ram[hlin_addr+1]==beach_color) &&
		(ram[hlin_addr+2]==beach_color)) {

		printf("NOCOLLIDE %x!\n",beach_color);
		collide=0;
	}

	printf("COLLIDE %x %x %x not %x %x\n",
		ram[hlin_addr],ram[hlin_addr+1],ram[hlin_addr+2],
		ground_color,beach_color);

	return collide;
}
#endif

#if 0

void clear_top_a(void) {

	int i;

	ram[COLOR]=a;
	for(i=0;i<40;i+=2) {
		hlin_double(ram[DRAW_PAGE],0,40,i);
	}
}

void clear_top(int page) {
	int i;

	ram[COLOR]=0x0;
	for(i=0;i<40;i+=2) {
		hlin_double(page,0,40,i);
	}
}

void clear_bottom(int page) {
	int i;

	/* NORMAL space */
	ram[COLOR]=0xa0;
	for(i=40;i<48;i+=2) {
		hlin_double(page,0,40,i);
	}
}

#endif

void vtab(int ypos) {
	ram[CV]=ypos-1;
}

void htab(int xpos) {
	ram[CH]=xpos-1;
}

void move_cursor(void) {

	int address;

	address=gr_addr_lookup[ram[CV]];
	address+=ram[CH];

	address+=(ram[DRAW_PAGE]<<8);

	ram[BASL]=address&0xff;
	ram[BASH]=address>>8;

}

void print_both_pages(char *string) {
	unsigned char temp;

	temp=ram[DRAW_PAGE];

	ram[DRAW_PAGE]=0;
	move_and_print(string);

	ram[DRAW_PAGE]=4;
	move_and_print(string);

	ram[DRAW_PAGE]=temp;

}

void move_and_print(char *string) {

	int address;

	address=gr_addr_lookup[ram[CV]];
	address+=ram[CH];

	address+=(ram[DRAW_PAGE]<<8);

	ram[BASL]=address&0xff;
	ram[BASH]=address>>8;

	print(string);
}



void print(char *string) {

	for(y=0;y<strlen(string);y++) {
		a=string[y];
		a=a|0x80;
		ram[y_indirect(BASL,y)]=a;
	}
	ram[BASL]+=strlen(string);
}

void print_inverse(char *string) {

	for(y=0;y<strlen(string);y++) {
		a=string[y];
		if ((a>='a') && (a<='z')) a&=~0x20;	// convert to uppercase
		a=(a&0x3f);

		ram[y_indirect(BASL,y)]=a;
	}
	ram[BASL]+=strlen(string);
}

void print_flash(char *string) {

	for(y=0;y<strlen(string);y++) {
		a=string[y];
		a=(a&0x3f)|0x40;
		ram[y_indirect(BASL,y)]=a;
	}
	ram[BASL]+=strlen(string);
}