/**
 * TrueReality - Display/local/amigados/amigados.c
 * Copyright (C) 1998 Mathias "AmiDog" Roslund
 *
 * This program is free software; you can redistribute it and/
 * or modify it under the terms of the GNU General Public Li-
 * cence as published by the Free Software Foundation; either
 * version 2 of the Licence, or any later version.
 *
 * This program is distributed in the hope that it will be use-
 * ful, but WITHOUT ANY WARRANTY; without even the implied war-
 * ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public Licence for more details.
 *
 * You should have received a copy of the GNU General Public
 * Licence along with this program; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
 * USA.
 *
 * Information about me (the author):
 *   Niki W. Waibel, Reichenau 20, 6890 Lustenau, Austria - EUROPE
 *   niki.waibel@gmx.net
**/

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

#include <signal.h>
#include <unistd.h>

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <powerup/ppcproto/intuition.h>
#include <powerup/ppclib/interface.h>
#include <powerup/ppclib/time.h>
#include <powerup/gcclib/powerup_protos.h>
#include <powerup/ppcproto/exec.h>
#include <powerup/ppcinline/graphics.h>
#include <powerup/ppcproto/dos.h>
#include <math.h>

#include "../../../config.h"
#include "../../../parser_extern.h"
#include "../../../n64/type_sizes.h"
#include "../../../n64/memory_extern.h"
#include "../../../dispatch.h"
#include "amigados.h"

#ifdef DEBUG
#  include "../../../debug.h"
#endif

#define twomerges1(a, b, c, d, shift, mask) \
     { \
     temp1 = b; \
     temp2 = d; \
     temp1 >>= shift; \
     temp2 >>= shift; \
     temp1 ^= a; \
     temp2 ^= c; \
     temp1 &= mask; \
     temp2 &= mask; \
     a ^= temp1; \
     c ^= temp2; \
     temp1 <<= shift; \
     temp2 <<= shift; \
     b ^= temp1; \
     d ^= temp2; \
     }


/* Original Variables */
static WORD width=0;    /* initial VI_WIDTH_REG (0x4400008) value */
static WORD height=0;   /* width*3/4 */
static int WIDTH=640;
static int HEIGHT=480;
#undef DEPTH
static int DEPTH;
static WORD flags=0;


/* Local functions */
static int InitDisplay();
static int DestroyDisplay();
static void PrintText(char *text, int x, int y);
static void PutImage(int x, int y, int w, int h);
#ifdef DEBUG_DISPLAY
  static void PrintDisplayMode(WORD flags);
#endif


/* Amiga specific variables */
int _width_, _height_, _depth_;
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct BitMap agabitmap; 
struct Screen *screen;
struct Window *window;
amigaprefstype amigaprefs;
int img_bytes_per_line;
unsigned char *img_data;
unsigned char *ham_buf;
unsigned char color6bit[64];


int load_prefs()
{
  FILE *file;
  char str[50];
  int i, width, height;
  unsigned long dispid;

  /* Default settings */
  amigaprefs.type=AGA;
  amigaprefs.dispidlo=0x00021000;
  amigaprefs.dispidhi=0x00029004;
  amigaprefs.mouse=0;

  /* Open file for reading */
  file=fopen("TrueReality.cfg", "r");

  if (file!=NULL)
  {

    if (fscanf(file, "%s", str)!=EOF)
    {
      if (strcmp(str, "AGA")==0)
        amigaprefs.type=AGA;

      if (strcmp(str, "GFX")==0)
        amigaprefs.type=GFX;

      if (strcmp(str, "HAM8")==0)
        amigaprefs.type=AGAHAM;
    }

    for (i=0; i<2; i++)
    {
      if (fscanf(file, "%d %d %lx", &width, &height, &dispid)!=EOF)
      {
        if (width==320 && height==240)
          amigaprefs.dispidlo=dispid;
        else if (width==640 && height==480)
          amigaprefs.dispidhi=dispid;
      }
    }

    if (fscanf(file, "%s", str)!=EOF)
    {
      if (strcmp(str, "MOUSE")==0)
        amigaprefs.mouse=1;
    }

    /* Close the file */
    fclose(file);
  }
  else
  {
    printf("Could not open TrueReality.cfg!\n");
    return -1;
  }

  if (amigaprefs.type==AGA)
    _depth_=6;
  else
    _depth_=8;

  if (amigaprefs.type==AGAHAM)
  {
    /* Set HAM bit */
    amigaprefs.dispidlo|=0x800;
    amigaprefs.dispidhi|=0x800;
  }

  return 0;
}


void checkinput(void) /* Check if Left Mouse Button are being pressed */
{
  unsigned char *lmb; /* Creat pointer to OS register */

  lmb=(unsigned char*)0xbfe001; /* Set the pointer to the right register */

  if ((*lmb & 0x40)==0) /* Check if LMB is being pressed */
    dispatch(SHUTDOWN, 0);
}


void set_color(int number, int red, int green, int blue)
{
  unsigned long lred=0, lgreen=0, lblue=0;
  
  lred+=red;
  lred=lred & 0x000000ff;
  lred=lred << 24;

  lgreen+=green;
  lgreen=lgreen & 0x000000ff;
  lgreen=lgreen << 24;
  
  lblue+=blue;
  lblue=lblue & 0x000000ff;
  lblue=lblue << 24;

  SetRGB32(&screen->ViewPort, number, lred, lgreen, lblue);
}


int open_libraries()
{
  IntuitionBase = (struct IntuitionBase *)
    OpenLibrary( "intuition.library", 0 );

  if( IntuitionBase == NULL )
    return -1;

  GfxBase = (struct GfxBase *)
    OpenLibrary( "graphics.library", 0);

  if( GfxBase == NULL )
  {
    CloseLibrary( IntuitionBase );
    return -1;
  }

  return 0;
}


void close_libraries()
{
  CloseLibrary(GfxBase);
  CloseLibrary(IntuitionBase);
}


void close_screen()
{
  CloseWindow(window);
  CloseScreen(screen);
  if (amigaprefs.type==AGA || amigaprefs.type==AGAHAM)
    FreeRaster(agabitmap.Planes[0], _width_, _height_*_depth_);
}


int open_screen(int width, int height)
{
  int dispid, i;
  int red, green, blue;
  unsigned char *temp;

  /* Choose ID */
  if (width==320)
    dispid=amigaprefs.dispidlo;
  else
    dispid=amigaprefs.dispidhi;

  /* Init */
  _width_=width;
  _height_=height;
  img_bytes_per_line=_width_*DEPTH/8;

  /* Screendepth, default 8bit (actually it's 6bit, but TR calls it 8bit)... */
  DEPTH=8;

  if (amigaprefs.type==AGAHAM)
  {
    /* 32bit for HAM8 */
    DEPTH=32;

    /* Allocate HAM8 buffer */
    ham_buf=(unsigned char*)malloc(_width_*_height_);
    memset(ham_buf, 0, _width_*_height_);
  }

  /* Allocate chunky buffer */
  img_data=(unsigned char*)malloc(_width_*_height_*DEPTH/8);
  memset(img_data, 0, _width_*_height_*DEPTH/8);

  if (amigaprefs.type==AGA || amigaprefs.type==AGAHAM)
  {
    /* Init screen resources */
    temp = (unsigned char*)AllocRaster(_width_, _height_*_depth_);
    memset(temp, 0, _width_*_height_*_depth_/8);

    InitBitMap(&agabitmap, _depth_, _width_, _height_); 

    for(i=0; i<_depth_; i++)
      agabitmap.Planes[i] = temp + i * RASSIZE(_width_, _height_);

    /* Open screen */
    screen = OpenScreenTags(NULL,
             SA_BitMap,(int)&agabitmap,
             SA_Width,_width_,
             SA_Height,_height_,
             SA_Depth,_depth_,
             SA_Quiet,TRUE,
             SA_ShowTitle,FALSE,
             SA_Type,CUSTOMSCREEN,
             SA_DisplayID,dispid,
             TAG_DONE);
  }
  else if (amigaprefs.type==GFX)
  {
    /* Open screen */
    screen = OpenScreenTags(NULL,
             SA_Width,_width_,
             SA_Height,_height_,
             SA_Depth,_depth_,
             SA_Quiet,TRUE,
             SA_ShowTitle,FALSE,
             SA_Type,CUSTOMSCREEN,
             SA_DisplayID,dispid,
             TAG_DONE);
  }

  /* Could the screen be opened? */
  if(screen == NULL)
  {
    close_libraries();
    printf("The screen could NOT be opened!\n");
    return -1;
  }

  /* Open window */
  window=OpenWindowTags(NULL,
         WA_CustomScreen,(int)screen,
         WA_Width,_width_,
         WA_Height,_height_,
         WA_IDCMP,IDCMP_MOUSEBUTTONS|IDCMP_RAWKEY|IDCMP_ACTIVEWINDOW|IDCMP_INACTIVEWINDOW,
         WA_RMBTrap,TRUE,
         WA_Backdrop,TRUE,
         WA_Borderless,TRUE,
         WA_Activate,TRUE,
         TAG_DONE);

  /* Could the window be opened? */
  if(window == NULL)
  {    
    CloseScreen(screen);
    if (amigaprefs.type==AGA || amigaprefs.type==AGAHAM)
      FreeRaster(agabitmap.Planes[0], _width_, _height_*_depth_);
    close_libraries();
    printf("The window could NOT be opened!\n");
    return -1;
  }

  /* Set the palette */
  for(i=0; i<64; i++)
  {
    blue  = (i & 0x03) << 6;
    green = (i & 0x0C) << 4;
    red   = (i & 0x30) << 2;

    set_color(i,red,green,blue);
    color6bit[i] = i;
  }

  return 0;
}


void c2p(void *chunky, void *bitplanes, int chunkyx, int chunkyy, int xoffset, int yoffset, int bitplanesize)
{
typedef unsigned long ULONG;

     ULONG     *c;
     ULONG     temp1, temp2;
     ULONG     *p0, *p1, *p2, *p3, *p4, *p5, *p6, *p7;
     ULONG     d0, d1, d2, d3, d4, d5, d6, d7;
     ULONG     t0, t1, t2, t3, t4, t5, t6, t7;
     int  i;

     c = (ULONG *) chunky;
     p0 = (ULONG *) (&(((char *) bitplanes)[yoffset * chunkyx >> 3]));
     p1 = (ULONG *) (&(((char *) p0)[bitplanesize]));
     p2 = (ULONG *) (&(((char *) p1)[bitplanesize]));
     p3 = (ULONG *) (&(((char *) p2)[bitplanesize]));
     p4 = (ULONG *) (&(((char *) p3)[bitplanesize]));
     p5 = (ULONG *) (&(((char *) p4)[bitplanesize]));

     if (_depth_==8)
     {
       p6 = (ULONG *) (&(((char *) p5)[bitplanesize]));
       p7 = (ULONG *) (&(((char *) p6)[bitplanesize]));
     }

     i = chunkyx * chunkyy / (sizeof (ULONG) * 8);

     d0 = *c++;
     d1 = *c++;
     d2 = *c++;
     d3 = *c++;
     d4 = *c++;
     d5 = *c++;
     d6 = *c++;
     d7 = *c++;

     twomerges1(d0, d4, d1, d5, 16, 0x0000ffff);
     twomerges1(d2, d6, d3, d7, 16, 0x0000ffff);

     twomerges1(d0, d2, d1, d3, 8, 0x00ff00ff);
     twomerges1(d4, d6, d5, d7, 8, 0x00ff00ff);

     twomerges1(d0, d1, d2, d3, 4, 0x0f0f0f0f);
     twomerges1(d4, d5, d6, d7, 4, 0x0f0f0f0f);

     twomerges1(d0, d4, d1, d5, 2, 0x33333333);
     twomerges1(d2, d6, d3, d7, 2, 0x33333333);

     twomerges1(d0, d2, d1, d3, 1, 0x55555555);
     twomerges1(d4, d6, d5, d7, 1, 0x55555555);

     t0 = d7;
     t1 = d5;
     t2 = d3;
     t3 = d1;
     t4 = d6;
     t5 = d4;
     t6 = d2;
     t7 = d0;

     while (--i)
     {
          d0 = *c++;
          d1 = *c++;
          d2 = *c++;
          d3 = *c++;
          d4 = *c++;
          d5 = *c++;
          d6 = *c++;
          d7 = *c++;

          *p0++ = t0;

          twomerges1(d0, d4, d1, d5, 16, 0x0000ffff);
          twomerges1(d2, d6, d3, d7, 16, 0x0000ffff);

          *p1++ = t1;

          twomerges1(d0, d2, d1, d3, 8, 0x00ff00ff);
          twomerges1(d4, d6, d5, d7, 8, 0x00ff00ff);

          *p2++ = t2;

          twomerges1(d0, d1, d2, d3, 4, 0x0f0f0f0f);
          twomerges1(d4, d5, d6, d7, 4, 0x0f0f0f0f);

          *p3++ = t3;

          twomerges1(d0, d4, d1, d5, 2, 0x33333333);
          twomerges1(d2, d6, d3, d7, 2, 0x33333333);

          *p4++ = t4;

          twomerges1(d0, d2, d1, d3, 1, 0x55555555);
          twomerges1(d4, d6, d5, d7, 1, 0x55555555);

          *p5++ = t5;

          if (_depth_==8)
          {
            *p6++ = t6;
            *p7++ = t7;
          }

          t0 = d7;
          t1 = d5;
          t2 = d3;
          t3 = d1;
          t4 = d6;
          t5 = d4;
          t6 = d2;
          t7 = d0;
     }

     *p0++ = t0;
     *p1++ = t1;
     *p2++ = t2;
     *p3++ = t3;
     *p4++ = t4;
     *p5++ = t5;

     if (_depth_==8)
     {
     *p6++ = t6;
     *p7++ = t7;
     }
}


/*******************************************************************************
*                                                                              *
* int OpenVisual()                                                             *
* int CloseVisual()                                                            *
*                                                                              *
********************************************************************************
*                                                                              *
* These routines are used by the emulator to open and to close a visual.       *
*                                                                              *
* Both return a negative int on error and zero on success.                     *
*                                                                              *
*******************************************************************************/

int OpenVisual()
{
  if(InitDisplay() < 0)
    exit(20);
  /*  return(-1);  */

  prefs.display = LOCAL_DISPLAY;

  return(0);

} /* int OpenVisual() */

int CloseVisual()
{
  DestroyDisplay();

  prefs.display = NO_DISPLAY;

  return(0);

} /* int CloseVisual() */


/*******************************************************************************
*                                                                              *
* static int InitDisplay()                                                     *
*                                                                              *
********************************************************************************
*                                                                              *
* This routine is used to initialize the visual.                               *
* It should print serious information of the current status.                   *
* It must NOT be called by the emulator. It's called from 'int OpenVisual()'.  *
* The name is not standard but it should be used!                              *
*                                                                              *
* The routine returns a negative int on error and zero on success.             *
*                                                                              *
*******************************************************************************/

static int InitDisplay()
{
  printf("Initializing AmigaOS display:\n");

  printf("Reading AmigaOS prefs ... ");
  if (load_prefs()<0)
    return -1;
  else
    printf("OK\n");

  printf("Opening libraries ... ");
  if (open_libraries()<0)
    return -1;
  else
    printf("OK\n");

  printf("Opening screen ... ");
  if (open_screen(640,480)<0)
    return -1;
  else
    printf("OK\n");

  printf("-------------------------------------------------------------------------------\n");

  RefreshVisual();

  return(0);

} /* static int InitDisplay() */


/*******************************************************************************
*                                                                              *
* static int DestroyDisplay()                                                  *
*                                                                              *
********************************************************************************
*                                                                              *
* This routine is used to destroy the visual.                                  *
* It should print serious information of the current status.                   *
* It must NOT be called by the emulator. It's called from 'int CloseVisual()'. *
* The name is not standard but it should be used!                              *
*                                                                              *
* The routine returns a negative int on error and zero on success.             *
*                                                                              *
*******************************************************************************/

static int DestroyDisplay()
{
  printf("-------------------------------------------------------------------------------\n");
  printf("Destroying AmigaOS display:\n");

  printf("Closing screen ... ");
  close_screen();
  printf("OK\n");

  printf("Closing libraries ... ");
  close_libraries();
  printf("OK\n");

  printf("-------------------------------------------------------------------------------\n");

  return(0);

} /* static int DestroyDisplay() */


static void PrintText(char *text, int x, int y)
{
  /* Should print to the window/screen actually, might be added later (AmiDog) */

  printf("%s\n", text);

} /* static void PrintText(char *text, int x, int y) */


#ifdef DEBUG_DISPLAY

static void PrintDisplayMode(WORD flags)
{
        printf("Displaymode:\n");
   

   
        switch(flags & 0x0003)
        {
            case 0:
                printf("   blank (no data, no sync)\n");
                break;

            case 1:
                printf("   reserved\n");
                break;

            case 2:
                printf("   5/5/5/3 (\"16\" bit)\n");
                break;
         
            case 3:
                printf("   8/8/8/8 (32 bit)\n");
                break;
         
        } /* switch(flags & 0x0003) */
   

   
        if(flags & 0x0004)
                printf("   gamma_dither_enable\n");
        if(flags & 0x0008)
                printf("   gamma_enable\n");
        if(flags & 0x0010)
                printf("   divot_enable\n");
        if(flags & 0x0040)
                printf("   serrate\n");
   

   
        printf("   anti-alias (aa) mode\n");
   
        switch((flags & 0x0300) >> 8)
        {
            case 0:
                printf("      aa & resamp (always fetch extra lines)\n");
                break;

            case 1:
                printf("      aa & resamp (fetch extra lines if needed)\n");
                break;

            case 2:
                printf("      resamp only (treat as all fully covered)\n");
                break;
         
            case 3:
                printf("      neither (replicate pixels, no interpolate)\n");
                break;
         
        } /* switch((flags & 0x0300) >> 8) */
   
} /* static void PrintDisplayMode(WORD flags) */

#endif


void RefreshVisual()
{
        WORD    new_width;
        int     i, j, k, l;
        BYTE    *Imsrc1, *Imdst1, c;
        HWORD   *Imsrc2, *Imdst2, ui;
        WORD    *Imsrc4, *Imdst4, ul;


   /* Quite with LMB */
   checkinput();
   
           /* video flags */
  
        flags = ((WORD *)mem.vi_reg)[0];

#ifdef DEBUG_DISPLAY
        if(prefs.debug & DBG_DISP_REFRESH)
                puts("DBG_DISP_REFRESH: refresh");
        if(prefs.debug & DBG_DISP_MODE)
                PrintDisplayMode(flags);
#endif
  
        /* video width */
        /* if width in VI_WIDTH_REG (0x04400008) has changed set windows width */
   
        new_width = ((WORD *)mem.vi_reg)[2];
        if(new_width != width)
        {
                if(new_width < 320 || new_width > 640)
                        width = 640;
                else
                        width = new_width;

                height = (width * 3) >> 2;

               /* Change screensize (AmiDog) */
               close_screen();
               open_screen(width, height);
               printf("AmigaOS: screensize changed to: %lu, %lu\n", width, height);
        }

        /* display it! */

        if(new_width >= 320 && new_width <= 640)
        {
                if(flags & 0x0003)
                {                       
                         /* Compare screensize with WIDTH/HEIGHT */
                         if(WIDTH != _width_ || HEIGHT != _height_)
                        {

                                WIDTH  = _width_;
                                HEIGHT = _height_;

                        } /* if(WIDTH != _width_ || HEIGHT != _height_) */

                        /* source video origin */
                        Imsrc4 = (WORD  *) (mem.rd_ram + ((WORD *)mem.vi_reg)[1]);
                        Imsrc2 = (HWORD *) Imsrc4;
                        Imsrc1 = (BYTE  *) Imsrc2;
         
                        /* destination video origin */
                        Imdst4 = (WORD  *) img_data;
                        Imdst2 = (HWORD *) Imdst4;
                        Imdst1 = (BYTE  *) Imdst2;

/* everything in the following switch comes from:  */
/* Guenther Sohler <guenther.sohler@newlogic.at>   */
/* HEY, GREAT WORK! (niki.waibel@gmx.net)          */
                        switch(DEPTH)
                        {
                            case 8:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 2/2/2 (8bit - 6bit used)  n64: 5/5/5/3 (16bit) */
                                        j=0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                /* 
                                                                ** The following was modified by me
                                                                ** (Niki W. Waibel) to support correct colors
                                                                ** on big and little endians.
                                                                */
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];                                                                                                                              
#endif
                                                                c  = color6bit[((ui & 0x30) >> 4) | ((ui & 0x600) >> 7) | ((ui & 0xc000) >> 10)];
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst1[j*img_bytes_per_line+l] = c;
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 2/2/2 (8bit - 6bit used)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                c  = color6bit[((ul & 0xc0) >> 6) | ((ul & 0xc000) >> 12) | (( ul & 0xc00000) >> 18)];
                                                                for(; l*width<k*WIDTH;l++)
                                                                        Imdst1[j*img_bytes_per_line+l] = c;
                                                        }
                                                }
                                        break;

                                } /* switch(flags & 0x0003) */
                                break;
            
                            case 16:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 5/6/5 (16bit)   n64: 5/5/5/3 (16bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                /* 
                                                                ** The following was modified by me
                                                                ** (Niki W. Waibel) to support correct colors
                                                                ** on big and little endians.
                                                                */
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];
#endif
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img_bytes_per_line/2+l] = ((ui >> 1) & 0x1f) + (ui & 0xffc0);
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 5/6/5 (16bit)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                ui = ((ul & 0xF8) >> 3) | ((ul & 0xFC00) >> 5) | ((ul & 0xF80000) >> 8);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img_bytes_per_line/2+l] = ui;
                                                        }
                                                }
                                                break;

                                } /* switch(flags & 0x0003) */
                                break;

                        
                            /* 
                            ** The following 15/24/32bit stuff was hacked out by myself
                            ** (Niki W. Waibel) using the above (8/16bit) from
                            ** Guenther Sohler as a bases.
                            */
                            case 15:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 5/5/5 (15bit)   n64: 5/5/5/3 (16bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];
#endif
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img_bytes_per_line/2+l] = ui >> 1;
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 5/5/5 (15bit)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                ui = ((ul & 0xF8) >> 3) | ((ul & 0xFC00) >> 5) | ((ul & 0xF80000) >> 8);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img_bytes_per_line/2+l] = ui;
                                                        }
                                                }
                                                break;

                                } /* switch(flags & 0x0003) */
                                break;

                            case 24:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 8/8/8 (24bit)   n64: 5/5/5/3 (16bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];
#endif
                                                                for(; l*width<k*WIDTH; l++)
                                                                {
#ifdef ENDIAN_IS_LITTLE
                                                                        Imdst1[j*img_bytes_per_line+l*3+0] = (ui << 2) & 0xf8;
                                                                        Imdst1[j*img_bytes_per_line+l*3+2] = (ui >> 8) & 0xf8;
#endif
#ifdef ENDIAN_IS_BIG
                                                                        Imdst1[j*img_bytes_per_line+l*3+2] = (ui << 2) & 0xf8;
                                                                        Imdst1[j*img_bytes_per_line+l*3+0] = (ui >> 8) & 0xf8;
#endif
                                                                        Imdst1[j*img_bytes_per_line+l*3+1] = (ui >> 3) & 0xf8;
                                                                }
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 8/8/8 (24bit)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                ui = ((ul & 0xF8) >> 3) | ((ul & 0xFC00) >> 5) | ((ul & 0xF80000) >> 8);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img_bytes_per_line/2+l] = ui;
                                                        }
                                                }
                                                break;

                                } /* switch(flags & 0x0003) */
                                break;

                            case 32:
                                switch(flags & 0x0003)
                                {
                                    case 0:
                                    case 1:
                                        break;

                                    case 2:   /* local: 8/8/8 (32bit)   n64: 5/5/5/3 (16bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
#ifdef ENDIAN_IS_LITTLE
                                                                ui = Imsrc2[(i*width+k)^0x01];
#endif
#ifdef ENDIAN_IS_BIG
                                                                ui = Imsrc2[i*width+k];
#endif
                                                                for(; l*width<k*WIDTH; l++)
                                                                {
#ifdef ENDIAN_IS_LITTLE
                                                                        Imdst1[j*img_bytes_per_line+l*4+0] = (ui << 2) & 0xf8;
                                                                        Imdst1[j*img_bytes_per_line+l*4+2] = (ui >> 8) & 0xf8;
#endif
#ifdef ENDIAN_IS_BIG
                                                                        Imdst1[j*img_bytes_per_line+l*4+2] = (ui << 2) & 0xf8;
                                                                        Imdst1[j*img_bytes_per_line+l*4+0] = (ui >> 8) & 0xf8;
#endif
                                                                        Imdst1[j*img_bytes_per_line+l*4+1] = (ui >> 3) & 0xf8;
                                                                }
                                                        }
                                                }
                                        break;

                                    case 3:   /* local: 8/8/8 (32bit)   n64: 8/8/8/8 (32bit) */
                                        j = 0;
                                        for(i=0; i<height; i++)
                                                for(; j*height<i*HEIGHT; j++)
                                                {
                                                        l = 0;
                                                        for(k=0; k<width; k++)
                                                        {
                                                                ul = Imsrc4[i*width+k];
                                                                ui = ((ul & 0xF8) >> 3) | ((ul & 0xFC00) >> 5) | ((ul & 0xF80000) >> 8);
                                                                for(; l*width<k*WIDTH; l++)
                                                                        Imdst2[j*img_bytes_per_line/2+l] = ui;
                                                        }
                                                }
                                                break;

                                } /* switch(flags & 0x0003) */
                                break;

                            default:
                                fprintf(stderr, "Display depth of %d bit is not supported! (yet ;) )\n", DEPTH);
                                
                        } /* switch(DEPTH) */
         
                        PutImage(0, 0, WIDTH, HEIGHT);
                }
                else
                {
                        PrintText("DISPLAY: blank (no data, no sync)", 20, 20);
                }
        }
        else
        {
                if(!(flags & 0x0003))
                        PrintText("DISPLAY: blank (no data, no sync)", 20, 20);
         
                PrintText("DISPLAY: wrong width (640 will be used!)", 20, 40);
        }
   
} /* void RefreshVisual() */


void GetVisualName(char *name, int *n) /* Ok, 981222 */
{
  strncpy(name, DISPLAYNAME, (size_t) *n);

  *n = strlen(DISPLAYNAME);

} /* void GetVisualName() */


WORD CheckVisual()
{
  static HWORD keystate = 0;
  static sBYTE stick_x = 0;
  static sBYTE stick_y = 0;
  struct IntuiMessage *imsg = NULL;
  ULONG imCode,imClass;
  int x, y;

     /* Check for IDCMP messages */
     if((imsg=(struct IntuiMessage *)GetMsg(window->UserPort)))
     {
        imClass=imsg->Class;
        imCode=imsg->Code;
        x=imsg->MouseX;
        y=imsg->MouseY;
        ReplyMsg((struct Message *)imsg);

        /* FocusChange */

        if(imClass==IDCMP_ACTIVEWINDOW)
        {
          puts("   ***   F O C U S   I N   ***");
        }
        else if (imClass==IDCMP_INACTIVEWINDOW)
        {
          puts("   ***   F O C U S   O U T   ***");
        }

        /* ButtonPress / ButtonRelease */ 

        if(imClass==IDCMP_MOUSEBUTTONS)
        {
        }

        /* KeyPress and KeyReleases */

        if(imClass==IDCMP_RAWKEY)
        {
                if(imCode<128) /* Key is being pressed */
                {
                        switch(imCode)
                        {
                            case 69: /* ESC */
                                dispatch(SHUTDOWN, 0);

                            case 87: /* F8, Refresh Visual */
                                RefreshVisual();
                                break;

                            case 16: /* Q, Start Button */
                                keystate |= 0x1000;
                                break;

                            case 17: /* W, A-Button */
                                keystate |= 0x8000;
                                break;

                            case 18: /* E, B-Button */
                                keystate |= 0x4000;
                                break;

                            case 62: /* 8 (num), C-Button UP */
                                keystate |= 0x0008;
                                break;

                            case 30: /* 2 (num), C-Button DOWN */
                                keystate |= 0x0004;
                                break;

                            case 45: /* 4 (num), C-Button LEFT */
                                keystate |= 0x0002;
                                break;

                            case 47: /* 6 (num), C-Button RIGHT */
                                keystate |= 0x0001;
                                break;

                            case 76: /* UP (arrow), D-Button UP */
                                keystate |= 0x0800;
                                break;

                            case 77: /* DOWN (arrow), D-Button DOWN */
                                keystate |= 0x0400;
                                break;

                            case 79: /* LEFT (arrow), D-Button LEFT */
                                keystate |= 0x0200;
                                break;

                            case 78: /* RIGTH (arrow), D-Button RIGHT */
                                keystate |= 0x0100;
                                break;

                            case 32: /* A, R-Button */
                                keystate |= 0x0010;
                                break;

                            case 33: /* S, L-Button */
                                keystate |= 0x0020;
                                break;

                            case 34: /* D, Z-Button */
                                keystate |= 0x2000;
                                break;

                            case 39: /* K, Stick Up */
                                if(stick_y < 120)
                                        stick_y += 0x08;
                                break;

                            case 38: /* J, Stick Down */
                                if(stick_y > -120)
                                        stick_y -= 0x08;
                                break;

                            case 40: /* L, Stick Right */
                                if(stick_x < 120)
                                        stick_x += 0x08;
                                break;

                            case 37: /* H, Stick Left */
                                if(stick_x > -120)
                                        stick_x -= 0x08;
                                break;

                        } /* switch(imCode) */
                }
                else /* (imCode>127), Key released */
                {
                imCode-=128; /* Remove "Key released" bit */
                        switch(imCode)
                        {
                            case 16: /* Q, Start Button */
                                keystate &= ~0x1000;
                                break;

                            case 17: /* W, A-Button */
                                keystate &= ~0x8000;
                                break;

                            case 18: /* E, B-Button */
                                keystate &= ~0x4000;
                                break;

                            case 62: /* 8 (num), C-Button UP */
                                keystate &= ~0x0008;
                                break;

                            case 30: /* 2 (num), C-Button DOWN */
                                keystate &= ~0x0004;
                                break;

                            case 45: /* 4 (num), C-Button LEFT */
                                keystate &= ~0x0002;
                                break;

                            case 47: /* 6 (num), C-Button RIGHT */
                                keystate &= ~0x0001;
                                break;

                            case 76: /* UP (arrow), D-Button UP */
                                keystate &= ~0x0800;
                                break;

                            case 77: /* DOWN (arrow), D-Button DOWN */
                                keystate &= ~0x0400;
                                break;

                            case 79: /* LEFT (arrow), D-Button LEFT */
                                keystate &= ~0x0200;
                                break;

                            case 78: /* RIGHT (arrow), D-Button RIGHT */
                                keystate &= ~0x0100;
                                break;

                            case 32: /* A, R-Button */
                                keystate &= ~0x0010;
                                break;

                            case 33: /* S, L-Button */
                                keystate &= ~0x0020;
                                break;

                            case 34: /* D, Z-Button */
                                keystate &= ~0x2000;
                                break;

                        } /* switch(imCode) */

                } /* (imCode>127), Key released */

        } /* imClass=IDCMP_RAWKEY */

     } /* Check for IDCMP messages */

#ifdef DEBUG_CONTROLLER
        if(prefs.debug & DBG_CONTR_BUTTONS)
                printf
                (
                        "Controller: "
                        "0x%04hx %+4d %+4d (0x%08lx) (0xBBBBXXYY) "
                        "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",

                        keystate, stick_x, stick_y,
                        (WORD)(keystate << 16) | ((WORD)(HWORD)(stick_x << 8)) | ((WORD)(BYTE)stick_y),
                        (keystate & 0x8000) ? "A "      : "",
                        (keystate & 0x4000) ? "B "      : "",
                        (keystate & 0x2000) ? "Z "      : "",
                        (keystate & 0x1000) ? "START "  : "",
                        (keystate & 0x0800) ? "Dup "    : "",
                        (keystate & 0x0400) ? "Ddown "  : "",
                        (keystate & 0x0200) ? "Dleft "  : "",
                        (keystate & 0x0100) ? "Dright " : "",
                        (keystate & 0x0080) ? ""        : "",
                        (keystate & 0x0040) ? ""        : "",
                        (keystate & 0x0020) ? "L "      : "",
                        (keystate & 0x0010) ? "R "      : "",
                        (keystate & 0x0008) ? "Cup "    : "",
                        (keystate & 0x0004) ? "Cdown "  : "",
                        (keystate & 0x0002) ? "Cleft "  : "",
                        (keystate & 0x0001) ? "Cright " : "" 
                );
#endif

  return(((WORD)(keystate << 16)) | ((WORD)(HWORD)(stick_x << 8)) | ((WORD)(BYTE)stick_y));

} /* WORD CheckVisual() */


static void PutImage(int X, int Y, int W, int H)
{
  unsigned char *dst, *src;
  int r, g, b, r_old, g_old, b_old, rdiff, gdiff, bdiff, x, y;

  /* Perform a fullscreen c2p */
  if (amigaprefs.type==AGA)
    c2p((void *)img_data, (void *)agabitmap.Planes[0], W, H, 0, 0, RASSIZE(_width_, _height_));
  else if (amigaprefs.type==GFX)
    WritePixelArray8(&screen->RastPort, 0, 0, W-1, H-1, img_data, &screen->RastPort);
  else if (amigaprefs.type==AGAHAM)
  {
    src=(unsigned char *)img_data; dst=(unsigned char *)ham_buf;

    /* 32bit -> HAM8, uses 1*1 pixels and NO prescan (=UGLY) */
    for (y=0; y<_height_; y++)
    {
      r_old=0;
      g_old=0;
      b_old=0;

      for (x=0; x<_width_; x++)
      {
        r=*src++;
        g=*src++;
        b=*src++;
        src++;  

        rdiff=abs(r-r_old);
        gdiff=abs(g-g_old);
        bdiff=abs(b-b_old);

        if (rdiff>=gdiff && rdiff>=bdiff)
        {
          *dst++=(r>>2)|0x80;
          r_old=r;
        }
        else if (gdiff>=rdiff && gdiff>=bdiff)
        {
          *dst++=(g>>2)|0xc0;
          g_old=g;
        }
        else
        {
          *dst++=(b>>2)|0x40;
          b_old=b;
        }
      }
    }

    /* Perform 8bit C2P */
    c2p((void *)ham_buf, (void *)agabitmap.Planes[0], W, H, 0, 0, RASSIZE(_width_, _height_));
  }
} /* static void PutImage(int X, int Y, int W, int H) */
