/* Screen handling utility functions V1.22 4/9/04
   See screen.h for docs
   Copyright 2008 Jeffrey Lee
   This file is part of WOUM.
   WOUM is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
   WOUM is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   You should have received a copy of the GNU General Public License
   along with WOUM.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _SCREEN_C
#define _SCREEN_C

#include <math.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"
#include "swis.h"

#include "screen.h"

static _kernel_swi_regs screen_regs;

screen_struct screen;

void screen_getdata(void)
{
	int vars[10];
	vars[0] = 148;
	vars[1] = 149;
	vars[2] = 7;
	vars[3] = 11;
	vars[4] = 12;
	vars[5] = 3;
	vars[6] = 4;
	vars[7] = 5;
	vars[8] = 150;
	vars[9] = -1;
	screen_regs.r[0] = (int) vars;
	screen_regs.r[1] = (int) &screen;
	_kernel_swi(OS_ReadVduVariables,&screen_regs,&screen_regs);
	screen.osx = screen.width << screen.xeig;
	screen.osy = screen.height << screen.yeig;
	/* Calculate how many screen banks are available at the moment */
	screen.num_banks = screen.size/screen.length;
}

void screen_write8(int x,int y,int c)
{
	*((char *) screen.vdu+((screen.width+1) * y) + x) = c;
}

int screen_read8(int x,int y)
{
	return (int) (*((char *) screen.vdu+((screen.width+1) * y) + x));
}

trucol screen_reados(int x,int y)
{
	trucol pix;
	int tmp;
	/* Perform translation into a trucol */
	screen_regs.r[0] = x;
	screen_regs.r[1] = y;
	_kernel_swi(OS_ReadPoint,&screen_regs,&screen_regs);
	tmp = screen_regs.r[2] | screen_regs.r[3];
	if (screen.colours == MODE_16B)
	{
		/* Manual conversion */
		pix.r = (tmp & 0x1F) << 3;
		pix.g = (tmp & 0x3E0) >> 2;
		pix.b = (tmp & 0x7C00) >> 7;
	}
	else if (screen.colours == MODE_24B)
	{
		pix.r = (tmp & 255);
		pix.g = (tmp & 0xFF00) >> 8;
		pix.b = (tmp & 0xFF0000) >> 16;
	}
	else if (screen.colours == MODE_8B)
	{
		/* Translate from TTBBGGRR */
		pix.r = (((tmp & 192) >> 6) + ((tmp & 3) << 2)) * 17;
		pix.g = (((tmp & 192) >> 6) + (tmp & 12)) * 17;
		pix.b = (((tmp & 192) >> 6) + ((tmp & 48) >> 2)) * 17;
	}
	else
	{
		/* Use another SWI... */
		screen_regs.r[0] = tmp;
		screen_regs.r[1] = 16;
		_kernel_swi(OS_ReadPalette,&screen_regs,&screen_regs);
		tmp = screen_regs.r[2];
		pix.r = (tmp & 0xFF00) >> 8;
		pix.g = (tmp & 0xFF0000) >> 16;
		pix.b = (tmp >> 24) & 255;
	}
	return pix;
}

void screen_swap(void)
{
	int tmp;
	screen_regs.r[0] = 112;
	screen_regs.r[1] = screen.displaynum; /* New vdu bank is (what we believe to be) current vdu one */
	_kernel_swi(OS_Byte,&screen_regs,&screen_regs);
	screen_regs.r[0] = 113;
	screen_regs.r[1] = screen.vdunum;
	_kernel_swi(OS_Byte,&screen_regs,&screen_regs);
	tmp = screen.vdunum;
	screen.vdunum = screen.displaynum;
	screen.displaynum = tmp;
}

void screen_setbank(int b)
{
	screen_regs.r[0] = 112;
	screen_regs.r[1] = b;
	_kernel_swi(OS_Byte,&screen_regs,&screen_regs);
	screen_regs.r[0] = 113;
	screen_regs.r[1] = b;
	_kernel_swi(OS_Byte,&screen_regs,&screen_regs);
	screen.vdunum = screen.displaynum = b;
}

void screen_setvdu(int b)
{
	screen_regs.r[0] = 112;
	screen_regs.r[1] = b;
	_kernel_swi(OS_Byte,&screen_regs,&screen_regs);
	screen.vdunum = b;
}

void screen_setdisplay(int b)
{
	screen_regs.r[0] = 113;
	screen_regs.r[1] = b;
	_kernel_swi(OS_Byte,&screen_regs,&screen_regs);
	screen.displaynum = b;
}

void screen_getbanks()
{
	screen_regs.r[0] = 250;
	screen_regs.r[1] = 0;
	screen_regs.r[2] = 255;
	_kernel_swi(OS_Byte,&screen_regs,&screen_regs);
	screen.vdunum = screen_regs.r[1];
	screen.displaynum = screen_regs.r[2];
}

void screen_vsync(void)
{
	screen_regs.r[0] = 19;
	_kernel_swi(OS_Byte,&screen_regs,&screen_regs);
}

void screen_setmode(int mode)
{
	screen_regs.r[0] = 0;
	screen_regs.r[1] = mode;
	_kernel_swi(OS_ScreenMode,&screen_regs,&screen_regs);
}

void screen_oldmode(int mode)
{
	/* Use SWI &100 to set the mode */
	_kernel_swi(0x100 + 22,&screen_regs,&screen_regs);
	_kernel_swi(0x100 + mode,&screen_regs,&screen_regs);
}

void screen_rpcmode(int x,int y,int col)
{
	int mblock[6];
	mblock[0] = 1;
	mblock[1] = x;
	mblock[2] = y;
	mblock[3] = col; /* 0=1bpp,1=2bpp,2=4bpp,3=8bpp,4=16bpp,5=24bpp */
	mblock[4] = -1;
	mblock[5] = -1;
	screen_regs.r[0] = 0;
	screen_regs.r[1] = (int) mblock;
	_kernel_swi(OS_ScreenMode,&screen_regs,&screen_regs);
}

trucol screen_s8_2_24(int col)
{
	/* Convert the screens 8bit format (BGGRBRTT) to 24bit union */
	trucol conv;
	conv.x = 0;
	conv.r = ((col & 7) + ((col & 16) >> 1)) * 17;
	conv.g = ((col & 3) + ((col & 96) >> 3)) * 17;
	conv.b = ((col & 3) + ((col & 8) >> 1) + ((col & 128) >> 4)) * 17;
	return conv;
}

int screen_24_2_s8(trucol col)
{
	int c;
	/* Convert 24bit union to screens 8bit format */
	c = ((col.r & 63) + (col.g & 63) + (col.b & 63))/48;
	c = c+(col.r & 64)/16 + (col.r & 128)/8;
	c = c+(col.g & 192)/2;
	c = c+(col.b & 64)/8 + (col.b & 128);
	return c;
}

int screen_24_2_s16(trucol col)
{
	/* Convert 24 bit into 16 bit */
	return (col.r & (31*8))/8 + (col.g & (31*8))*4 + (col.b & (31*8))*128;
}

int screen_createbanks(int nb)
{
	screen_getdata();
	/* Will try and claim screen mem based upon nb*screen.length */
	screen_regs.r[0]=2;
	screen_regs.r[1]=(nb*screen.length)-screen.size;
	_kernel_swi(OS_ChangeDynamicArea,&screen_regs,&screen_regs);
	screen_getdata();
	if (screen.num_banks != nb)
		return 0;
	return 1;
}

int screen_readmode()
{
	/* Return mode number/specifier block */
	int l;
	int *b;
	screen_regs.r[0]=1;
	_kernel_swi(OS_ScreenMode,&screen_regs,&screen_regs);
	if (((unsigned int) screen_regs.r[1]) < 256)
		return screen_regs.r[1];
	b = (int *) screen_regs.r[1];
	l=5;
	while (b[l] != -1)
		l+=2;
	l++;
	b = malloc(4*l);
	if (b == 0)
		return 0;
	memcpy(b,(void *) screen_regs.r[1],l*4);
	return (int) b;
}

int *screen_enumeratemodes(int *l)
{
	/* Return list of all screen modes */
	int *i;
	screen_regs.r[0] = 2;
	screen_regs.r[2] = 0;
	screen_regs.r[6] = 0;
	screen_regs.r[7] = 0;
	_kernel_swi(OS_ScreenMode,&screen_regs,&screen_regs);
	i = malloc(-screen_regs.r[7]);
	if (i == 0)
		return 0;
	screen_regs.r[2] = 0;
	screen_regs.r[6] = (int) i;
	screen_regs.r[7] = -screen_regs.r[7];
	_kernel_swi(OS_ScreenMode,&screen_regs,&screen_regs);
	*l = -screen_regs.r[2];
	return i;
}

#endif
