/*****************************************************************************
*
*                                  ATARI.C
*				  
*   from DKBTrace (c) 1990  David Buck
*
*  This module implements the IBM-specific routines for DKBTrace.
*
* This software is freely distributable. The source and/or object code may be
* copied or uploaded to communications services so long as this notice remains
* at the top of each file.  If any changes are made to the program, you must
* clearly indicate in the documentation and in the programs startup message
* who it was who made the changes. The documentation should also describe what
* those changes were. This software may not be included in whole or in
* part into any commercial package without the express written consent of the
* author.  It may, however, be included in other public domain or freely
* distributed software so long as the proper credit for the software is given.
*
* This software is provided as is without any guarantees or warranty. Although
* the author has attempted to find and correct any bugs in the software, he
* is not responsible for any damage caused by the use of the software.  The
* author is under no obligation to provide service, corrections, or upgrades
* to this package.
*
* Despite all the legal stuff above, if you do find bugs, I would like to hear
* about them.  Also, if you have any comments or questions, you may contact me
* at the following address:
*
*     David Buck
*     22C Sonnet Cres.
*     Nepean Ontario
*     Canada, K2H 8W7
*
*  I can also be reached on the following bulleton boards:
*
*     ATX              (613) 526-4141
*     OMX              (613) 731-3419
*     Mystic           (613) 731-0088 or (613) 731-6698
*
*  Fidonet:   1:163/109.9
*  Internet:  David_Buck@Carleton.CA
*  The "You Can Call Me RAY" BBS    (708) 358-5611
*
*  IBM Port by Aaron A. Collins. Aaron may be reached on the following BBS'es:
*
*     The "You Can Call Me RAY" BBS (708) 358-5611
*     The Information Exchange BBS  (708) 945-5575
*
****************************************************************************
*
* Adaptation au ST/TT par                                                    
*
*   Gemini TT pour la r‚cup‚ration des sources et la premiere adaptation     
*   Gemini TT + RedRackam pour l'adaptation au Lattice C et au Turbo C    
*     ( detection et correction des bugs de la bibliotheque math‚matique du
*     coprocesseur 68882 )
*   RedRackam pour le trac‚ en palette fixe et l'adaptation au Pure C
*   RedRackam + Oulan Bator ( utilisation de sa routine de calcul de palette
*     variable )  pour le trac‚ en palette variable optimis‚ et les options
*     d1,d2 et d3
*
*   version Atari ST/TT  1.26 du 17/04/1992	
*
***************************************************************************/


/* Original IBM VGA "colour" output routines for MS/DOS by Aaron A. Collins.

   This will deliver approximate colorings using HSV values for the selection.
   The pallette map is divided into 4 parts - upper and lower half generated
   with full and half "value" (intensity), respectively.  These halves are
   further halved by full and half saturation values of each range (pastels).
   There are three constant colors, black, white, and grey.  They are used
   when the saturation is low enough that the hue becomes undefined, and which
   one is selected is based on a simple range map of "value".  Usage of the
   pallette is accomplished by converting the requested color RGB into an HSV
   value.  If the saturation is too low (< .25) then black, white or grey is
   selected.  If there is enough saturation to consider looking at the hue,
   then the hue range of 1-63 is scaled into one of the 4 pallette quadrants
   based on its "value" and "saturation" characteristics.

   Further SVGA, MVGA mods by Aaron A. Collins:
   SVGA, MVGA assembler routines originally by John Bridges.
   VESA assembler routines from FRACTINT, by The Stone Soup Group
   AT&T VDC600 SVGA mods to DKB Trace 2.01 module IBM.C by John Gooding

   This file now represents the collective wisdom of the VGAKIT34 package,
   with support for all of the SVGA types known to mankind.  Seriously,
   VGAKIT34 is an excellent technical reference for IBM programmers wishing
   to do any sort of SVGA video access, and it encompasses nearly all of the
   SVGA adapters in use today.  It was written by John Bridges, a.k.a.
   CIS:73307,606, GENIE:J.BRIDGES.  It was originally coded in IBM 80x86
   assembler, and since DKBTrace is a completely "C"-based package, I have
   converted John's assembler routines all into "C".  These may be a tad bit
   slower, but they should be compatible across a wide variety of 80x86/(S)VGA
   machines.  Note if you have a regular cheapo VGA card like myself, included
   is "MODE13x" or MVGA (modified VGA) mode (some call it "tweaked", but I
   call it "Simulated SVGA"), which gives 360x480 on any reasonably register-
   compatible plain vanilla VGA card.  This mode gives a good simulated 640 by
   480 screen resolution.  I did not implement all the neat hi-res modes of
   all the various SVGA adapters, if you select a trace size bigger than the
   program and/or card can handle (most likely 640x480), it is dynamically
   scaled to fit the available resolution, so you'll be able to see a rough
   approximation of an 800x600 trace even on any el-cheapo VGA card at 320x200
   resolution.  The VESA VGA mode was freely adapted from FRACTINT, whose GIF
   reading routines we are already using in DKBTrace.  I hope my conversion
   of it works properly.

   There is still a reported problem with the EVEREX autodetect returning
   TRIDENT.  In fact EVEREX uses a TRIDENT chip set, but apparently there
   is some difference in operation.  There are cryptic diagnostic messages
   such as T0000, etc. printed as a result of the autodetection routines
   to help track down why the error is happening.  If you are experiencing
   problems with EVEREX or TRIDENT, make note of the letter-4 digit code you
   are given.  There is now an autodetect for VDC600 that I hope will work
   universally.  A similar problem as the EVEREX exists, in that the VDC600
   is detected as a PARADISE because it uses the PARADISE chip set.  I am now
   looking for what I believe to be the model number in the BIOS ROM of the
   VDC600 to differentiate between the two.  I hope this works  with all
   VDC600's, as I only had one example to work from.  Please send all bug
   reports to Aaron Collins at the "You Can Call Me RAY" BBS, the number is
   above in the header of this and the other DKB source files.
*/
#define _PURE_C_
#define DBL double
#define PROMPTEXIT 8
#define ANTIALIAS 16
#define DEBUGGING 32
#define RGBSEPARATE 64
#define EXITENABLE 128
#define CONTINUE_TRACE 256
#define VERBOSE_FILE 512
#define ON_ERROR_WAIT_KEYBD_TO_QUIT 1024

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <aes.h>
#include <vdi.h>
#include <string.h>
#ifdef __TOS__
#include <ext.h>
#include <tos.h>
#else
#include <dos.h>
#include <conio.h>
#endif

#ifndef VGA_FALCON
	#include <mode.h>
#endif	
/* avec la nouvelle version de PURE C (1.1), mode.h est inclus dans
TOS.H et il n'est donc pas necessaire de definir	*/
/* a l'exeption de Vsetscreen (extension de setscreen)	*/
/* que je definis donc ici de toute facon	*/

#define VSetscreen(a,b,c,d) (int)xbios(5,a,b,c,d)
extern unsigned int options;
extern char DisplayFormat;

/*
#ifdef __TOS__
#define LineStyles LStyles
#define FillStyles FStyles
#endif
*/

#include <time.h>

/* definition du type de l'‚cran	*/
typedef enum 
{	FALCON=1,
	MATRIX=2,
	AUTRES=0
}	t_ecran;	


t_ecran ecran=AUTRES;	
void *inter;		/* zone reservee pour stocker l'image	*/
int		dim_x,dim_y;		/* dimension en x et y de l'image totale 		*/
int lastx=-1, lasty, lastline;	/* Pixel / Line Caches */

int   contrl[12];   /* vdi parameter arrays */
int   intin[256];
int   intout[128];
int   ptsin[256];
int   ptsout[128];
int   work_in[11];    /* Input to GSX parameter array		*/
int   work_out[57];   /* Output from GSX parameter array	*/
int   phys_handle,handle,menu_acc,vdi_handle;
int   buffer[8];
size_t la_taille_ecran;
int mode_in;
int rez_in;
int c_y;	/* coordonnees en y ligne calculee	*/
int ap_id;
int ap_id_dest;	/* l'identification du programme destination	*/
char cookie_char[5];
FILE *res_abs;
void *adr_ecran;
 

typedef struct
{
	int used;
	int x;
	int y;
}	coul_struct;

int nb_cabs=0;
int modecode;
int old_mode;
void *pb,*lb;

long table_couleur[4096];	/* table dans laquelle est stock‚ le nombre
							d'occurence de chaque couleur			*/
int *ptr_ecran;		/* adresse ou dkb range ses images en nb bits	*/
int *ptr_l1e;
unsigned long *ptr_matrix;
int len_ligne;
void *inter;		/* zone reservee pour stocker l'image	*/
int		dim_x,dim_y;		/* dimension en x et y de l'image totale 		*/

int	 nb_plan;			/* nombre de plan de bits	*/
int	nb_bit_coul;		/* calcul du nombre de bits par couleur	*/
int dec_droite;			/* nombre de decalage vers la droite a effectuer */
char masque;			/* masque a appliquer sur chaque couleur pour ne garder que les bits
		   				significatifs dans le choix de couleur courant	*/
int dec_red;	/* decalage a gauche pour le rouge	*/
int dec_green;	/* decalage a gauche de la composante verte	*/

double AspectRatio;             /* Aspect ratio of a pixel on the screen*/
int    MaxX, MaxY;              /* The maximum resolution of the screen */
int    MaxColors;               /* The maximum # of colors available    */
int    ErrorCode;               /* Reports any graphics errors          */

int falcon;
extern unsigned int Options;
extern char DisplayFormat;


/* prootype des fonction affichage appelees	*/
void affi_image(void);
void open_vwork2(void);
void affi_final(void);
void display_finished2(void);

/* ces deux routines sont necessaires pour un fonctionnement correct
 sous PC et latice C			*/

/*--------------------------------------------------------
	PROCEDURE 	: get_cookie1
	AUTEUR		: M. Abramson
	DATE		: 27/4/1992
	DESCRIPTION: cette procedure renvoie le long mot associe au cookie
				pass‚ en parametre 1, et 0 si il n'y en a pas
	PARAMETRES:	
			le mot a rechercher est dans la variable cookie_char
	RETURN	:
			long	: long mot associe au cookie
		MODIFICATIONS:
		AUTEUR	:
		DATE	:
		DESCRIPTION:
--------------------------------------------------------*/
long get_cookie1(void)
{
	const void ** ptr_cookie=(void **)0x5a0;	/* adresse de base du cookie	*/
	char *adr_first_cookie;
	char chaine_comp[5];
	char *cookie_ident;
	long *cookie_val;
	int nb_cookie=0;
	adr_first_cookie=*ptr_cookie;		/* recuperer le pointeur sur les cookies	*/
	if (adr_first_cookie==0)
		return(0);
	cookie_ident=(char *)adr_first_cookie;
	cookie_val=(long *)((long)adr_first_cookie+4);
	while ((long *)*cookie_ident!=0)
	
	{
		nb_cookie++;
		sprintf(chaine_comp,"%4s",cookie_ident);
		chaine_comp[4]=0;
		if (strncmp(chaine_comp,cookie_char,4)==0)
			return(*cookie_val);
		cookie_ident+=8;
		cookie_val+=2;
	}	
	return(-1);
}

/*--------------------------------------------------------
	PROCEDURE 	: get_cookie
	AUTEUR		: M. Abramson
	DATE		: 27/4/1992
	DESCRIPTION: cette procedure renvoie le long mot associe au cookie
				pass‚ en parametre 1, et 0 si il n'y en a pas
	PARAMETRES:	
		arg1	: chaine a rechercher
	RETURN	:
			long	: long mot associe au cookie
		MODIFICATIONS:
		AUTEUR	:
		DATE	:
		DESCRIPTION:
--------------------------------------------------------*/
long get_cookie(char *a)
{
	sprintf(cookie_char,"%4s",a);
	return(Supexec(get_cookie1));
}


void  Wait_Keyboard_And_Quit(int q)
{
  	if (Options & ON_ERROR_WAIT_KEYBD_TO_QUIT)
	{
		printf ("\007\007");	/* long beep */
			/* attendre que le clavier soit vide	*/
		while (Crawio(0xFF)) Crawcin();
			
		/* attendre une touche clavier */	
		Crawcin();
	}
	exit(q);
} 



void open_vwork(void)
{   int i;
	unsigned int tail_pal;
	long mch;
	int   work_out2[57];   /* Output from GSX parameter array	*/

    for (i=0;i<10;work_in[i++]=1);
        work_in[10]=2;
    v_opnvwk(work_in,&vdi_handle,work_out);
	mch=get_cookie("_MCH");
	if (mch==0x30000L)	
	{
		tail_pal=32767;
		falcon=1;
		ecran=FALCON;
	}
	else
	{
	 	tail_pal=work_out[39];	/* nombre de couleur dans la palette	*/
		vq_extnd(vdi_handle,1,work_out2);
		if (work_out2[5]==0)	/* si pas de look up table	*/
		{
			if (work_out2[4]>=16)		/* et si on a plus de 16 plans  couleur	*/
				ecran=MATRIX;
		}
	}
	if (ecran!=MATRIX)
	{
	  	nb_plan=log(tail_pal+1)/log(2);	/* calcul du nombre de plan de bits	*/
		nb_bit_coul=nb_plan/3;			/* calcul du nombre de bits par couleur	*/
		dec_droite=8-nb_bit_coul;		/* nombre de decalage vers la droite a effectuer
	 								   sur chaque index de couleur pour passer de 24
	 								   bits a x bits	*/
		masque=(0xFF>>dec_droite)<<dec_droite	; /* dans le masque, on ne garde a 1
								que les nb_bits_couleurs les plus a gauche	*/	 								   
		dec_green=nb_bit_coul-dec_droite+falcon;	/* le vert doit etre decale de dec_droite vers
									la droite pour ne garder que la prtie significative
									puis de nb_bit_coul vers la gauche pour aller a gauche du
									bleu. La solution optimale est donc de lui appliquer
									un masque ne gardant que les nb_bit_coul les plus a gauche
									du mot, puis de decaler le mot de nb_bit-dec_droite
									vers la gauche	*/
									
		dec_red=2*nb_bit_coul-dec_droite+falcon;	/* idem, mais a  decaler vers la gauche de 2*nb_bit	*/
								/* sur falcon, on doit decaler le rouge et le vert
								  de 1 bits de plus pour laisser libre					
								  le bit 5 (bit overscan)			*/
	}
}       

void display_close()   /* setup to Text 80x25 (mode 3) */
{
}


void display_plot (x, y, Red, Green, Blue)   /* plot a single RGB pixel */
   int x, y;
   unsigned int Red, Green, Blue;
   {
    unsigned char  coulor;
   unsigned int la_couleur;
  	unsigned long coul_long;

 	lasty = y;		/* save current working line */
 	lastx = x;		/* save most recent pixel done */
 	if (x>=dim_x-1)
  		lastx=-1;

   /* Translate RGB value to best of 256 pallete Colors (by HSV?) */
	if (DisplayFormat!='0')	/* si rendu max	*/
	{	
		/* on calcule la couleur sur le nombre de bits maximum de la palette	*/
		/* 12 sur un STE/TT, 16 (15+1) sur un FALCON	*/
		switch (ecran)
		{
			case FALCON:
				la_couleur=((Red&masque)<<dec_red)+
					((Green&masque)<<dec_green)+
					(Blue>>dec_droite);

				*(ptr_l1e++)=la_couleur;
			break;
			case MATRIX:
				/* la couleur est sur 32 bits soit donc 2 acces */
				/* en 16 bits									*/
				coul_long=((size_t)Red<<16)+(Green<<8)+Blue;
				*(ptr_matrix++)=coul_long;
			break;
			default:
				la_couleur=((Red&masque)<<dec_red)+
					((Green&masque)<<dec_green)+
					(Blue>>dec_droite);
				table_couleur[la_couleur]++;
				*(ptr_ecran++)=la_couleur;
			break;				
		}	
		if (x==dim_x-1)	/* si on est en fin de ligne	*/
		{
			switch (ecran)
			{
				case FALCON:
					ptr_ecran+=len_ligne;
					ptr_l1e=ptr_ecran;
				break;
				case MATRIX:
					ptr_ecran+=len_ligne;
					ptr_matrix=ptr_ecran;
				break;
				default:
					c_y=y;
					affi_image();
				break;
			}
		}
	}
}
   
   
void display_finished ()
{
	FILE *RES;
	int i;
	long pile;
	void *buf_screen;

	if (!falcon)
	{
		affi_final();
	}
	else
	{
		if (DisplayFormat=='1')
		{
			buf_screen=malloc(la_taille_ecran);
			if (buf_screen==0)
			{
				printf("erreur, plus de memoire\n");
				Wait_Keyboard_And_Quit(-1);
			}
			VSetscreen(	buf_screen,
						buf_screen,
						3,
						modecode);
			memcpy(buf_screen,
				adr_ecran,
				la_taille_ecran);
			free(adr_ecran);	/* liberer l'ecran de calcul	*/
			adr_ecran=buf_screen;	/* et pointer sur l'ecran physique	*/
		}
	}
  	if (DisplayFormat>'0')	/* liberer les buffers memoire	*/
	{
	    if (Options & PROMPTEXIT)
		{
			printf ("\007\007");	/* long beep */
				/* attendre que le clavier soit vide	*/
			while (Crawio(0xFF)) Crawcin();
				
			/* attendre une touche clavier */	
			Crawcin();
		}
	}
	if (!falcon)
	{
	  	if (DisplayFormat>'0')	/* liberer les buffers memoire	*/
		{
			free(inter);
		}
	}	
	else
	{
		/* si on avait change le mode, on retourne au mode initial	*/
		if ((DisplayFormat=='1') || (DisplayFormat=='3'))
		{
			VSetscreen(lb,
						pb,
						3,
						old_mode);
		}
		if (adr_ecran)
		{
			free((void *)adr_ecran);
		}
	}
	appl_exit();
	/* fonction qui restaure la palette et liberant la memoire	*/
	if (!falcon)
	{
	/* a supprimer dans la version 2 process					*/
		display_finished2();	
	}	
}



   
/***************************************************************/
   
void display_init(width, height) /* Set video to requested or best mode */
int width, height;
{
	int type_mon;
   unsigned int u;
   int i;
   int xasp, yasp;                       /* Used to read the aspect ratio*/
   unsigned long taille;
   long l, lt;
    char *ptr_buf;
	int big_screen;

#define MASQUE 0x10F		/* pour isoler les bits interessants	*/

/* les modes correspondant … un grand ecran	*/
#define BS1 COL80|VERTFLAG|BPS16	/*16 bits 80 col interlaced	*/
#define BS2 COL40|BPS16			/* 16 bits 40 col non entrelaces	*/
#define BS3 COL80|BPS8			/* 80 colonnes 8 bits non entrelace	*/
	
  open_vwork();
  
  ap_id=appl_init();
  dim_x=width;
  dim_y=height;	
   
   	switch (ecran)
   	{
	case FALCON:
		type_mon=mon_type();
		if (type_mon==0)
		{
			fprintf(stderr,"d‚sol‚, affichage true color impossible\n sur moniteur N&B\n");
			DisplayFormat='0';
		}else
		{
			old_mode=Vsetmode(-1);
			type_mon=old_mode & VGA_FALCON;	
			if (type_mon==VGA_FALCON)
			{
				len_ligne=320;		/* vga	*/
				/* on passe en 16 bits true color tout en conservant l'atat du flag pal	*/
				modecode=BPS16|COL40|VGA_FALCON| (old_mode&112);
			}else
			{
				len_ligne=640;		/* TV	*/
				modecode=BPS16|COL80|TV|(old_mode&112)|VERTFLAG;
			}
		}
		if ((modecode & OVERSCAN)==OVERSCAN)
		{
			len_ligne*=1.2;
		}
		/* on recupere la taille de l'‚cran en fonction du modecode calcul‚	*/
		la_taille_ecran =VgetSize(modecode);
		if (DisplayFormat=='1')	/* affichage	 final	*/
		{
			adr_ecran=calloc(la_taille_ecran,1);
			if (adr_ecran==0)
			{
				fprintf(stderr,"d‚sol‚, plus assez de m‚moire pour calculer une image avec affichage");
				DisplayFormat='0';		
			}
			ptr_l1e=ptr_ecran=adr_ecran;								
		}
		else
		{	
			if (DisplayFormat=='3')	
			{	/* on passe en true color	*/
				/* on recupere les anciennes info ecran	*/
				lb=Logbase();
				pb=Physbase();
				/* et on passe dans le nouveau mode	*/
				/* on etudie l'ancienne config ecran	*/
				switch (old_mode & MASQUE)
				{
					case BS1:
						VSetscreen(lb,pb,3,modecode);
						adr_ecran=0;
						ptr_l1e=ptr_ecran=pb;
					break;
					case BS2:
					case BS3:	/* ancienne config, 80 col 8 bit non entrelace	*/
						if (type_mon==VGA_FALCON)
						{
							VSetscreen(lb,pb,3,modecode);
							adr_ecran=0;
							ptr_l1e=ptr_ecran=pb;
						}
						else
						{
							adr_ecran=calloc(la_taille_ecran,1);
							VSetscreen(adr_ecran,
										adr_ecran,
										3,
										modecode);
							ptr_l1e=ptr_ecran=adr_ecran;
						}	
					break;
					default:
						adr_ecran=calloc(la_taille_ecran,1);
						if (adr_ecran==0)
						{
							fprintf(stderr,"d‚sol‚, plus assez de m‚moire pour calculer une image avec affichage");
							DisplayFormat='0';		
						}
						VSetscreen(adr_ecran,
									adr_ecran,
									3,
									modecode);
						ptr_l1e=ptr_ecran=adr_ecran;				
					break;
				}	/* du test sur le mode courant */
			}	/*du cas display format 3	*/	
		}	/* du test sur le mode overscan	*/
	break;
	case MATRIX:
		lb=Logbase();
		pb=Physbase();
		ptr_matrix=ptr_ecran=pb;
		len_ligne=(work_out[0]+1)*2;
	break;
	default:
		if (DisplayFormat!='0')
		{
			taille=(unsigned long)dim_x*dim_y*2;	/* les donnees sont conservees en memoire en nb_coul (nb<=32768) */
			/*									donc 2 octets par donnees*/
		   	inter=malloc(taille);	/* reserver une zone pour sauver l'image	*/
			ptr_ecran=inter;
		}
		open_vwork2();
	break;
	}
}

#if !__STDC__

/* ANSI Standard psuedo-random number generator */

static unsigned long int next = 1;

/*int rand()
   {
   next = next * 1103515245L + 12345L;
   return ((int) (next / 0x10000L) & 0x7FFF);
   }

void srand(seed)
   unsigned int seed;
   {
   next = seed;
   }
*/
#endif



