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