[net.micro.cbm] C-Power graphics package part 4 of 6

prindle@nadc (10/21/86)

From: prindle@NADC

/* Graphics Package - Part 1
** Filename: grafpak1.c
** Author:   Mark R. Rinfret
** Date:     12/14/85
**
** Description:
**
** This package provides access to the
** hardware graphics capabilities of the
** Commodore 64 computer.  The routines
** are styled somewhat after the features
** provided by the Commodore Super Expander.
** The graphics area has been restricted to
** bank 2 (32768) for initial development.
**
** Routines in this package:
**   colors   - sets colors for pens 0-3
**   drawchar - puts a character on graphics screen
**   drawtext - puts string on graphics screen, with rotation
**   ginit    - initializes graphics routines
**   graphics - sets graphics mode
**   pset     - sets/clears a single pixel
**   line     - draws line segments
*/

#include <strings.h>
#include <math.h>
#include "grafpak.h"

/* Mask and bit value definitions.
** Mask arrays are selected by graphics mode (1 or 2) 
*/

static char gmask1[8] = {/* hi-res bit masks */
  0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};

static char gmask2[8] = {/* multi-color bit masks */
  0xfc,0xfc,0xf3,0xf3,0xcf,0xcf,0x3f,0x3f};

/* Bit value arrays are selected by pen number (1-3) */

static char hrbit1[8] = {/* pen 1 bit values */
  1,2,4,8,16,32,64,128};

static char mcbit1[8] = {/* pen 1 multicolor bit values */
  0x01,0x01,0x04,0x04,0x10,0x10,0x40,0x40};

static char mcbit2[8] = {/* pen 2 multicolor bit values */
  0x02,0x02,0x08,0x08,0x20,0x20,0x80,0x80};

static char mcbit3[8] = {/* pen 3 bit values */
  0x03,0x03,0x0c,0x0c,0x30,0x30,0xc0,0xc0};

/* Quick-lookup bitmap y-offset table */
static unsigned y_offset[25] = {
  0,320,640,960,1280,1600,1920,2240,2560,
  2880,3200,3520,3840,4160,4480,4800,
  5120,5440,5760,6080,6400,6720,7040,
  7360,7680
  };

/* Quick-lookup color memory y-offset table.  The index is expected to be
** adjusted to row number.  A y pixel coordinate can be converted to a row
** number simply by dividing by 8 (right shift 3)
**/
static unsigned y2color[25] = {
  0,40,80,120,160,200,240,280,
  320,360,400,440,480,520,560,
  600,640,680,720,760,800,840,
  880,920,960
  };

/* Set colors
** Parameters are:
**   border
**   background
**   pen 1
**   pen 2
**   pen 3
**
** Negative value => no change
 */
colors(bo,bg,p1,p2,p3)
int bo,bg,p1,p2,p3;
{
  if (bo>=0) poke(53280,bo);
  if (bg>=0) {
    pencolor[0]=bg;
    poke(53281,bg);
  }
  if (p1>=0) {
    pencolor[1] = p1;
    poke(COLOR,p1);
  }
  if (p2>=0) pencolor[2] = p2;
  if (p3>=0) pencolor[3] = p3;

}
  
/* Initialize Graphics Package */
ginit()
{
  gbank = 2;
  vbase = gbank * 16384; /* VIC-II base address */
  gbase = vbase + 8192;  /* only allowed location in bank 2 */
  sbase = vbase;
  gmode = 0;
  pencolor[0] = 1;       /* white background */
  pencolor[1] = 0;       /* black foreground */
  pencolor[2] = 5;       /* green multi-color 1 */
  pencolor[3] = 6;       /* blue multi-color 2 */
  cur_x = 0;
  cur_y = 0;
}

graphics(mode,clear)
unsigned mode,clear;
{
  if (mode<0 || mode>2) {
    textmode();
    printf("\nIllegal mode: %d\n",mode);
    exit();
  }
  gmode = mode;          /* save mode */
  if (mode==0) textmode();
  else {
    if (mode==1) hires();
    else multicolor();
  }
  if (clear) {
    if (mode==0) putchar(147);
    else {
      bmfill(0);
      cfill();   /* fill color memory */
    }
  }
}

/* Fill bit-map area with value */
bmfill(v)
unsigned v;
{
  register char *p;
  register char v1; /* local copy for speed */
  register unsigned i;

  v1 = v;           /* local copy to zero page */

  p = (char *) gbase;
  for (i=0;i<8000;i++)
    *p++ = v1;
}

/* Fill color memory with default colors. */
cfill()
{
  register char *p; /* Color memory pointer */
  register char c;
  register unsigned i;

  p = (char *) sbase; /* 1st, do screen memory */

/* Upper four bits are foreground, lower 4 bits are background */

  if (gmode==1)   
    c = (pencolor[1] << 4) | pencolor[0]; 
  else
    c = (pencolor[1] << 4) | pencolor[2];

  for (i=0;i<1000;i++)
    *p++ = c;

  if (gmode==2) { /* Multi-color mode? */
    p = (char *) COLORMEM; /* color ram */
    c = pencolor[3];
    for (i=0;i<1000;i++)
      *p++ = c;
  }
}
  
/* Set graphics screen base address */

unsigned vb,bb;
{
  register unsigned bank;

  bank = vb / 16384; /* calculate bank number */

  poke(C2DDRA,peek(C2DDRA) | 3); /* bits 0,1 are outputs */
  poke(CI2PRA,(peek(CI2PRA) & 252) | (3-bank));
  if (bb & 0x2000) { /* upper half of bank? */
    poke(VMCSB,peek(VMCSB) | 8);   /* set bit 3 */
  }
  else
    poke(VMCSB,peek(VMCSB) & 247); /* clear bit 3 */    
}

/* Set Screen Memory Location 
** In text mode, screen memory is where characters printed to the screen
** are displayed.  In bit-mapped mode, this area is used as another
** color memory area.  The value computed is the offset, a multiple of 1024,
** from the base address of the current bank being accessed by the VIC-II
** chip.
*/
setscreen(vb,sa)
unsigned vb,sa;
{
  register unsigned a;

  a = ((sa-vb)/64); /* (sa-vb)/1024 << 4 */
  poke(VMCSB,(peek(VMCSB) & 15) | a);
}

/* Set Text Graphics mode */
  setmap(0,1024);
  setscreen(0,1024);
  poke(VICCTL,peek(VICCTL) & 223);   
  poke(COLOR,pencolor[1]);
}

/* Set Multi-Color Graphics Mode */
multicolor()
{
  setmap(vbase,gbase);
  setscreen(vbase,sbase);
  poke(VICCTL,peek(VICCTL) | 32);
  poke(VICMC,peek(VICMC) | 16);
  poke(53281,pencolor[0]);
  aspect = 160.0 / 200.0;  /* compute aspect ratio */
}


/* Set Hi-Res Graphics Mode */
hires()
{
  setmap(vbase,gbase);
  setscreen(vbase,sbase);
  poke(VICCTL,peek(VICCTL) | 32);
  poke(VICMC,peek(VICMC) & 239); /* disable multi-color mode */
  poke(53281,pencolor[0]); /* background color reg */
  aspect = 320.0 / 200.0;  /* compute aspect ratio */

/* Convert pixel coordinate to byte offset.
** The BASIC formula from the PRG is:
**   row  = int(y/8)
**   char = int(x/8)
**   line = y and 7
**   loc  = base+row*320+char*8+line
**
** However, note that we can avoid the
** division and multiplication by using
** a table-lookup for row offset and by
** proper use of masking.
**
** IMPORTANT:  This routine only computes the OFFSET to the graphic byte,
** NOT its actual location.  You must add the graphics base address to 
** get the actual location.
*/
gofst(x,y)
unsigned x,y;
{
  register unsigned loc;
  
  loc = y_offset[y >> 3] + /* row offset */
    (x & 0xfff8) +         /* character offset */

  return loc;
}
 
/* Draw one pixel.
** Parameters:
**   0 <= xx  <= 319 (hi-res)
**   0 <= xx  <= 159 (multi-color)
**   0 <= yy  <= 199
**   0 <= pen <= 3
*/
pset(xx,yy,pen)
unsigned xx,yy,pen;
{
  register char bit,val,mask;
  register unsigned x,y;
  register unsigned loc,ofst;

  x=xx;  y=yy;
  if (gmode==2) x = x << 1; /* adjust for multi-color (x=x*2) */
  if (x<0 || x>319 || y<0 || y>199) return;

  cur_x = xx;               /* set current location */  
  cur_y = yy;
  ofst = gofst(x,y);        /* get pixel offset */
  loc = gbase + ofst;       /* true pixel byte address */
  bit = 7-(x & 7);          /* compute bit number */
  if (gmode==1) {           /* Hi-res */
    if (pen==0) val=0;      /* clearing bit */
    mask = gmask1[bit];     /* get pixel mask */
    poke(loc,(peek(loc) & mask) | val); /* set/clear bit */    
    y = y>>3;               /* divide by 8 */
    loc = sbase + y2color[y] + (x>>3);
    poke(loc,pencolor[0] + (pencolor[1] << 4));
  }
  else {                    /* Multi-color */ 
    switch(pen) {

    case 0: val=0;
            break;

    case 1: val=mcbit1[bit];
            break;

    case 2: val=mcbit2[bit];
            break;

    case 3: val=mcbit3[bit];
            break;

    default: val=0;
    }
  
    mask = gmask2[bit];
    poke(loc,(peek(loc) & mask) | val); /* set/clear bits */
    y = y>>3;               /* y = y/8 */
    x = x>>3;               /* x = x/8 */
    if (pen==3) {
      loc = COLORMEM + y2color[y] + x;
      poke(loc,pencolor[3]);
    }
    else if (pen>0){
      loc = sbase + y2color[y] + x;      
      poke(loc,(pencolor[1]<<4) | pencolor[2]);
    }
  }
      
}
/* Bresenham's line drawing algorithm.
** Draw a line between two coordinate pairs.
** This routine was adapted from an article appearing
** in the November '85 issue of BYTE Magazine.
*/
 
line(x1,y1,x2,y2,pen)
unsigned x1,y1,x2,y2,pen;
{
  register int x,y,z,a,b,d,dx,dy;
  register int deltap,deltaq;

  dx = abs(x2-x1);
  dy = abs(y2-y1);

  if (dy <= dx) {   /* Slope <= 1 */
    x = x1;              /* initialize x */
    y = y1;          /* initialize y */
    z = x2;          /* set sentinel in  x direction */
    
    if (x1 <= x2) a = 1; /* x increases */
    else a = -1;         /* x decreases */
    
    if (y <= y2) b = 1;  /* y increases */
    else b = -1;         /* y decreases */
    
    deltap = dy + dy;    /* initialize decision function and deltas */
    d = deltap - dx;
    deltaq = d - dx;
      
    /* Locate and plot points */
    pset(x,y,pen);
    for (;x != z;) {
      x += a;
      if (d < 0 ) d += deltap;
      else {
        y += b;
        d += deltaq;
      }
      pset(x,y,pen);
    }
  }
  else {                 /* dx <= dy so view x as function of y */
    y = y1;
    x = x1;
    z = y2;             /* set sentinel in y direction */
    
    /* Now set y increment */
    if (y1 <= y2) a = 1; /* y increases */
    else a = -1;         /* y decreases */

    /* Now set x increment */
    if (x1 <= x2) b = 1; /* x increases */
    else b = -1;         /* x decreases */
    
    /* Initialize decision function and its deltas */
    deltap = dx + dx;
    d = deltap - dy;
    deltaq = d - dy;
    
    /* Locate and plot points */
    pset(x,y,pen);        /* first point */
    for (;y != z;) {
      y += a;
      if (d < 0) d += deltap;
      else {
        x += b;
        d += deltaq;        
      }
      pset(x,y,pen);
    }
  }
}

/* Draw one character in any mode.
** Character ROM map:
---------------------------------------------------
            Set 1

**   53248: Upper-case & Misc.    0- 63  No SHIFT
**          Graphics/Graphics    64-127  SHIFT/CBM

**   54272  Upper-case & Misc.  128-191  No SHIFT    reverse
**          Graphics/Graphics   192-255  SHIFT/CBM   field
---------------------------------------------------
**   55296  Lower-case & Misc.    0- 63  No SHIFT
**          Upper-case/Graphics  64-127  SHIFT/CBM

**   56320  Lower-case & Misc.  128-191  No SHIFT    reverse
**          Upper-case/Graphics 192-255  SHIFT/CBM   field
---------------------------------------------------
**
** Note:  this routine currently only provides access to character set
** 2 and does not address reversed characters.  Its features should be
** expanded to provide both character sets (complete).
*/
drawchar(x,y,c,pen)
unsigned x,y,c,pen;
{
  register char c1;                /* translated character */
  register unsigned cbase;         /* base address of character set */
  register char *cp;
  register unsigned i,j;
  char pixels[8];                  /* matrix for one character */

  if (gmode==0) {                  /* text mode? */
    }
  else {                           /* hi-res or multi-color */
    cbase = 55296;                 /* Unconditional use of set 2 */
    c1=c;
    if (c1 < 32) c1 += 64;         /* translate to screen code */
    else if (c1>63 && c1<92) c1-=64;
    else if (c1>191 && c1<223) c1 -= 128;     
    cp = (char *) (cbase + c1*8);  /* compute rom location */

    poke(56334,peek(56334) & 254); /* disable timer (interrupts) */
    poke(1,peek(1) & 251);         /* map in character rom */

    for (i=0;i<8;i++) pixels[i] = *cp++;

    poke(1,peek(1) | 4);           /* map out character rom */
    poke(56334,peek(56334) | 1);   /* enable timer */

    for (i=0;i<8;i++) {            /* plot character */
      c1 = pixels[i];              /* get row of character */
      for (j=0;j<8;j++) {          /* do dots in row */
        if (c1 & 0x80)             /* pixel set? */
          pset(x+j,y+i,pen);
        c1 = c1 << 1;
      }
    }
  }
}

/* Output a complete text string in current mode.
** Text may be rotated by specifying a non-zero
** angle parameter. 
** Parameters:
**   x,y:   starting coordinate for top left of first character
**   s:     null terminated text string
**   angle: rotation angle, in degrees
**   pen:   pen number (0-3) to be used
*/
drawtext(x,y,s,angle,pen)
unsigned x,y; char *s; float angle; unsigned pen;
{
  register char c,*p;
  register unsigned xx,yy;
  float xradius,yradius,angl,fx,fy;

  xx=x; yy=y;
  fx=x; fy=y;              /* convert to floating point */
  yradius = 8.0;
  angl = angle / RADCON;   /* convert angle to radians */
  for (p=s;(c=*p++);) {    /* loop thru string */
    drawchar(xx,yy,c,pen); /* draw character */
    xradius = yradius * aspect;
    xx = fx + xradius * cos(angl);
    yy = fy + yradius * sin(angl);
    yradius += 8.0;         /* compute new radius */
  }
}