Home | History | Annotate | Line # | Download | only in dev
grf_cc.c revision 1.5
      1 #include "grf.h"
      2 #if NGRF > 0
      3 
      4 /* Graphics routines for the AMIGA native custom chip set. */
      5 
      6 #include "sys/param.h"
      7 #include "sys/errno.h"
      8 #include "grfioctl.h"
      9 #include "grfvar.h"
     10 #include "grf_ccreg.h"
     11 #include "../include/cpu.h"
     12 #include "../amiga/custom.h"
     13 #include "../amiga/cia.h"
     14 
     15 extern caddr_t CHIPMEMADDR;
     16 extern caddr_t chipmem_steal ();
     17 
     18 struct ccfb ccfb = {
     19   DEF_DISP_WIDTH,
     20   DEF_DISP_HEIGHT,
     21   DEF_DISP_X, DEF_DISP_Y,
     22   DEF_DISP_Z,
     23   0,
     24   DEF_FB_WIDTH,
     25   DEF_FB_HEIGHT,
     26   0,
     27   DEF_FB_X, DEF_FB_Y, DEF_FB_Z,
     28 #if 0
     29   DEF_DIWSTRT, DEF_DIWSTOP, DEF_DDFSTRT, DEF_DDFSTOP,
     30 #endif
     31   DEF_COL0, DEF_COL1, DEF_COL2, DEF_COL3, 0,0,0,0,0,0,0,0,0,0,0,0,
     32   DEF_COL10, DEF_COL11, DEF_COL12, DEF_COL13, 0,0,0,0,0,0,0,0,0,0,0,0,
     33   0,				/* chip ram for beep sample */
     34   DEF_PERIOD, DEF_VOLUME,	/* beep sample period and volume */
     35   0,DEF_ABEEP,			/* beep timer, timer init value */
     36   0,DEF_DBEEP,			/* beep timer, timer init value */
     37   0,0,				/* cop1, cop2 */
     38   0,				/* pointer */
     39   0,0,				/* mouseH, mouseV */
     40   0,0,				/* lastMouseH, lastMouseV */
     41   0,0,				/* mouseX, mouseY */
     42   0,0,0,			/* mouseb1, mouseb2, mouseb3 */
     43   0,0,				/* joy1, joy2 */
     44   DEF_SCREEN,DEF_MOUSE,		/* screen/mouse blank timer init */
     45   0,0,				/* screenblank, mouseblank */
     46   0,0,				/* enableFlag, pad */
     47 };
     48 
     49 /*
     50  * custom copper list structure.  It replaces the macro method of
     51  * building copper lists for a good reason.  You want to change
     52  * diwstrt in an ioctl() handler?  well, with this struct, it is
     53  * trivial :-)
     54  *
     55  * YOU DON'T WANT! ioctl's to the console should NOT use any
     56  * implementation dependant data format to set values, they
     57  * should pass hi-level information that is processed by
     58  * the different console drivers. This driver would recalculate
     59  * diwstrt (for example) from given disp_* values.
     60  */
     61 typedef struct {
     62   u_short planes[6][4];		/* move + hi word, move + lo word */
     63   u_short bplcon0[2];		/* move + viewmode */
     64   u_short bplcon1[2];		/* move + BPLCON1 */
     65   u_short bpl1mod[2];		/* move + BPL1MOD */
     66   u_short bpl2mod[2];		/* move + BPL2MOD */
     67   u_short diwstrt[2];		/* move + DIWSTRT */
     68   u_short diwstop[2];		/* move + DIWSTOP */
     69   u_short ddfstrt[2];		/* move + DDFSTRT */
     70   u_short ddfstop[2];		/* move + DDFSTOP */
     71   u_short sprites[4*8];		/* 8  sprites (0 = mouseptr, 7 unused) */
     72   u_short colors[32*2];		/* move + color, 32 color regs */
     73   u_short copother[4];		/* move + COP1LC (to point to other copper list) */
     74   u_short finish[6];		/* COPEND instruction, -or-
     75 				   move + (COP2LC, COP2LC + 2, COPJMP2) */
     76 } COPPERLIST;
     77 
     78 /*
     79  * custom struct to describe the mousepointer sprite in chipram.
     80  * the header is tweaked by the vbl handler to move the mouse sprite
     81  * around.  the image[] array can be modified by the ioctl() handler
     82  * to change the image for the sprite!
     83  *
     84  * Again, we should probably have a much higher resolution, generic
     85  * sprite, and scale that down if necessary in the invidial drivers.
     86  */
     87 typedef struct {
     88   u_char header[4];
     89   u_short image[16*2];
     90   u_short footer[2];
     91 } SPRITEPTR;
     92 
     93 /*
     94  * initializer values for the pointer struct in chip ram.  It is a stupid
     95  * crosshair sprite, in just one color.  Do NOT change the first 4 bytes!
     96  */
     97 static SPRITEPTR pointerInit = {
     98   0x50,0x50,0x60,0x00,		/* header */
     99   0x0000,0x0000,		/* image */
    100   0x0080,0x0000,
    101   0x0080,0x0000,
    102   0x0080,0x0000,
    103   0x0080,0x0000,
    104   0x0080,0x0000,
    105   0x0080,0x0000,
    106   0x0080,0x0000,
    107   0x7f7f,0x0000,
    108   0x0080,0x0000,
    109   0x0080,0x0000,
    110   0x0080,0x0000,
    111   0x0080,0x0000,
    112   0x0080,0x0000,
    113   0x0080,0x0000,
    114   0x0080,0x0000,
    115   0x0000,0x0000,		/* footer */
    116 };
    117 
    118 /*
    119  * void initbeep(struct ccfb *fb);
    120  *
    121  * synopsis:
    122  *  allocates 20 bytes for a sine wave sample (in chip ram) and
    123  *  initializes it.  The audio hardware is turned on to play
    124  *  the sine wave sample in an infinite loop!  The volume is just
    125  *  set to zero so you don't hear it...  The sample is played in
    126  *  channels 0 and 1 so it goes out left+right audio jacks in the
    127  *  back of the machine.  The DMA is not enabled here... it is
    128  *  enabled in cc_init() below...  To make an audible beep, all
    129  *  that is needed is to turn on the volume, and then have the
    130  *  vbl handler turn off the volume after the desired beep duration
    131  *  has elapsed.
    132  *
    133  *  The custom chip console should really be broken down into a
    134  *  physical and logical layer.  The physical layer should have things
    135  *  like the bitplanes, copper list, mousepointer chipram, and the
    136  *  audible beep.  The logical layers should have their own private
    137  *  mousepointer image, color palette, and beep parameters.  The logical
    138  *  layer can keep an image of chipram for its own context - layers of
    139  *  sorts, in amigaos parlance.
    140  */
    141 static inline void
    142 initbeep (fb)
    143     struct ccfb *fb;
    144 {
    145   static char sample[20] = {
    146     0,39,75,103,121,127,121,103,75,39,0,
    147     -39,-75,-103,-121,-127,-121,-103,-75,-39
    148   };
    149   short i;
    150   char *ptr = chipmem_steal(20);
    151 
    152   if (!ptr) panic("Can't chipmem_steal 20 bytes!\n");
    153   fb->beepSample = ptr;
    154   for (i=0; i<20; i++) *ptr++ = sample[i];
    155   fb->beepTimer = fb->beepTime;
    156   custom.aud[0].lc = custom.aud[1].lc =
    157     (void *)((caddr_t)fb->beepSample - CHIPMEMADDR);
    158   custom.aud[0].len = custom.aud[1].len = 10;
    159   custom.aud[0].per = custom.aud[1].per = fb->beepPeriod;
    160   custom.aud[0].vol = custom.aud[1].vol = 0;
    161   fb->beepTimer = fb->dbeepTimer = 0;
    162   /* make SURE to disallow any audio interrupts - we don't need them */
    163   custom.intena = INTF_AUD0 | INTF_AUD1 | INTF_AUD2 | INTF_AUD3;
    164 }
    165 
    166 /*
    167  * void initpointer (struct ccfb *fb);
    168  *
    169  * synopsis:
    170  *  this routine initializes the mouse pointer part of the ccfb.
    171  *  currently, it only needs to copy the initializer data to the
    172  *  allocated chip ram.
    173  */
    174 static inline void
    175 initpointer (fb)
    176     struct ccfb *fb;
    177 {
    178   SPRITEPTR *pointer = (SPRITEPTR *)fb->pointer;
    179 
    180   /* initialize pointer structure */
    181   *pointer = pointerInit;
    182 }
    183 
    184 /*
    185  * void initcop (COPPERLIST *cop, COPPERLIST *othercop, int shf,
    186  *               struct ccfb *fb);
    187  *
    188  * synopsis:
    189  *  this function initializes one copperlist, treated as short-
    190  *  frame list if SHF is TRUE.
    191  *  it is assumed that initpointer has been called by the time
    192  *  initcop() is called.
    193  *
    194  *  This is REALLY basic stuff... even teenaged eurodemo coders
    195  *  understand it :-)  Normally, I'd have done this in assembly
    196  *  as a bunch of dc.w statements...  it is just translated into
    197  *  struct form here...
    198  *
    199  *  (yep, since this *is no* eurodemo here :-)) Hey, and we
    200  *  even have symbolic names for registers too :-))
    201  */
    202 static void inline
    203 initcop (cop, othercop, shf, fb)
    204     COPPERLIST *cop, *othercop;
    205     int shf;
    206     struct ccfb *fb;
    207 {
    208   SPRITEPTR *pointer = (SPRITEPTR *)fb->pointer;
    209   unsigned long screen;
    210   unsigned long rowbytes = fb->fb_width >> 3; /* width of display, in bytes */
    211   u_short *plptr;
    212   u_short c, i, strt, stop;
    213 
    214   /* get PA of display area */
    215   screen = (unsigned long) fb->fb - (unsigned long) CHIPMEMADDR;
    216   fb->fb_planesize = fb->fb_height * rowbytes;
    217 
    218   /* account for possible interlaced half-frame */
    219   if (shf)
    220     screen += rowbytes;
    221 
    222   /* account for oversized framebuffers */
    223   screen += (fb->fb_x >> 3) + (fb->fb_y * rowbytes);
    224 
    225 #define MOVE COP_MOVE
    226 
    227   /* initialize bitplane pointers for all planes */
    228   for (plptr = &cop->planes[0][0], i = 0; i < fb->fb_z; i++)
    229     {
    230       MOVE (plptr, bplpth(i), HIADDR (screen));
    231       plptr += 2;
    232       MOVE (plptr, bplptl(i), LOADDR (screen));
    233       plptr += 2;
    234       screen += fb->fb_planesize;
    235     }
    236   /* set the other bitplane pointers to 0, I hate this fixed size array.. */
    237   while (i < 6)
    238     {
    239       MOVE (plptr, bplpth(i), 0);
    240       plptr += 2;
    241       MOVE (plptr, bplptl(i), 0);
    242       plptr += 2;
    243       i++;
    244     }
    245 
    246   c =   0x8000 				/* HIRES */
    247       | ((fb->fb_z & 7) << 12)		/* bitplane use */
    248       | 0x0200				/* composite COLOR enable (whatever this is..) */
    249       | 0x0004;				/* LACE */
    250   MOVE (cop->bplcon0, bplcon0, c);
    251   MOVE (cop->bplcon1, bplcon1, 0);	/* nothing */
    252 
    253   /* modulo is one line for interlaced displays, plus difference between
    254      virtual and effective framebuffer size */
    255   MOVE (cop->bpl1mod, bpl1mod, (fb->fb_width + (fb->fb_width - fb->disp_width)) >> 3);
    256   MOVE (cop->bpl2mod, bpl2mod, (fb->fb_width + (fb->fb_width - fb->disp_width)) >> 3);
    257 
    258   /* these use pre-ECS register interpretation. Might want to go ECS ? */
    259   strt = (((fb->disp_y >> 1) & 0xff)<<8) | ((fb->disp_x >> 1) & 0xff);
    260   MOVE (cop->diwstrt, diwstrt, strt);
    261   stop = (((((fb->disp_y + fb->disp_height + 1-shf)>>1) & 0xff)<<8)
    262           | (((fb->disp_x + fb->disp_width)>>1) & 0xff));
    263   MOVE (cop->diwstop, diwstop, stop);
    264   /* NOTE: default values for strt: 0x2c81, stop: 0xf4c1 */
    265 
    266   /* these are from from HW-manual.. */
    267   strt = ((strt & 0xff) - 9) >> 1;
    268   MOVE (cop->ddfstrt, ddfstrt, strt);
    269   stop = strt + (((fb->disp_width >> 4) - 2) << 2);
    270   MOVE (cop->ddfstop, ddfstop, stop);
    271 
    272   /* sprites */
    273   {
    274     /* some cleverness... footer[0] is a ZERO longword in chip */
    275     u_short *spr = &cop->sprites[0];
    276     u_short addr = CUSTOM_OFS(sprpt[0]);
    277     u_short i;
    278     for (i=0; i<8; i++) {	/* for all sprites (8 of em) do */
    279       *spr++ = addr; *spr++ = HIADDR(&pointer->footer[0]);
    280       addr += 2;
    281       *spr++ = addr; *spr++ = LOADDR(&pointer->footer[0]);
    282       addr += 2;
    283     }
    284   }
    285   cop->sprites[0*4+1] = HIADDR((caddr_t)pointer-CHIPMEMADDR);
    286   cop->sprites[0*4+3] = LOADDR((caddr_t)pointer-CHIPMEMADDR);
    287 
    288   /* colors */
    289   for (i = 0; i < 32; i++)
    290     MOVE (cop->colors+i*2, color[i], fb->col[i]);
    291 
    292   /* setup interlaced display by constantly toggling between two copperlists */
    293   MOVE (cop->copother,   cop1lch, HIADDR ((unsigned long) othercop - (unsigned long) CHIPMEMADDR));
    294   MOVE (cop->copother+2, cop1lcl, LOADDR ((unsigned long) othercop - (unsigned long) CHIPMEMADDR));
    295 
    296   /* terminate copper list */
    297   COP_END (cop->finish);
    298 }
    299 
    300 /*
    301  * Install a sprite.
    302  * The sprites to be loaded on the alternate frames
    303  * can be specified separately,
    304  * so interlaced sprites are possible.
    305  */
    306 cc_install_sprite(gp, num, spr1, spr2)
    307 	struct grf_softc *gp;
    308 	int num;
    309 	u_short *spr1, *spr2;
    310 {
    311   struct ccfb *fb = &ccfb;
    312   COPPERLIST *cop;
    313 
    314   cop = (COPPERLIST*)fb->cop1;
    315   cop->sprites[num*4+1] = HIADDR((caddr_t)spr1-CHIPMEMADDR);
    316   cop->sprites[num*4+3] = LOADDR((caddr_t)spr1-CHIPMEMADDR);
    317 
    318   cop = (COPPERLIST*)fb->cop2;
    319   cop->sprites[num*4+1] = HIADDR((caddr_t)spr2-CHIPMEMADDR);
    320   cop->sprites[num*4+3] = LOADDR((caddr_t)spr2-CHIPMEMADDR);
    321 }
    322 
    323 /*
    324  * Uninstall a sprite.
    325  */
    326 cc_uninstall_sprite(gp, num)
    327 	struct grf_softc *gp;
    328 	int num;
    329 {
    330   struct ccfb *fb = &ccfb;
    331   SPRITEPTR *pointer = (SPRITEPTR*)fb->pointer;
    332   COPPERLIST *cop;
    333 
    334     /* some cleverness... footer[0] is a ZERO longword in chip */
    335   cop = (COPPERLIST*)fb->cop1;
    336   cop->sprites[num*4+1] = HIADDR(&pointer->footer[0]);
    337   cop->sprites[num*4+3] = LOADDR(&pointer->footer[0]);
    338 
    339   cop = (COPPERLIST*)fb->cop2;
    340   cop->sprites[num*4+1] = HIADDR(&pointer->footer[0]);
    341   cop->sprites[num*4+3] = LOADDR(&pointer->footer[0]);
    342 }
    343 
    344 /*
    345  * Install a copper list extension.
    346  */
    347 cc_install_cop_ext(gp, cl1, cl2)
    348 	struct grf_softc *gp;
    349 	u_short *cl1, *cl2;
    350 {
    351   struct ccfb *fb = &ccfb;
    352   COPPERLIST *cop;
    353 
    354   cop = (COPPERLIST*)fb->cop1;
    355   COP_MOVE (cop->finish+0, cop2lch, HIADDR((caddr_t)cl1-CHIPMEMADDR));
    356   COP_MOVE (cop->finish+2, cop2lcl, LOADDR((caddr_t)cl1-CHIPMEMADDR));
    357   COP_MOVE (cop->finish+4, copjmp2, 0);
    358 
    359   cop = (COPPERLIST*)fb->cop2;
    360   COP_MOVE (cop->finish+0, cop2lch, HIADDR((caddr_t)cl2-CHIPMEMADDR));
    361   COP_MOVE (cop->finish+2, cop2lcl, LOADDR((caddr_t)cl2-CHIPMEMADDR));
    362   COP_MOVE (cop->finish+4, copjmp2, 0);
    363 }
    364 
    365 /*
    366  * Uninstall a copper list extension.
    367  */
    368 cc_uninstall_cop_ext(gp, cl1, cl2)
    369 	struct grf_softc *gp;
    370 	u_short *cl1, *cl2;
    371 {
    372   register struct ccfb *fb = &ccfb;
    373   COPPERLIST *cop;
    374 
    375   cop = (COPPERLIST*)fb->cop1;
    376   COP_END (cop->finish);
    377 
    378   cop = (COPPERLIST*)fb->cop2;
    379   COP_END (cop->finish);
    380 }
    381 
    382 /*
    383  * Call this function any time a key is hit to ensure screen blanker unblanks
    384  */
    385 void
    386 cc_unblank ()
    387 {
    388   if (!ccfb.screenBlank) {	/* screenblank timer 0 means blank! */
    389     COPPERLIST *c1 = (COPPERLIST *)ccfb.cop1, *c2 = (COPPERLIST *)ccfb.cop2;
    390     /* turn on sprite and raster DMA */
    391     custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_SPRITE;
    392     ccfb.mouseBlank = ccfb.mouseTime; /* start mouseblank timer */
    393     /* screen was black, reset background color to the one in ccfb! */
    394     c1->colors[1] = c2->colors[1] = ccfb.col[0];
    395   }
    396   /* restart the screenblank timer */
    397   ccfb.screenBlank = ccfb.screenTime;
    398 }
    399 
    400 /*
    401  * void cc_bell(void);
    402  *
    403  * Synopsis:
    404  *  trigger audible bell
    405  * Description
    406  *  Call this function to start a beep tone.  The beep lasts for
    407  *  ccfb.beepTime 60ths of a second (can adjust it in the ccfb structure
    408  *  in an ioctl().  The sample is playing in left+right aud0+aud1 hardware
    409  *  channels all the time, just the volume is off when the beep isn't
    410  *  heard.  So here we just turn on the volume (ccfb.beepVolume, it can
    411  *  also be set by ioctl() call) and set the timer (ccfb.beepTime can
    412  *  be set by ioctl() as well).  The cc_vbl() routine counts down the
    413  *  timer and shuts off the volume when it reaches zero.
    414  */
    415 void
    416 cc_bell ()
    417 {
    418   custom.aud[0].vol = ccfb.beepVolume;
    419   custom.aud[1].vol = ccfb.beepVolume;
    420   ccfb.beepTimer = ccfb.beepTime;
    421 }
    422 
    423 /*
    424  * void cc_vbl(void);
    425  *
    426  * synopsis:
    427  *  vertical blank service routine for the console.
    428  *  provides the following:
    429  *   samples mouse counters and positions mouse sprite
    430  *   samples joystick inputs
    431  *   counts down mouseblanker timer and blanks mouse if it is time
    432  *   counts down screenblanker timer and blanks if it is time
    433  *   counts down audio beep timer and shuts of the volume if the beep is done
    434  *   unblanks mouse/screen if mouse is moved
    435  * not implemented yet:
    436  *  it should adjust color palette in copper list over time to effect
    437  *   display beep.
    438  *
    439  * There's black magic going on here with assembly-in-C.. Since this
    440  * is an interrupt handler, and it should be fast, ignore the obscure but
    441  * probably fast processing of the mouse for now...
    442  */
    443 void
    444 cc_vbl ()
    445 {
    446   u_short w0, w1;
    447   u_char *b0 = (u_char *)&w0, *b1 = (u_char *)&w1;
    448   SPRITEPTR *p = (SPRITEPTR *)ccfb.pointer;
    449 
    450   ccfb.lastMouseH = ccfb.mouseH;
    451   ccfb.lastMouseV = ccfb.mouseV;
    452 
    453   /* horizontal mouse counter */
    454   w1 = custom.joy0dat;
    455   b0[1] = ccfb.mouseH;		/* last counter val */
    456   ccfb.mouseH = b1[1];		/* current is now last */
    457   b1[1] -= b0[1];		/* current - last */
    458   b1[0] = (b1[1] & 0x80) ? 0xff : 0x00; /* ext.w */
    459   ccfb.mouseX += w1;
    460   if (ccfb.mouseX < 0) ccfb.mouseX = 0;
    461   if (ccfb.mouseX > ccfb.fb_width-1) ccfb.mouseX = ccfb.fb_width-1;
    462 
    463   /* vertical mouse counter */
    464   w1 = custom.joy0dat;
    465   b1[1] = b1[0];
    466   b0[1] = ccfb.mouseV;
    467   ccfb.mouseV = b1[1];
    468   b1[1] -= b0[1];
    469   b1[0] = (b1[1] & 0x80) ? 0xff : 0x00; /* ext.w */
    470   ccfb.mouseY += w1;
    471   if (ccfb.mouseY < 0) ccfb.mouseY = 0;
    472   if (ccfb.mouseY > ccfb.fb_height-1) ccfb.mouseY = ccfb.fb_height-1;
    473 
    474   /* mouse buttons (should renumber them, middle button should be #2!) */
    475   ccfb.mouseb1 = (ciaa.pra & (1<<6)) ? 0 : !0;
    476   ccfb.mouseb2 = (custom.pot1dat & (1<<2)) ? 0 : !0;
    477   ccfb.mouseb3 = (custom.pot1dat & (1<<0)) ? 0 : !0;
    478 
    479   /* position pointer sprite */
    480   w0 = ccfb.mouseY >> 1;
    481   b0[1] += 0x24;
    482   p->header[0] = b0[1];
    483   b0[1] += 16;
    484   p->header[2] = b0[1];
    485 
    486   w0 = ccfb.mouseX >> 1;
    487   w0 += 120;
    488   if (w0 & 1) p->header[3] |= 1; else p->header[3] &= ~1;
    489   w0 >>= 1;
    490   p->header[1] = b0[1];
    491 
    492   /* joystick #1 */
    493   ccfb.joy0 = 0;
    494   w0 = custom.joy1dat;
    495   w1 = w0 >> 1;
    496   w1 ^= w0;
    497   if (w1 & (1<<9)) ccfb.joy0 |= JOYLEFT;
    498   if (w1 & (1<<1)) ccfb.joy0 |= JOYRIGHT;
    499   if (w1 & (1<<8)) ccfb.joy0 |= JOYUP;
    500   if (w1 & (1<<0)) ccfb.joy0 |= JOYDOWN;
    501   if ( (ciaa.pra & (1<<7)) == 0 ) ccfb.joy0 |= JOYBUTTON;
    502 
    503   /* joystick #2 (normally mouse port) */
    504   ccfb.joy1 = 0;
    505   w0 = custom.joy0dat;
    506   w1 = w0 >> 1;
    507   w1 ^= w0;
    508   if (w1 & (1<<9)) ccfb.joy1 |= JOYLEFT;
    509   if (w1 & (1<<1)) ccfb.joy1 |= JOYRIGHT;
    510   if (w1 & (1<<8)) ccfb.joy1 |= JOYUP;
    511   if (w1 & (1<<0)) ccfb.joy1 |= JOYDOWN;
    512   if ( (ciaa.pra & (1<<6)) == 0 ) ccfb.joy1 |= JOYBUTTON;
    513 
    514   /* only do screenblanker/mouseblanker/display beep if screen is enabled */
    515   if (ccfb.enableFlag) {
    516     /* handle screen blanker */
    517     if (ccfb.screenBlank) {
    518       COPPERLIST *c1 = (COPPERLIST *)ccfb.cop1, *c2 = (COPPERLIST *)ccfb.cop2;
    519       ccfb.screenBlank--;
    520       if (!ccfb.screenBlank) {
    521 	custom.dmacon = DMAF_RASTER | DMAF_SPRITE;
    522 	c1->colors[1] = c2->colors[1] = 0; /* make screen BLACK */
    523       }
    524     }
    525 
    526     /* handle mouse blanker */
    527     if (ccfb.mouseBlank) {
    528       ccfb.mouseBlank--;
    529       if (!ccfb.mouseBlank) custom.dmacon = DMAF_SPRITE;
    530     }
    531     else if (ccfb.lastMouseH != ccfb.mouseH || ccfb.lastMouseV != ccfb.mouseV) {
    532       cc_unblank();
    533       ccfb.mouseBlank = ccfb.mouseTime;
    534       custom.dmacon = DMAF_SETCLR | DMAF_SPRITE;
    535     }
    536 
    537     /* handle visual beep (not implemented yet) */
    538   }
    539 
    540   /* handle audible beep */
    541   if (ccfb.beepTimer) ccfb.beepTimer--;
    542   if (!ccfb.beepTimer) custom.aud[0].vol = custom.aud[1].vol = 0;
    543 }
    544 
    545 /* useful function for debugging.. */
    546 int
    547 amiga_mouse_button (num)
    548      int num;
    549 {
    550   switch (num)
    551     {
    552     case 1:
    553       return ccfb.mouseb1;
    554 
    555     case 2:
    556       return ccfb.mouseb2;
    557 
    558     case 3:
    559       return ccfb.mouseb3;
    560 
    561     default:
    562       return 0;
    563     }
    564 }
    565 
    566 
    567 /* Initialize hardware.
    568  * Must point g_display at a grfinfo structure describing the hardware.
    569  * Returns 0 if hardware not present, non-zero ow.
    570  */
    571 cc_init(gp, ad)
    572 	struct grf_softc *gp;
    573 	struct amiga_device *ad;
    574 {
    575   register struct ccfb *fb = &ccfb;
    576   struct grfinfo *gi = &gp->g_display;
    577   u_char *fbp, save;
    578   int fboff, fbsize;
    579   int s;
    580 
    581   /* if already initialized, fail */
    582   if (fb->fb) return 0;
    583 
    584   /* disable dma */
    585   custom.dmacon  = DMAF_BLTDONE
    586     | DMAF_BLTNZERO | DMAF_BLITHOG | DMAF_BLITTER | DMAF_DISK
    587       | DMAF_AUD3 | DMAF_AUD2 | DMAF_AUD1 | DMAF_AUD0;
    588 
    589   fb->mouseBlank = fb->mouseTime;
    590   fb->screenBlank = fb->screenTime;
    591 
    592   /* testing for the result is really redundant because chipmem_steal
    593      panics if it runs out of memory.. */
    594   fbsize = (fb->fb_width >> 3) * fb->fb_height * fb->fb_z;
    595   if (! (fb->fb = (u_char *) chipmem_steal (fbsize))
    596       || !(fb->cop1 = (u_short *) chipmem_steal (sizeof(COPPERLIST)))
    597       || !(fb->cop2 = (u_short *) chipmem_steal (sizeof(COPPERLIST)))
    598       || !(fb->pointer = (u_short *)chipmem_steal (sizeof(SPRITEPTR)))
    599       )
    600     return 0;
    601 
    602   /* clear the display. bzero only likes regions up to 64k, so call multiple times */
    603   for (fboff = 0; fboff < fbsize; fboff += 64*1024)
    604     bzero (fb->fb + fboff, fbsize - fboff > 64*1024 ? 64*1024 : fbsize - fboff);
    605 
    606   /* init the audio beep */
    607   initbeep(fb);
    608   /* initialize the sprite pointer */
    609   initpointer(fb);
    610 
    611   /* initialize the copper lists  */
    612   initcop (fb->cop1, fb->cop2, 0, fb);
    613   initcop (fb->cop2, fb->cop1, 1, fb);
    614 
    615   /* start the new display */
    616 
    617   /* ok, this is a bit rough.. */
    618   /* mtk: not any more! :-) */
    619   /* mykes: phew, thanks :-) */
    620   s = splhigh ();
    621 
    622   /* install dummy, to get display going (for vposr to count.. ) */
    623   custom.cop1lc  = (void *) ((unsigned long)fb->cop1 - (unsigned long) CHIPMEMADDR);
    624   custom.copjmp1 = 0;
    625 
    626   /* enable DMA (so the copperlists are executed and eventually
    627      cause a switch to an interlaced display on system not already booting that
    628      way. THANKS HAMISH for finding this bug!!) */
    629   custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER |
    630 		  DMAF_COPPER | DMAF_SPRITE | DMAF_AUD0 | DMAF_AUD1;
    631 
    632   /* this is real simple:  wait for LOF bit of vposr to go high - then start
    633      the copper list! :-) */
    634   while (custom.vposr & 0x8000);
    635   while (!(custom.vposr & 0x8000));
    636 
    637   custom.cop1lc  = (void *) ((unsigned long)fb->cop1 - (unsigned long) CHIPMEMADDR);
    638   custom.copjmp1 = 0;
    639 
    640   custom.intreq = INTF_VERTB;
    641 
    642   splx (s);
    643 
    644 #if 0
    645   /* tame the blitter. Copying one word onto itself should put it into
    646      a consistent state. This is black magic... */
    647   custom.bltapt =
    648     custom.bltbpt =
    649       custom.bltcpt =
    650         custom.bltdpt = 0;
    651   custom.bltamod =
    652     custom.bltbmod =
    653       custom.bltcmod =
    654         custom.bltdmod = 0;
    655   custom.bltafwm =
    656     custom.bltalwn = 0xffff;
    657   custom.bltcon0 = 0x09f0;
    658   custom.bltcon1 = 0;
    659   custom.bltsize = 1;
    660 #endif
    661 
    662   /* enable VBR interrupts. This is also done in the serial driver, but it
    663      really belongs here.. */
    664   custom.intena = INTF_SETCLR | INTF_VERTB; /* under amigaos, INTF_INTEN is needed */
    665 
    666 #if 0
    667 #ifdef DEBUG
    668   /* prove the display is up.. */
    669   for (fboff = 0; fboff < fbsize; fboff++)
    670     {
    671       fb->fb[fboff] = 0xff;
    672       DELAY(10);
    673     }
    674   for (fboff = 0; fboff < fbsize; fboff++)
    675     {
    676       fb->fb[fboff] = 0;
    677       DELAY(10);
    678     }
    679 #endif
    680 #endif
    681 
    682   gp->g_data = (caddr_t) fb;
    683   gi->gd_regaddr = 0xdff000;
    684   gi->gd_regsize = sizeof (custom);
    685 
    686   gi->gd_fbaddr  = fb->fb - (u_char *) CHIPMEMADDR;
    687 #if 0
    688   /* mykes kludges here to make gi look like 1 bitplane */
    689   gi->gd_fbsize  = fbsize/2;
    690 #else
    691   /* don't see why we should kludge here.. we have
    692      disp_z to indicate the real depth of the display */
    693   gi->gd_fbsize  = fbsize;
    694 #endif
    695 
    696   gi->gd_colors = 1 << fb->disp_z;
    697   gi->gd_planes = fb->disp_z;
    698 
    699   gi->gd_fbwidth  = fb->fb_width;
    700   gi->gd_fbheight = fb->fb_height;
    701   gi->gd_fbx	  = fb->fb_x;
    702   gi->gd_fby	  = fb->fb_y;
    703   gi->gd_dwidth   = fb->disp_width;
    704   gi->gd_dheight  = fb->disp_height;
    705   gi->gd_dx	  = fb->disp_x;
    706   gi->gd_dy	  = fb->disp_y;
    707 
    708   gp->g_regkva = 0;		/* builtin */
    709   gp->g_fbkva  = fb->fb;
    710 
    711   fb->enableFlag = !0;
    712   return(1);
    713 }
    714 
    715 cc_config(gp, di)
    716 	register struct grf_softc *gp;
    717 	struct grfdyninfo *di;
    718 {
    719   register struct ccfb *fb = &ccfb;
    720   struct grfinfo *gi = &gp->g_display;
    721   u_char *fbp, save;
    722   int fboff, fbsize;
    723   int s;
    724 
    725   /* bottom missing... */
    726 
    727 }
    728 
    729 /*
    730  * Change the mode of the display.
    731  * Right now all we can do is grfon/grfoff.
    732  * Return a UNIX error number or 0 for success.
    733  */
    734 cc_mode(gp, cmd, arg)
    735 	register struct grf_softc *gp;
    736 	int cmd;
    737 	void *arg;
    738 {
    739   switch (cmd)
    740     {
    741     case GM_GRFON:
    742       ccfb.enableFlag = !0;
    743       ccfb.screenBlank = ccfb.screenTime;
    744       ccfb.mouseBlank = ccfb.mouseTime;
    745       custom.dmacon  = DMAF_SETCLR | DMAF_RASTER | DMAF_COPPER | DMAF_SPRITE;
    746       return 0;
    747 
    748     case GM_GRFOFF:
    749       ccfb.enableFlag = 0;
    750       custom.dmacon  = DMAF_RASTER | DMAF_COPPER | DMAF_SPRITE;
    751       return 0;
    752 
    753     case GM_GRFCONFIG:
    754       return cc_config (gp, (struct grfdyninfo *) arg);
    755 
    756     default:
    757       break;
    758     }
    759 
    760   return EINVAL;
    761 }
    762 
    763 #endif
    764 
    765