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 */
}
}