/*      $NetBSD: grf_rt.c,v 1.62 2022/05/03 20:52:30 andvar Exp $ */

/*
* Copyright (c) 1993 Markus Wild
* Copyright (c) 1993 Lutz Vieweg
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
*    must display the following acknowledgement:
*      This product includes software developed by Lutz Vieweg.
* 4. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "opt_amigacons.h"

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: grf_rt.c,v 1.62 2022/05/03 20:52:30 andvar Exp $");

#include "grfrt.h"
#include "ite.h"
#if NGRFRT > 0

/* Graphics routines for the Retina board,
  using the NCR 77C22E+ VGA controller. */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/device_impl.h>    /* XXX autoconf abuse */
#include <machine/cpu.h>
#include <amiga/amiga/device.h>
#include <amiga/dev/zbusvar.h>
#include <amiga/dev/grfioctl.h>
#include <amiga/dev/grfvar.h>
#include <amiga/dev/grf_rtreg.h>

int rt_ioctl(struct grf_softc *gp, u_long, void *);

/*
* marked true early so that retina_cnprobe() can tell if we are alive.
*/
int retina_inited;


/*
* This driver for the MacroSystem Retina board was only possible,
* because MacroSystem provided information about the pecularities
* of the board. THANKS! Competition in Europe among gfx board
* manufacturers is rather tough, so Lutz Vieweg, who wrote the
* initial driver, has made an agreement with MS not to document
* the driver source (see also his comment below).
* -> ALL comments after
* -> " -------------- START OF CODE -------------- "
* -> have been added by myself (mw) from studying the publically
* -> available "NCR 77C22E+" Data Manual
*/
/*
* This code offers low-level routines to access the Retina graphics-board
* manufactured by MS MacroSystem GmbH from within NetBSD for the Amiga.
*
* Thanks to MacroSystem for providing me with the necessary information
* to create these routines. The sparse documentation of this code
* results from the agreements between MS and me.
*/

extern unsigned char kernel_font_8x8_width, kernel_font_8x8_height;
extern unsigned char kernel_font_8x8_lo, kernel_font_8x8_hi;
extern unsigned char kernel_font_8x8[];


#define MDF_DBL 1
#define MDF_LACE 2
#define MDF_CLKDIV2 4


/* standard-palette definition */

unsigned char NCRStdPalette[16*3] = {
/*   R   G   B  */
         0,  0,  0,
       192,192,192,
       128,  0,  0,
         0,128,  0,
         0,  0,128,
       128,128,  0,
         0,128,128,
       128,  0,128,
        64, 64, 64, /* the higher 8 colors have more intensity for  */
       255,255,255, /* compatibility with standard attributes       */
       255,  0,  0,
         0,255,  0,
         0,  0,255,
       255,255,  0,
         0,255,255,
       255,  0,255
};


/* The following structures are examples for monitor-definitions. To make one
  of your own, first use "DefineMonitor" and create the 8-bit monitor-mode of
  your dreams. Then save it, and make a structure from the values provided in
  the file DefineMonitor stored - the labels in the comment above the
  structure definition show where to put what value.

  Then you'll need to adapt your monitor-definition to the font you want to
  use. Be FX the width of the font, then the following modifications have to
  be applied to your values:

  HBS = (HBS * 4) / FX
  HSS = (HSS * 4) / FX
  HSE = (HSE * 4) / FX
  HBE = (HBE * 4) / FX
  HT  = (HT  * 4) / FX

  Make sure your maximum width (MW) and height (MH) are even multiples of
  the fonts' width and height.
*/

#if 0
/* horizontal 31.5 kHz */

/*                                      FQ     FLG    MW   MH   HBS HSS HSE HBE  HT  VBS  VSS  VSE  VBE   VT  */
  struct MonDef MON_640_512_60  = { 50000000,  28,  640, 512,   81, 86, 93, 98, 95, 513, 513, 521, 535, 535,
  /* Depth,           PAL, TX,  TY,    XY,FontX, FontY,    FontData,  FLo,  Fhi */
         4, NCRStdPalette, 80,  64,  5120,    8,     8, kernel_font_8x8,   32,  255};

struct MonDef MON_640_480_62_G  = { 50000000,   4,  640, 480,  161,171,184,196,195, 481, 484, 492, 502, 502,
         8, NCRStdPalette,640,480,  5120,    8,     8, kernel_font_8x8,   32,  255};
/* Enter higher values here ^   ^ for panning! */

/* horizontal 38kHz */

  struct MonDef MON_768_600_60  = { 75000000,  28,  768, 600,   97, 99,107,120,117, 601, 615, 625, 638, 638,
         4, NCRStdPalette, 96,  75,  7200,    8,     8, kernel_font_8x8,   32,  255};

/* horizontal 64kHz */

  struct MonDef MON_768_600_80  = { 50000000, 24,  768, 600,   97,104,112,122,119, 601, 606, 616, 628, 628,
         4, NCRStdPalette, 96,  75,  7200,    8,     8, kernel_font_8x8,   32,  255};

  struct MonDef MON_1024_768_80 = { 90000000, 24, 1024, 768,  129,130,141,172,169, 769, 770, 783, 804, 804,
         4, NCRStdPalette,128,  96, 12288,    8,     8, kernel_font_8x8,   32,  255};

/*                                     FQ     FLG    MW   MH   HBS HSS HSE HBE  HT  VBS  VSS  VSE  VBE   VT  */
struct MonDef MON_1024_768_80_G = { 90000000, 0,  1024, 768,  257,258,280,344,343, 769, 770, 783, 804, 804,
         8, NCRStdPalette, 1024, 768, 12288,    8,     8, kernel_font_8x8,   32,  255};

  struct MonDef MON_1024_1024_59= { 90000000, 24, 1024,1024,  129,130,141,173,170,1025,1059,1076,1087,1087,
         4, NCRStdPalette,128, 128, 16384,    8,     8, kernel_font_8x8,   32,  255};

/* WARNING: THE FOLLOWING MONITOR MODES EXCEED THE 90-MHz LIMIT THE PROCESSOR
           HAS BEEN SPECIFIED FOR. USE AT YOUR OWN RISK (AND THINK ABOUT
           MOUNTING SOME COOLING DEVICE AT THE PROCESSOR AND RAMDAC)!     */

  struct MonDef MON_1280_1024_60= {110000000,  24, 1280,1024,  161,162,176,211,208,1025,1026,1043,1073,1073,
         4, NCRStdPalette,160, 128, 20480,    8,     8, kernel_font_8x8,   32,  255};

struct MonDef MON_1280_1024_60_G= {110000000,   0, 1280,1024,  321,322,349,422,421,1025,1026,1043,1073,1073,
         8, NCRStdPalette,1280,1024, 20480,    8,     8, kernel_font_8x8,   32,  255};

/* horizontal 75kHz */

  struct MonDef MON_1280_1024_69= {120000000,  24, 1280,1024,  161,162,175,200,197,1025,1026,1043,1073,1073,
         4, NCRStdPalette,160, 128, 20480,    8,     8, kernel_font_8x8,   32,  255};

#else

struct MonDef monitor_defs[] = {
/* horizontal 31.5 kHz */

  { 50000000,  28,  640, 512,   81, 86, 93, 98, 95, 513, 513, 521, 535, 535,
         4, NCRStdPalette, 80,  64,  5120,    8,     8, kernel_font_8x8,   32,  255},

/* horizontal 38kHz */

  { 75000000,  28,  768, 600,   97, 99,107,120,117, 601, 615, 625, 638, 638,
         4, NCRStdPalette, 96,  75,  7200,    8,     8, kernel_font_8x8,   32,  255},

/* horizontal 64kHz */

  { 50000000, 24,  768, 600,   97,104,112,122,119, 601, 606, 616, 628, 628,
         4, NCRStdPalette, 96,  75,  7200,    8,     8, kernel_font_8x8,   32,  255},

  { 90000000, 24, 1024, 768,  129,130,141,172,169, 769, 770, 783, 804, 804,
         4, NCRStdPalette,128,  96, 12288,    8,     8, kernel_font_8x8,   32,  255},

  /* GFX modes */

/* horizontal 31.5 kHz */

  { 50000000,   4,  640, 480,  161,171,184,196,195, 481, 484, 492, 502, 502,
         8, NCRStdPalette,640, 480,  5120,    8,     8, kernel_font_8x8,   32,  255},

/* horizontal 64kHz */

  { 90000000, 0,  1024, 768,  257,258,280,344,343, 769, 770, 783, 804, 804,
         8, NCRStdPalette, 1024, 768, 12288,    8,     8, kernel_font_8x8,   32,  255},

/* WARNING: THE FOLLOWING MONITOR MODES EXCEED THE 90-MHz LIMIT THE PROCESSOR
           HAS BEEN SPECIFIED FOR. USE AT YOUR OWN RISK (AND THINK ABOUT
           MOUNTING SOME COOLING DEVICE AT THE PROCESSOR AND RAMDAC)!     */

  {110000000,   0, 1280,1024,  321,322,349,422,421,1025,1026,1043,1073,1073,
         8, NCRStdPalette,1280,1024, 20480,    8,     8, kernel_font_8x8,   32,  255},
};

static const char *monitor_descr[] = {
 "80x64 (640x512) 31.5kHz",
 "96x75 (768x600) 38kHz",
 "96x75 (768x600) 64kHz",
 "128x96 (1024x768) 64kHz",

 "GFX (640x480) 31.5kHz",
 "GFX (1024x768) 64kHz",
 "GFX (1280x1024) 64kHz ***EXCEEDS CHIP LIMIT!!!***",
};

int retina_mon_max = sizeof (monitor_defs)/sizeof (monitor_defs[0]);

/* patchable */
int retina_default_mon = 0;
int retina_default_gfx = 4;

#endif


static struct MonDef *current_mon;

/* -------------- START OF CODE -------------- */


static const long FQTab[16] =
{ 25175000,  28322000,  36000000,  65000000,
 44900000,  50000000,  80000000,  75000000,
 56644000,  63000000,  72000000, 130000000,
 90000000, 100000000, 110000000, 120000000 };


/*--------------------------------------------------*/
/*--------------------------------------------------*/

#if 0
static struct MonDef *default_monitor = &DEFAULT_MONDEF;
#endif

int retina_alive(struct MonDef *);
static int rt_load_mon(struct grf_softc *, struct MonDef *);


/*
* used to query the retina to see if its alive (?)
*/
int
retina_alive(struct MonDef *mdp)
{
       short clksel;

       for (clksel = 15; clksel; clksel--) {
               if (FQTab[clksel] == mdp->FQ)
                       break;
       }
       if (clksel < 0)
               return(0);
       if (mdp->DEP != 4)
               return(1);
       if (mdp->FX == 4 || (mdp->FX >= 7 && mdp->FX <= 16))
               return(1);
       return(0);
}

static int
rt_load_mon(struct grf_softc *gp, struct MonDef *md)
{
       struct grfinfo *gi = &gp->g_display;
       volatile void *ba;
       volatile char *fb;
       short FW, clksel, HDE, VDE;

       for (clksel = 15; clksel; clksel--) {
               if (FQTab[clksel] == md->FQ) break;
       }
       if (clksel < 0)
               return(0);

       ba = gp->g_regkva;
       fb = (volatile char*)gp->g_fbkva;

       FW = 0;
       if (md->DEP == 4) {
               switch (md->FX) {
                   case 4:
                       FW = 0;
                       break;
                   case 7:
                       FW = 1;
                       break;
                   case 8:
                       FW = 2;
                       break;
                   case 9:
                       FW = 3;
                       break;
                   case 10:
                       FW = 4;
                       break;
                   case 11:
                       FW = 5;
                       break;
                   case 12:
                       FW = 6;
                       break;
                   case 13:
                       FW = 7;
                       break;
                   case 14:
                       FW = 8;
                       break;
                   case 15:
                       FW = 9;
                       break;
                   case 16:
                       FW = 11;
                       break;
                   default:
                       return(0);
                       break;
               };
       }

       if (md->DEP == 4) HDE = (md->MW+md->FX-1)/md->FX;
       else              HDE = (md->MW+3)/4;
       VDE = md->MH-1;

       /* hmm... */
       fb[0x8000] = 0;

               /* enable extension registers */
       WSeq (ba, SEQ_ID_EXTENDED_ENABLE,       0x05);

#if 0
       /* program the clock oscillator */
       vgaw (ba, GREG_MISC_OUTPUT_W, 0xe3 | ((clksel & 3) * 0x04));
       vgaw (ba, GREG_FEATURE_CONTROL_W, 0x00);

       /* XXXX according to the NCR specs, this register should be set to 1
          XXXX before doing the MISC_OUTPUT setting and CLOCKING_MODE
          XXXX setting. */
       WSeq (ba, SEQ_ID_RESET,                 0x03);

       WSeq (ba, SEQ_ID_CLOCKING_MODE,         0x01 | ((md->FLG & MDF_CLKDIV2)/ MDF_CLKDIV2 * 8));
       WSeq (ba, SEQ_ID_MAP_MASK,              0x0f);
       WSeq (ba, SEQ_ID_CHAR_MAP_SELECT,       0x00);
               /* odd/even write select + extended memory */
       WSeq (ba, SEQ_ID_MEMORY_MODE,   0x06);
       /* XXXX I think this order of setting RESET is wrong... */
       WSeq (ba, SEQ_ID_RESET,                 0x01);
       WSeq (ba, SEQ_ID_RESET,                 0x03);
#else
       WSeq (ba, SEQ_ID_RESET,                 0x01);

               /* set font width + rest of clocks */
       WSeq (ba, SEQ_ID_EXT_CLOCK_MODE,        0x30 | (FW & 0x0f) | ((clksel & 4) / 4 * 0x40) );
               /* another clock bit, plus hw stuff */
       WSeq (ba, SEQ_ID_MISC_FEATURE_SEL,      0xf4 | (clksel & 8) );

       /* program the clock oscillator */
       vgaw (ba, GREG_MISC_OUTPUT_W,           0xe3 | ((clksel & 3) * 0x04));
       vgaw (ba, GREG_FEATURE_CONTROL_W,       0x00);

       WSeq (ba, SEQ_ID_CLOCKING_MODE,         0x01 | ((md->FLG & MDF_CLKDIV2)/ MDF_CLKDIV2 * 8));
       WSeq (ba, SEQ_ID_MAP_MASK,              0x0f);
       WSeq (ba, SEQ_ID_CHAR_MAP_SELECT,       0x00);
               /* odd/even write select + extended memory */
       WSeq (ba, SEQ_ID_MEMORY_MODE,           0x06);
       WSeq (ba, SEQ_ID_RESET,                 0x03);
#endif

               /* monochrome cursor */
       WSeq (ba, SEQ_ID_CURSOR_CONTROL,        0x00);
               /* bank0 */
       WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI,      0x00);
       WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO,      0x00);
       WSeq (ba, SEQ_ID_DISP_OFF_HI ,          0x00);
       WSeq (ba, SEQ_ID_DISP_OFF_LO ,          0x00);
               /* bank0 */
       WSeq (ba, SEQ_ID_SEC_HOST_OFF_HI,       0x00);
       WSeq (ba, SEQ_ID_SEC_HOST_OFF_LO,       0x00);
               /* 1M-chips + ena SEC + ena EMem + rw PrimA0/rw Sec/B0 */
       WSeq (ba, SEQ_ID_EXTENDED_MEM_ENA,      0x3 | 0x4 | 0x10 | 0x40);
#if 0
               /* set font width + rest of clocks */
       WSeq (ba, SEQ_ID_EXT_CLOCK_MODE,        0x30 | (FW & 0x0f) | ((clksel & 4) / 4 * 0x40) );
#endif
       if (md->DEP == 4) {
                       /* no ext-chain4 + no host-addr-bit-16 */
               WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR,        0x00);
                       /* no packed/nibble + no 256bit gfx format */
               WSeq (ba, SEQ_ID_EXT_PIXEL_CNTL,        0x00);
       }
       else {
               WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR,        0x02);
                       /* 256bit gfx format */
               WSeq (ba, SEQ_ID_EXT_PIXEL_CNTL,        0x01);
       }
               /* AT-interface */
       WSeq (ba, SEQ_ID_BUS_WIDTH_FEEDB,       0x06);
               /* see fg/bg color expansion */
       WSeq (ba, SEQ_ID_COLOR_EXP_WFG,         0x01);
       WSeq (ba, SEQ_ID_COLOR_EXP_WBG,         0x00);
       WSeq (ba, SEQ_ID_EXT_RW_CONTROL,        0x00);
#if 0
               /* another clock bit, plus hw stuff */
       WSeq (ba, SEQ_ID_MISC_FEATURE_SEL,      0xf4 | (clksel & 8) );
#endif
               /* don't tristate PCLK and PIX */
       WSeq (ba, SEQ_ID_COLOR_KEY_CNTL,        0x40 );
               /* reset CRC circuit */
       WSeq (ba, SEQ_ID_CRC_CONTROL,           0x00 );
               /* set RAS/CAS swap */
       WSeq (ba, SEQ_ID_PERF_SELECT,           0x20);

       WCrt (ba, CRT_ID_END_VER_RETR,          (md->VSE & 0xf ) | 0x20);
       WCrt (ba, CRT_ID_HOR_TOTAL,             md->HT   & 0xff);
       WCrt (ba, CRT_ID_HOR_DISP_ENA_END,      (HDE-1)  & 0xff);
       WCrt (ba, CRT_ID_START_HOR_BLANK,       md->HBS  & 0xff);
       WCrt (ba, CRT_ID_END_HOR_BLANK,         (md->HBE & 0x1f) | 0x80);

       WCrt (ba, CRT_ID_START_HOR_RETR,        md->HSS  & 0xff);
       WCrt (ba, CRT_ID_END_HOR_RETR,          (md->HSE & 0x1f) | ((md->HBE & 0x20)/ 0x20 * 0x80));
       WCrt (ba, CRT_ID_VER_TOTAL,             (md->VT  & 0xff));
       WCrt (ba, CRT_ID_OVERFLOW,              (( (md->VSS  & 0x200) / 0x200 * 0x80)
                                                | ((VDE     & 0x200) / 0x200 * 0x40)
                                                | ((md->VT  & 0x200) / 0x200 * 0x20)
                                                |                              0x10
                                                | ((md->VBS & 0x100) / 0x100 * 8   )
                                                | ((md->VSS & 0x100) / 0x100 * 4   )
                                                | ((VDE     & 0x100) / 0x100 * 2   )
                                                | ((md->VT  & 0x100) / 0x100       )));
       WCrt (ba, CRT_ID_PRESET_ROW_SCAN,       0x00);

       if (md->DEP == 4) {
               WCrt (ba, CRT_ID_MAX_SCAN_LINE, ((  (md->FLG & MDF_DBL)/ MDF_DBL * 0x80)
                                                |                                 0x40
                                                | ((md->VBS & 0x200)/0x200      * 0x20)
                                                | ((md->FY-1)                   & 0x1f)));
       }
       else {
               WCrt (ba, CRT_ID_MAX_SCAN_LINE, ((  (md->FLG & MDF_DBL)/ MDF_DBL * 0x80)
                                                |                                 0x40
                                                | ((md->VBS & 0x200)/0x200      * 0x20)
                                                | (0                            & 0x1f)));
       }

       WCrt (ba, CRT_ID_CURSOR_START, (md->FY & 0x1f) - 2);
       WCrt (ba, CRT_ID_CURSOR_END, (md->FY & 0x1f) - 1);

       WCrt (ba, CRT_ID_START_ADDR_HIGH, 0x00);
       WCrt (ba, CRT_ID_START_ADDR_LOW, 0x00);

       WCrt (ba, CRT_ID_CURSOR_LOC_HIGH, 0x00);
       WCrt (ba, CRT_ID_CURSOR_LOC_LOW, 0x00);

       WCrt (ba, CRT_ID_START_VER_RETR, md->VSS & 0xff);
       WCrt (ba, CRT_ID_END_VER_RETR, (md->VSE & 0x0f) | 0x80 | 0x20);
       WCrt (ba, CRT_ID_VER_DISP_ENA_END, VDE & 0xff);
       if (md->DEP == 4)
               WCrt (ba, CRT_ID_OFFSET, (HDE / 2)  & 0xff);
       else
               WCrt (ba, CRT_ID_OFFSET, (md->TX / 8)  & 0xff);

       WCrt (ba, CRT_ID_UNDERLINE_LOC, (md->FY-1) & 0x1f);
       WCrt (ba, CRT_ID_START_VER_BLANK, md->VBS  & 0xff);
       WCrt (ba, CRT_ID_END_VER_BLANK, md->VBE & 0xff);
               /* byte mode + wrap + select row scan counter + cms */
       WCrt (ba, CRT_ID_MODE_CONTROL, 0xe3);
       WCrt (ba, CRT_ID_LINE_COMPARE, 0xff);

               /* enable extended end bits + those bits */
       WCrt (ba, CRT_ID_EXT_HOR_TIMING1, (                                      0x20
                                        | ((md->FLG & MDF_LACE)  / MDF_LACE   * 0x10)
                                        | ((md->HT  & 0x100) / 0x100          * 0x01)
                                        | (((HDE-1) & 0x100) / 0x100          * 0x02)
                                        | ((md->HBS & 0x100) / 0x100          * 0x04)
                                        | ((md->HSS & 0x100) / 0x100          * 0x08)));

       if (md->DEP == 4)
               WCrt (ba, CRT_ID_EXT_START_ADDR, (((HDE / 2) & 0x100)/0x100 * 16));
       else
               WCrt (ba, CRT_ID_EXT_START_ADDR, (((md->TX / 8) & 0x100)/0x100 * 16));

       WCrt (ba, CRT_ID_EXT_HOR_TIMING2,  ( ((md->HT  & 0x200)/ 0x200  * 0x01)
                                        | (((HDE-1) & 0x200)/ 0x200    * 0x02)
                                        | ((md->HBS & 0x200)/ 0x200    * 0x04)
                                        | ((md->HSS & 0x200)/ 0x200    * 0x08)
                                        | ((md->HBE & 0xc0) / 0x40     * 0x10)
                                        | ((md->HSE & 0x60) / 0x20     * 0x40)));

       WCrt (ba, CRT_ID_EXT_VER_TIMING, ( ((md->VSE & 0x10) / 0x10     * 0x80)
                                        | ((md->VBE & 0x300)/ 0x100    * 0x20)
                                        |                              0x10
                                        | ((md->VSS & 0x400)/ 0x400    * 0x08)
                                        | ((md->VBS & 0x400)/ 0x400    * 0x04)
                                        | ((VDE     & 0x400)/ 0x400    * 0x02)
                                        | ((md->VT  & 0x400)/ 0x400    * 0x01)));

       WGfx (ba, GCT_ID_SET_RESET, 0x00);
       WGfx (ba, GCT_ID_ENABLE_SET_RESET, 0x00);
       WGfx (ba, GCT_ID_COLOR_COMPARE, 0x00);
       WGfx (ba, GCT_ID_DATA_ROTATE, 0x00);
       WGfx (ba, GCT_ID_READ_MAP_SELECT, 0x00);
       WGfx (ba, GCT_ID_GRAPHICS_MODE, 0x00);
       if (md->DEP == 4)
               WGfx (ba, GCT_ID_MISC, 0x04);
       else
               WGfx (ba, GCT_ID_MISC, 0x05);
       WGfx (ba, GCT_ID_COLOR_XCARE, 0xff);
       WGfx (ba, GCT_ID_BITMASK, 0xff);

       /* reset the Attribute Controller flipflop */
       vgar (ba, GREG_STATUS1_R);
       WAttr (ba, ACT_ID_PALETTE0, 0x00);
       WAttr (ba, ACT_ID_PALETTE1, 0x01);
       WAttr (ba, ACT_ID_PALETTE2, 0x02);
       WAttr (ba, ACT_ID_PALETTE3, 0x03);
       WAttr (ba, ACT_ID_PALETTE4, 0x04);
       WAttr (ba, ACT_ID_PALETTE5, 0x05);
       WAttr (ba, ACT_ID_PALETTE6, 0x06);
       WAttr (ba, ACT_ID_PALETTE7, 0x07);
       WAttr (ba, ACT_ID_PALETTE8, 0x08);
       WAttr (ba, ACT_ID_PALETTE9, 0x09);
       WAttr (ba, ACT_ID_PALETTE10, 0x0a);
       WAttr (ba, ACT_ID_PALETTE11, 0x0b);
       WAttr (ba, ACT_ID_PALETTE12, 0x0c);
       WAttr (ba, ACT_ID_PALETTE13, 0x0d);
       WAttr (ba, ACT_ID_PALETTE14, 0x0e);
       WAttr (ba, ACT_ID_PALETTE15, 0x0f);

       vgar (ba, GREG_STATUS1_R);
       if (md->DEP == 4)
               WAttr (ba, ACT_ID_ATTR_MODE_CNTL, 0x08);
       else
               WAttr (ba, ACT_ID_ATTR_MODE_CNTL, 0x09);

       WAttr (ba, ACT_ID_OVERSCAN_COLOR, 0x00);
       WAttr (ba, ACT_ID_COLOR_PLANE_ENA, 0x0f);
       WAttr (ba, ACT_ID_HOR_PEL_PANNING, 0x00);
       WAttr (ba, ACT_ID_COLOR_SELECT, 0x00);

       vgar (ba, GREG_STATUS1_R);
               /* I have *NO* idea what strobing reg-0x20 might do... */
       vgaw (ba, ACT_ADDRESS_W, 0x20);

       if (md->DEP == 4)
               WCrt (ba, CRT_ID_MAX_SCAN_LINE, ( ((md->FLG & MDF_DBL)/ MDF_DBL * 0x80)
                                               |                                 0x40
                                               | ((md->VBS & 0x200)/0x200      * 0x20)
                                               | ((md->FY-1)                   & 0x1f)));
       else
               WCrt (ba, CRT_ID_MAX_SCAN_LINE, ( ((md->FLG & MDF_DBL)/ MDF_DBL * 0x80)
                                               |                                 0x40
                                               | ((md->VBS & 0x200)/0x200      * 0x20)
                                               | (0                            & 0x1f)));


       /* now it's time for guessing... */

       vgaw (ba, VDAC_REG_D,      0x02);

               /* if this does what I think it does, it selects DAC
                  register 0, and writes the palette in subsequent
                  registers, thus it works similar to the WD33C93
                  select/data mechanism */
       vgaw (ba, VDAC_REG_SELECT, 0x00);

       {

               short x = 15;
               const unsigned char * col = md->PAL;
               do {

                       vgaw (ba, VDAC_REG_DATA, *col++);
                       vgaw (ba, VDAC_REG_DATA, *col++);
                       vgaw (ba, VDAC_REG_DATA, *col++);


               } while (x--);

               if (md->DEP != 4) {
                       short xx = 256-17;
                       unsigned char cols = 16;
                       do {

                               vgaw(ba, VDAC_REG_DATA, cols);
                               vgaw(ba, VDAC_REG_DATA, cols);
                               vgaw(ba, VDAC_REG_DATA, cols);
                               cols++;

                       } while (xx--);
               }
       }


       /* now load the font into maps 2 (and 3 for fonts wider than 8 pixels) */
       if (md->DEP == 4) {

               /* first set the whole font memory to a test-pattern, so we
                  can see if something that shouldn't be drawn IS drawn.. */
               {
                       volatile char *c = fb;
                       long x;
                       Map(2);

                       for (x = 0; x < 65536; x++) {
                               *c++ = (x & 1)? 0xaa : 0x55;
                       }
               }

               {
                       volatile char *c = fb;
                       long x;
                       Map(3);

                       for (x = 0; x < 65536; x++) {
                               *c++ = (x & 1)? 0xaa : 0x55;
                       }
               }

               {
                 /* ok, now position at first defined character, and
                    copy over the images */
                 volatile char *c = fb + md->FLo * 32;
                 const unsigned char * f = md->FData;
                 unsigned short z;

                 Map(2);
                 for (z = md->FLo; z <= md->FHi; z++) {

                       short y = md->FY-1;
                       if (md->FX > 8){
                               do {
                                       *c++ = *f;
                                       f += 2;
                               } while (y--);
                       }
                       else {
                               do {
                                       *c++ = *f++;
                               } while (y--);
                       }

                       c += 32-md->FY;

                 }

                 if (md->FX > 8) {
                       unsigned short zz;

                       Map(3);
                       c = fb + md->FLo*32;
                       f = md->FData+1;
                       for (zz = md->FLo; z <= md->FHi; zz++) {

                               short y = md->FY-1;
                               do {
                                       *c++ = *f;
                                       f += 2;
                               } while (y--);

                               c += 32-md->FY;

                       }
                 }
               }

       }

               /* select map 0 */
       WGfx (ba, GCT_ID_READ_MAP_SELECT,       0);
       if (md->DEP == 4)
                       /* allow writes into maps 0 and 1 */
               WSeq (ba, SEQ_ID_MAP_MASK,              3);
       else
                       /* allow writes into all maps */
               WSeq (ba, SEQ_ID_MAP_MASK,              0x0f);

               /* select extended chain4 addressing:
                   !A0/!A1     map 0   character to be displayed
                   !A1/ A1     map 1   attribute of that character
                    A0/!A1     map 2   not used (masked out, ignored)
                    A0/ A1     map 3   not used (masked out, ignored) */
       WSeq (ba, SEQ_ID_EXT_VIDEO_ADDR,        RSeq(ba, SEQ_ID_EXT_VIDEO_ADDR) | 0x02);

       if (md->DEP == 4) {
               /* position in display memory */
               volatile unsigned short * c = (volatile unsigned short *) fb;

               /* fill with blank, white on black */
               const unsigned short fill_val = 0x2010;
               short x = md->XY;
               do {
                       *c = fill_val;
                       c += 2; } while (x--);

               /* I won't comment this :-)) */
               c = (volatile unsigned short *) fb;
               c += (md->TX-6)*2;
               {
                 unsigned short init_msg[6] = {0x520a, 0x450b, 0x540c, 0x490d, 0x4e0e, 0x410f};
                 unsigned short * f = init_msg;
                 x = 5;
                 do {
                       *c = *f++;
                       c += 2;
                 } while (x--);
               }
       }
       else if (md->DEP == 8) {
               /* could clear the gfx screen here, but that's what the X server does anyway */
               ;
       }

       gp->g_data      = (void *)md;
       gi->gd_regaddr  = (void *)ztwopa(ba);
       gi->gd_regsize  = 64*1024;

       gi->gd_fbaddr   = (void *)ztwopa(fb);
       gi->gd_fbsize   = 64*1024;      /* larger, but that's whats mappable */

       gi->gd_colors   = 1 << md->DEP;
       gi->gd_planes   = md->DEP;

       gi->gd_fbwidth  = md->MW;
       gi->gd_fbheight = md->MH;
       gi->gd_fbx      = 0;
       gi->gd_fby      = 0;
       gi->gd_dwidth   = md->TX * md->FX;
       gi->gd_dheight  = md->TY * md->FY;
       gi->gd_dx       = 0;
       gi->gd_dy       = 0;

       /* initialized, works, return 1 */
       return(1);
}

void grfrtattach(device_t, device_t, void *);
int grfrtprint(void *, const char *);
int grfrtmatch(device_t, cfdata_t, void *);

int rt_mode(struct grf_softc *, u_long, void *, u_long, int);
static int rt_getvmode(struct grf_softc *, struct grfvideo_mode *);
static int rt_setvmode(struct grf_softc *, unsigned, int);
int rt_getspritepos(struct grf_softc *, struct grf_position *);
int rt_setspritepos(struct grf_softc *, struct grf_position *);
int rt_getspriteinfo(struct grf_softc *, struct grf_spriteinfo *);
int rt_setspriteinfo(struct grf_softc *, struct grf_spriteinfo *);
int rt_getspritemax(struct grf_softc *, struct grf_position *);
int rt_getcmap(struct grf_softc *, struct grf_colormap *);
int rt_putcmap(struct grf_softc *, struct grf_colormap *);
int rt_bitblt(struct grf_softc *, struct grf_bitblt *);
int rt_blank(struct grf_softc *, int *);

CFATTACH_DECL_NEW(grfrt, sizeof(struct grf_softc),
   grfrtmatch, grfrtattach, NULL, NULL);

/*
* only used in console init
*/
static struct cfdata *cfdata;

/*
* we make sure to only init things once.  this is somewhat
* tricky regarding the console.
*/
int
grfrtmatch(device_t parent, cfdata_t cf, void *aux)
{
#ifdef RETINACONSOLE
       static int rtconunit = -1;
#endif
       struct zbus_args *zap;

       zap = aux;

       /*
        * allow only one retina console
        */
       if (amiga_realconfig == 0)
#ifdef RETINACONSOLE
               if (rtconunit != -1)
#endif
                       return(0);
       /*
        * check that this is a retina board.
        */
       if (zap->manid != 18260 || zap->prodid != 6)
               return(0);

#ifdef RETINACONSOLE
       if (amiga_realconfig == 0 || rtconunit != cf->cf_unit) {
#endif
               if ((unsigned)retina_default_mon >= retina_mon_max ||
                   monitor_defs[retina_default_mon].DEP == 8)
                       retina_default_mon = 0;

               current_mon = monitor_defs + retina_default_mon;
               if (retina_alive(current_mon) == 0)
                       return(0);
#ifdef RETINACONSOLE
               if (amiga_realconfig == 0) {
                       rtconunit = cf->cf_unit;
                       cfdata = cf;
               }
       }
#endif
       return(1);
}

/*
* attach to the grfbus (zbus)
*/
void
grfrtattach(device_t parent, device_t self, void *aux)
{
       static struct grf_softc congrf;
       struct device temp;
       struct zbus_args *zap;
       struct grf_softc *gp;

       zap = aux;

       if (self == NULL) {
               gp = &congrf;
               gp->g_device = &temp;
               temp.dv_private = gp;
       } else {
               gp = device_private(self);
               gp->g_device = self;
       }

       if (self != NULL && congrf.g_regkva != 0) {
               /*
                * we inited earlier just copy the info
                * take care not to copy the device struct though.
                */
               memcpy(&gp->g_display, &congrf.g_display,
                   (char *)&gp[1] - (char *)&gp->g_display);
       } else {
               gp->g_regkva = (volatile void *)zap->va;
               gp->g_fbkva = (volatile char *)zap->va + 64 * 1024;
               gp->g_unit = GRF_RETINAII_UNIT;
               gp->g_flags = GF_ALIVE;
               gp->g_mode = rt_mode;
#if NITE > 0
               gp->g_conpri = grfrt_cnprobe();
               grfrt_iteinit(gp);
#endif
               (void)rt_load_mon(gp, current_mon);
       }
       if (self != NULL)
               printf("\n");
       /*
        * attach grf
        */
       amiga_config_found(cfdata, gp->g_device, gp, grfrtprint, CFARGS_NONE);
}

int
grfrtprint(void *aux, const char *pnp)
{
       if (pnp)
               aprint_normal("grf%d at %s", ((struct grf_softc *)aux)->g_unit,
                       pnp);
       return(UNCONF);
}

static int
rt_getvmode(struct grf_softc *gp, struct grfvideo_mode *vm)
{
       struct MonDef *md;
       int vmul;

       if (vm->mode_num && vm->mode_num > retina_mon_max)
               return (EINVAL);

       if (! vm->mode_num)
               vm->mode_num = (current_mon - monitor_defs) + 1;

       md = monitor_defs + (vm->mode_num - 1);
       strncpy (vm->mode_descr, monitor_descr[vm->mode_num - 1],
           sizeof (vm->mode_descr));
       vm->pixel_clock  = md->FQ;
       vm->disp_width   = md->MW;
       vm->disp_height  = md->MH;
       vm->depth        = md->DEP;

       /*
        * From observation of the monitor definition table above, I guess that
        * the horizontal timings are in units of longwords. Hence, I get the
        * pixels by multiplication with 32 and division by the depth.
        * The text modes, apparently marked by depth == 4, are even more weird.
        * According to a comment above, they are computed from a depth==8 mode
        * (thats for us: * 32 / 8) by applying another factor of 4 / font width.
        * Reverse applying the latter formula most of the constants cancel
        * themselves and we are left with a nice (* font width).
        * That is, internal timings are in units of longwords for graphics
        * modes, or in units of characters widths for text modes.
        * We better don't WRITE modes until this has been real live checked.
        *                      - Ignatios Souvatzis
        */

       if (md->DEP != 4) {
               vm->hblank_start = md->HBS * 32 / md->DEP;
               vm->hsync_start  = md->HSS * 32 / md->DEP;
               vm->hsync_stop   = md->HSE * 32 / md->DEP;
               vm->htotal       = md->HT * 32 / md->DEP;
       } else {
               vm->hblank_start = md->HBS * md->FX;
               vm->hsync_start  = md->HSS * md->FX;
               vm->hsync_stop   = md->HSE * md->FX;
               vm->htotal       = md->HT * md->FX;
       }


       /* XXX move vm->disp_flags and vmul to rt_load_mon
       * if rt_setvmode can add new modes with grfconfig */
       vm->disp_flags = 0;
       vmul = 2;
       if (md->FLG & MDF_DBL) {
               vm->disp_flags |= GRF_FLAGS_DBLSCAN;
               vmul = 4;
       }
       if (md->FLG & MDF_LACE) {
               vm->disp_flags |= GRF_FLAGS_LACE;
               vmul = 1;
       }
       vm->vblank_start = md->VBS * vmul / 2;
       vm->vsync_start  = md->VSS * vmul / 2;
       vm->vsync_stop   = md->VSE * vmul / 2;
       vm->vtotal       = md->VT * vmul / 2;

       return (0);
}


static int
rt_setvmode(struct grf_softc *gp, unsigned mode, int txtonly)
{
       int error;

       if (!mode || mode > retina_mon_max)
               return (EINVAL);

       if (txtonly && monitor_defs[mode-1].DEP == 8)
               return (EINVAL);

       current_mon = monitor_defs + (mode - 1);

       error = rt_load_mon (gp, current_mon) ? 0 : EINVAL;

       return (error);
}


/*
* Change the mode of the display.
* Return a UNIX error number or 0 for success.
*/
int
rt_mode(struct grf_softc *gp, u_long cmd, void *arg, u_long a2, int a3)
{
/* implement these later... */

       switch (cmd) {
           case GM_GRFON:
               rt_setvmode (gp, retina_default_gfx + 1, 0);
               return (0);

           case GM_GRFOFF:
               rt_setvmode (gp, retina_default_mon + 1, 0);
               return (0);

           case GM_GRFCONFIG:
               return (0);

           case GM_GRFGETVMODE:
               return (rt_getvmode (gp, (struct grfvideo_mode *) arg));

           case GM_GRFSETVMODE:
               return (rt_setvmode (gp, *(unsigned *) arg, 1));

           case GM_GRFGETNUMVM:
               *(int *)arg = retina_mon_max;
               return (0);

           case GM_GRFIOCTL:
               return (rt_ioctl (gp, a2, arg));

           default:
               break;
       }

       return (EPASSTHROUGH);
}

int
rt_ioctl(register struct grf_softc *gp, u_long cmd, void *data)
{
       switch (cmd) {
           case GRFIOCGSPRITEPOS:
               return (rt_getspritepos (gp, (struct grf_position *) data));

           case GRFIOCSSPRITEPOS:
               return (rt_setspritepos (gp, (struct grf_position *) data));

           case GRFIOCSSPRITEINF:
               return (rt_setspriteinfo (gp, (struct grf_spriteinfo *) data));

           case GRFIOCGSPRITEINF:
               return (rt_getspriteinfo (gp, (struct grf_spriteinfo *) data));

           case GRFIOCGSPRITEMAX:
               return (rt_getspritemax (gp, (struct grf_position *) data));

           case GRFIOCGETCMAP:
               return (rt_getcmap (gp, (struct grf_colormap *) data));

           case GRFIOCPUTCMAP:
               return (rt_putcmap (gp, (struct grf_colormap *) data));

           case GRFIOCBITBLT:
               return (rt_bitblt (gp, (struct grf_bitblt *) data));

           case GRFIOCBLANK:
               return (rt_blank(gp, (int *)data));
       }

       return (EPASSTHROUGH);
}

int
rt_getcmap(struct grf_softc *gfp, struct grf_colormap *cmap)
{
       volatile unsigned char *ba;
       u_char red[256], green[256], blue[256], *rp, *gp, *bp;
       short x;
       int error;

       if (cmap->count == 0 || cmap->index >= 256)
               return (0);

       if (cmap->count > 256 - cmap->index)
               cmap->count = 256 - cmap->index;

       ba = gfp->g_regkva;
       /* first read colors out of the chip, then copyout to userspace */
       vgaw (ba, VDAC_REG_SELECT, cmap->index);
       x = cmap->count - 1;
       rp = red + cmap->index;
       gp = green + cmap->index;
       bp = blue + cmap->index;
       do {
               *rp++ = vgar (ba, VDAC_REG_DATA);
               *gp++ = vgar (ba, VDAC_REG_DATA);
               *bp++ = vgar (ba, VDAC_REG_DATA);
       }
       while (x--);

       if (!(error = copyout (red + cmap->index, cmap->red, cmap->count))
           && !(error = copyout (green + cmap->index, cmap->green, cmap->count))
           && !(error = copyout (blue + cmap->index, cmap->blue, cmap->count)))
               return (0);

       return (error);
}

int
rt_putcmap(struct grf_softc *gfp, struct grf_colormap *cmap)
{
       volatile unsigned char *ba;
       u_char red[256], green[256], blue[256], *rp, *gp, *bp;
       short x;
       int error;

       if (cmap->count == 0 || cmap->index >= 256)
               return 0;

       if (cmap->count > 256 - cmap->index)
               cmap->count = 256 - cmap->index;

       /* first copy the colors into kernelspace */
       if (!(error = copyin (cmap->red, red + cmap->index, cmap->count))
           && !(error = copyin (cmap->green, green + cmap->index, cmap->count))
           && !(error = copyin (cmap->blue, blue + cmap->index, cmap->count)))
       {
               ba = gfp->g_regkva;
               vgaw (ba, VDAC_REG_SELECT, cmap->index);
               x = cmap->count - 1;
               rp = red + cmap->index;
               gp = green + cmap->index;
               bp = blue + cmap->index;
               do {
                       vgaw (ba, VDAC_REG_DATA, *rp++);
                       vgaw (ba, VDAC_REG_DATA, *gp++);
                       vgaw (ba, VDAC_REG_DATA, *bp++);
               }
               while (x--);
               return (0);
       } else
               return (error);
}


int
rt_getspritepos(struct grf_softc *gp, struct grf_position *pos)
{
       volatile unsigned char *ba;

       ba = gp->g_regkva;
       pos->x = vgar (ba, SEQ_ID_CURSOR_X_LOC_LO) |
                       (vgar (ba, SEQ_ID_CURSOR_X_LOC_HI) << 8);
       pos->y = vgar (ba, SEQ_ID_CURSOR_Y_LOC_LO) |
                       (vgar (ba, SEQ_ID_CURSOR_Y_LOC_HI) << 8);
       return (0);
}

int
rt_setspritepos(struct grf_softc *gp, struct grf_position *pos)
{
       volatile unsigned char *ba;

       ba = gp->g_regkva;
       vgaw (ba, SEQ_ID_CURSOR_X_LOC_LO, pos->x & 0xff);
       vgaw (ba, SEQ_ID_CURSOR_X_LOC_HI, (pos->x >> 8) & 0x07);
       vgaw (ba, SEQ_ID_CURSOR_Y_LOC_LO, pos->y & 0xff);
       vgaw (ba, SEQ_ID_CURSOR_Y_LOC_HI, (pos->y >> 8) & 0x07);
       return (0);
}

/* assume an at least 2M retina (XXX), sprite is last in memory.
* According to the bogus docs, the cursor can be at most 128 lines
* in height, and the x-hostspot can be placed at most at pos 31,
* this gives width of a long
*/
#define SPRITE_ADDR (2*1024*1024 - 128*4)

int
rt_getspriteinfo(struct grf_softc *gp, struct grf_spriteinfo *info)
{
       volatile void *ba, *fb;

       ba = gp->g_regkva;
       fb = gp->g_fbkva;
       if (info->set & GRFSPRSET_ENABLE)
               info->enable = vgar (ba, SEQ_ID_CURSOR_CONTROL) & 0x01;
       if (info->set & GRFSPRSET_POS)
               rt_getspritepos (gp, &info->pos);
       if (info->set & GRFSPRSET_HOT) {
               info->hot.x = vgar (ba, SEQ_ID_CURSOR_X_INDEX) & 0x1f;
               info->hot.y = vgar (ba, SEQ_ID_CURSOR_Y_INDEX) & 0x7f;
       }
       if (info->set & GRFSPRSET_CMAP) {
               struct grf_colormap cmap;
               int index;
               cmap.index = 0;
               cmap.count = 256;
               rt_getcmap (gp, &cmap);
               index = vgar (ba, SEQ_ID_CURSOR_COLOR0);
               info->cmap.red[0] = cmap.red[index];
               info->cmap.green[0] = cmap.green[index];
               info->cmap.blue[0] = cmap.blue[index];
               index = vgar (ba, SEQ_ID_CURSOR_COLOR1);
               info->cmap.red[1] = cmap.red[index];
               info->cmap.green[1] = cmap.green[index];
               info->cmap.blue[1] = cmap.blue[index];
       }
       if (info->set & GRFSPRSET_SHAPE) {
               int saved_bank_lo = RSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO);
               int saved_bank_hi = RSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI);
               int last_bank = SPRITE_ADDR >> 6;
               int last_bank_lo = last_bank & 0xff;
               int last_bank_hi = last_bank >> 8;
               u_char mask;
               WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, last_bank_lo);
               WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, last_bank_hi);
               copyout (__UNVOLATILE(fb), info->image, 128*4);
               mask = RSeq (ba, SEQ_ID_CURSOR_PIXELMASK);
               WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, saved_bank_lo);
               WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, saved_bank_hi);
               copyout (&mask, info->mask, 1);
               info->size.x = 32; /* ??? */
               info->size.y = (RSeq (ba, SEQ_ID_CURSOR_CONTROL) & 6) << 4;
       }

       return (0);
}


int
rt_setspriteinfo(struct grf_softc *gp, struct grf_spriteinfo *info)
{
       volatile void *ba, *fb;
       u_char control;

       ba = gp->g_regkva;
       fb = gp->g_fbkva;
       control = vgar (ba, SEQ_ID_CURSOR_CONTROL);
       if (info->set & GRFSPRSET_ENABLE) {
               if (info->enable)
                       control |= 1;
               else
                       control &= ~1;
       vgaw (ba, SEQ_ID_CURSOR_CONTROL, control);
       }
       if (info->set & GRFSPRSET_POS)
               rt_setspritepos (gp, &info->pos);
       if (info->set & GRFSPRSET_HOT) {
               vgaw (ba, SEQ_ID_CURSOR_X_INDEX, info->hot.x & 0x1f);
               vgaw (ba, SEQ_ID_CURSOR_Y_INDEX, info->hot.y & 0x7f);
       }
       if (info->set & GRFSPRSET_CMAP) {
               /* hey cheat a bit here.. XXX */
               vgaw (ba, SEQ_ID_CURSOR_COLOR0, 0);
               vgaw (ba, SEQ_ID_CURSOR_COLOR1, 1);
       }
       if (info->set & GRFSPRSET_SHAPE) {
               int saved_bank_lo = RSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO);
               int saved_bank_hi = RSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI);
               int last_bank = SPRITE_ADDR >> 6;
               int last_bank_lo = last_bank & 0xff;
               int last_bank_hi = last_bank >> 8;
               u_char mask;
               WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, last_bank_lo);
               WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, last_bank_hi);
               copyin (info->image, __UNVOLATILE(fb), 128*4);
               WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, saved_bank_lo);
               WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, saved_bank_hi);
               copyin (info->mask, &mask, 1);
               WSeq (ba, SEQ_ID_CURSOR_PIXELMASK, mask);
               /* info->size.x = 32; *//* ??? */

               info->size.y = (RSeq (ba, SEQ_ID_CURSOR_CONTROL) & 6) << 4;
               control = (control & ~6) | ((info->size.y >> 4) & 6);
               vgaw (ba, SEQ_ID_CURSOR_CONTROL, control);

               /* sick intel bull-addressing.. */
               WSeq (ba, SEQ_ID_CURSOR_STORE_LO, SPRITE_ADDR & 0x0f);
               WSeq (ba, SEQ_ID_CURSOR_STORE_HI, 0);
               WSeq (ba, SEQ_ID_CURSOR_ST_OFF_LO, (SPRITE_ADDR >> 4) & 0xff);
               WSeq (ba, SEQ_ID_CURSOR_ST_OFF_HI, ((SPRITE_ADDR >> 4) >> 8) & 0xff);
       }

       return (0);
}


int
rt_getspritemax(struct grf_softc *gp, struct grf_position *pos)
{
       pos->x = 32;
       pos->y = 128;

       return (0);
}


/*
* !!! THIS AREA UNDER CONSTRUCTION !!!
*/

int
rt_bitblt(struct grf_softc *gp, struct grf_bitblt *bb)
{
       return (EINVAL);

#if 0
 volatile void *ba, fb;
 u_char control;
 u_char saved_bank_lo;
 u_char saved_bank_hi;
 u_char src_bank_lo, src_bank_hi;
 u_char dst_bank_lo, dst_bank_hi;
 u_long src_offset, dst_offset;
 u_short src_bank, dst_bank;
 u_char *srcp, *dstp;
 short x, y;
 u_long tot;

 ba = gp->g_regkva;
 fb = gp->g_fbkva;

 saved_bank_lo = RSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO);
 saved_bank_hi = RSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI);

 /* for now, only GRFBBcopy is supported, and only for depth 8. No
    clipping is performed, either... */

 if (bb->op != GRFBBcopy && gp->g_display.gd_planes != 8)
   return EINVAL;

 src_offset = op->src_x + op->src_y * gp->g_display.gd_fbwidth;
 dst_offset = op->dst_x + op->dst_y * gp->g_display.gd_fbwidth;
 tot = op->w * op->h;

 /* set write mode 1, "[...] data in the read latches is written
    to memory during CPU memory write cycles. [...]" */
 WGfx (ba, GCT_ID_GRAPHICS_MODE, (RGfx(ba, GCT_ID_GRAPHICS_MODE) & 0xfc) | 1);
 /* write to primary, read from secondary */
 WSeq (ba, SEQ_ID_EXTENDED_MEM_ENA, (RSeq(ba, SEQ_ID_EXTENDED_MEM_ENA) & 0x1f) | 0 );

 if (src_offset < dst_offset)
   {
     /* start at end */
     src_offset += tot;
     dst_offset += tot;
   }

 src_bank_lo = (src_offset >> 6) & 0xff;
 src_bank_hi = (src_offset >> 14) & 0xff;
 dst_bank_lo = (dst_offset >> 6) & 0xff;
 dst_bank_hi = (dst_offset >> 14) & 0xff;

 while (tot)
   {
     WSeq (ba, SEQ_ID_SEC_HOST_OFF_LO, src_bank_lo);
     WSeq (ba, SEQ_ID_SEC_HOST_OFF_HI, src_bank_hi);
     WSeq (ba, SEQ_ID_PRIM_HOST_OFF_LO, dst_bank_lo);
     WSeq (ba, SEQ_ID_PRIM_HOST_OFF_HI, dst_bank_hi);

     if (src_offset < dst_offset)
       {


       }
     else
       {

       }
   }


#endif
}


int
rt_blank(struct grf_softc *gp, int *on)
{
       struct MonDef *md = (struct MonDef *)gp->g_data;
       int r;

       r = 0x01 | ((md->FLG & MDF_CLKDIV2)/ MDF_CLKDIV2 * 8);

       WSeq(gp->g_regkva, SEQ_ID_CLOCKING_MODE, *on > 0 ? r : 0x21);

       return(0);
}

#endif  /* NGRF */