#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #define TUBE_PTS 16 #define TUBE_BACK .15 #define PICKSEG .1 #define MAXPSHOTS 8 #define MAXESHOTS 4 #include "cg6.h" static const char *fbpath = "/dev/cgsix0"; static const char *kbpath = "/dev/kbd0"; static int fbfd; static int kbfd; static int spfd; static volatile struct cg6fbc *fb; static volatile struct brooktree *bt; static volatile unsigned char *vram; static int lastfg; #define MIDX (1152/2) #define MIDY (900/2) typedef struct xy XY; typedef struct tube TUBE; typedef struct tubedata TUBEDATA; typedef struct enemytype ENEMYTYPE; typedef struct enemychain ENEMYCHAIN; typedef struct enemy ENEMY; typedef struct shottype SHOTTYPE; typedef struct shot SHOT; typedef struct gpt GPT; struct xy { double x; double y; } ; struct gpt { int colour; XY pt; } ; struct shottype { void (*new)(SHOT *); void (*destroy)(SHOT *); void (*draw)(SHOT *); int (*move)(SHOT *); } ; struct shot { SHOT *link; int seg; double loc; const SHOTTYPE *type; } ; struct enemychain { int count; ENEMY *chain; } ; struct enemy { ENEMY *link; const ENEMYTYPE *type; void *priv; int seg; double loc; ENEMYCHAIN *chain; } ; struct enemytype { int colx; int privsize; void (*draw)(ENEMY *); int (*tick)(ENEMY *); void (*new)(ENEMY *); void (*destroy)(ENEMY *); } ; struct tubedata { XY pts[TUBE_PTS]; XY centre; unsigned char open; } ; struct tube { TUBEDATA data; XY ipts[TUBE_PTS]; XY segmid[TUBE_PTS]; double seglen[TUBE_PTS]; } ; #define RPSIZE 64 static unsigned long int randpool[RPSIZE]; static int rp; static int rmp; static int drawbit; static int wantframe; static unsigned char lastsp; static int pseg; static int pws; #define NPWS 8 static TUBE *tube; static TUBE the_tube; static int tubeno; #define COL_SKIP (-1) #define COL_K 0 #define COL_R 1 #define COL_G 2 #define COL_Y 3 #define COL_B 4 #define COL_M 5 #define COL_C 6 #define COL_W 7 #define COL_TUBE 8 #define COL_CLAW 9 #define COL_PENDING 10 #define COL_FLIPPER 11 #define COL_TANKER 12 #define COL_SPIKE 13 #define COL_SPIKER 14 #define COL_SPIKETIP 15 #define COL_PULSAR 16 #define COL_PSRING 17 #define COL__N 18 /* pseudo "colours" that get converted to one of the above */ #define COL_PSFILL 19 static unsigned char colours[COL__N]; static SHOT *shots; static int npshots; static int level; static int firing; static int zapped; static int zapcount; static ENEMYCHAIN epend; static ENEMYCHAIN erising; static ENEMYCHAIN eend; static int maxenemies; static const SHOTTYPE shottype_p; static const ENEMYTYPE enemytype_flip; static const ENEMYTYPE enemytype_pend; static const TUBEDATA tubedata[] = { #include "tubedata" }; #define NTUBES (sizeof(tubedata)/sizeof(tubedata[0])) #define CLAWPTS 8 static const XY claws[NPWS][CLAWPTS] = { #include "claws" }; static const GPT pshot_pts[] = { #include "pshot" }; #define PSHOT_GN (sizeof(pshot_pts)/sizeof(pshot_pts[0])) static const GPT flip_pts[] = { #include "flipper" }; #define FLIP_GN (sizeof(flip_pts)/sizeof(flip_pts[0])) #if TUBE_PTS & (TUBE_PTS-1) #error Need to fix TUBEINC and TUBEDEC for TUBE_PTS not a power of two! #else #define TUBEINC(x) (((x)+1)&(TUBE_PTS-1)) #define TUBEDEC(x) (((x)+TUBE_PTS-1)&(TUBE_PTS-1)) #endif static __inline__ XY alphaxy(XY, double, XY, double) __attribute__((__const__)); static XY alphaxy(XY a, double fa, XY b, double fb) { return((XY){ .x = ((a.x * fa) + (b.x * fb)) / (fa + fb), .y = ((a.y * fa) + (b.y * fb)) / (fa + fb) }); } static __inline__ double hypotxy(XY) __attribute__((__const__)); static double hypotxy(XY a) { return(hypot(a.x,a.y)); } static __inline__ XY addxy(XY, XY) __attribute__((__const__)); static XY addxy(XY a, XY b) { return((XY){ .x = a.x + b.x, .y = a.y + b.y }); } static __inline__ XY subxy(XY, XY) __attribute__((__const__)); static XY subxy(XY a, XY b) { return((XY){ .x = a.x - b.x, .y = a.y - b.y }); } static __inline__ XY scalexy(XY, double) __attribute__((__const__)); static XY scalexy(XY a, double f) { return((XY){ .x = a.x * f, .y = a.y * f }); } static __inline__ XY rot90(XY) __attribute__((__const__)); static XY rot90(XY a) { return((XY){ .x = -a.y, .y = a.x }); } #define zeroxy() ((XY){.x=0,.y=0}) static void setcmap(int bit) { int i; int v; bt->addr = 0; for (i=0;i<=255;i++) { v = bit ? (i >> 4) : i; bt->cmap = (v & 1) ? 0xff000000 : 0; bt->cmap = (v & 2) ? 0xff000000 : 0; bt->cmap = (v & 4) ? 0xff000000 : 0; } } static void drain(void) { while (fb->s & 0x10000000) ; } static void draw(void) { while ((fb->draw & 0xa0000000) == 0xa0000000) ; } static void setfg(int col) { if (col != lastfg) { fb->fg = 0x11 * col; lastfg = col; } } static void fbrect(int x1, int y1, int x2, int y2) { fb->arecty = y1; fb->arectx = x1; fb->arecty = y2; fb->arectx = x2; draw(); } static void fblinexy(XY a, XY b) { fb->aliney = (int)(MIDY-a.y); fb->alinex = (int)(MIDX+a.x); fb->aliney = (int)(MIDY-b.y); fb->alinex = (int)(MIDX+b.x); draw(); } static void fbpointxy(XY a) { fb->apointy = (int)(MIDY-a.y); fb->apointx = (int)(MIDX+a.x); draw(); } static void setupfb(void) { void *mrv; struct fbgattr a; fbfd = open(fbpath,O_RDWR,0); if (fbfd < 0) { fprintf(stderr,"%s: %s: %s\n",__progname,fbpath,strerror(errno)); exit(1); } if ( (ioctl(fbfd,FBIOGATTR,&a) < 0) || (a.fbtype.fb_type != FBTYPE_SUNFAST_COLOR) || (a.fbtype.fb_width != 1152) || (a.fbtype.fb_height != 900) || (a.fbtype.fb_depth != 8) ) { close(fbfd); fprintf(stderr,"%s: %s: not a cg6\n",__progname,fbpath); exit(1); } mrv = mmap(0,0x16000+a.fbtype.fb_size,PROT_READ|PROT_WRITE,MAP_SHARED,fbfd,0x70000000); if (mrv == MAP_FAILED) { fprintf(stderr,"%s: can't mmap %s: %s\n",__progname,fbpath,strerror(errno)); exit(1); } fb = mrv; bt = (void *)(0x2000+(unsigned char *)mrv); vram = 0x16000 + (unsigned char *)mrv; drawbit = 0; fb->bg = 0; fb->pixelm = ~0; fb->s = 0; fb->mode = MODE_NORMAL | MODE_COLOR8; fb->alu = ALU_NORMAL | ALU_FG; fb->clip = 0; fb->offx = 0; fb->offy = 0; fb->clipminx = 0; fb->clipminy = 0; fb->clipmaxx = 1151; fb->clipmaxy = 899; fb->pm = ~0; drain(); fb->clipminx = 0; fb->clipminy = 0; fb->clipmaxx = 1151; fb->clipmaxy = 899; lastfg = -1; } static void setupkb(void) { int type; int mode; kbfd = open(kbpath,O_RDWR,0); if (kbfd < 0) { fprintf(stderr,"%s: can't open %s: %s\n",__progname,kbpath,strerror(errno)); exit(1); } if ( (ioctl(kbfd,KIOCTYPE,&type) < 0) || (type != KB_SUN3) ) { close(kbfd); fprintf(stderr,"%s: %s isn't a type-3\n",__progname,kbpath); exit(1); } mode = 1; ioctl(kbfd,KIOCSDIRECT,&mode); mode = TR_UNTRANS_EVENT; ioctl(kbfd,KIOCTRANS,&mode); mode = 1; ioctl(kbfd,FIONBIO,&mode); firing = 0; zapped = 0; } static void setupspinner(void) { struct sockaddr_in sin; spfd = socket(AF_INET,SOCK_STREAM,0); if (spfd < 0) { fprintf(stderr,"%s: spinner socket: %s\n",__progname,strerror(errno)); exit(1); } bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); sin.sin_addr.s_addr = htonl(0x0a000201); /* 10.0.2.1 */ sin.sin_port = htons(22222); if (connect(spfd,(void *)&sin,sizeof(sin)) < 0) { fprintf(stderr,"%s: spinner connect: %s\n",__progname,strerror(errno)); exit(1); } /* 100Hz = 10000 us, and 10000 = 0x2710 */ write(spfd,"\x00\x00\x00\x00\x00\x00\x27\x10",8); } static unsigned long int rnd(void) { int prp; prp = rp++; if (rp >= RPSIZE) rp = 0; randpool[prp] += randpool[rp]; return(randpool[prp]); } static double rndf(void) { return(((int)(rnd()&0x3fffffff))/(double)0x40000000); } static void initrandom(void) { int i; struct timeval tv; randpool[0] = 1; randpool[1] = getpid(); gettimeofday(&tv,0); randpool[2] = tv.tv_sec; randpool[3] = tv.tv_usec; for (i=4;i> 1) ^ ((v & 1) ? 0xedb88320 : 0); randpool[rmp] = v; rmp ++; if (rmp >= RPSIZE) rmp = 0; } } static void getspinner(void) { unsigned char spval; int n; static int fctr = 0; n = read(spfd,&spval,1); if (n < 0) { fprintf(stderr,"%s: spinner read: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { fprintf(stderr,"%s: spinner EOF\n",__progname); exit(1); } spval >>= 1; if ((spval-lastsp) & 0x40) { n = (lastsp - spval) & 0x3f; pws -= n; while (pws < 0) { pws += NPWS; pseg --; if (pseg < 0) { if (tube->data.open) { pseg = 0; pws = 0; } else { pseg = TUBE_PTS - 1; } } } } else { n = (spval - lastsp) & 0x3f; pws += n; while (pws >= NPWS) { pws -= NPWS; pseg ++; if (tube->data.open) { if (pseg >= TUBE_PTS-1) { pseg = TUBE_PTS - 2; pws = NPWS - 1; } } else { if (pseg >= TUBE_PTS) pseg = 0; } } } lastsp = spval; if (fctr < 1) { wantframe = 1; fctr = 3; } else { fctr --; } } static void draw_tube_seg(int i, int j) { fblinexy(tube->data.pts[j],tube->data.pts[i]); fblinexy(tube->ipts[i],tube->ipts[j]); } static void draw_tube(void) { int i; setfg(colours[COL_TUBE]); for (i=TUBE_PTS-1;i>=0;i--) { if ((i == pseg) || (i == TUBEINC(pseg))) continue; fblinexy(tube->data.pts[i],tube->ipts[i]); } for (i=TUBE_PTS-1;i>0;i--) draw_tube_seg(i,i-1); if (! tube->data.open) draw_tube_seg(0,TUBE_PTS-1); } static void draw_player(void) { int i; const XY *cv; XY p0; XY px; XY dx; XY dy; XY pts[CLAWPTS]; setfg(colours[COL_CLAW]); fblinexy(tube->data.pts[pseg],tube->ipts[pseg]); fblinexy(tube->data.pts[TUBEINC(pseg)],tube->ipts[TUBEINC(pseg)]); cv = &claws[pws][0]; p0 = tube->data.pts[pseg]; px = tube->data.pts[TUBEINC(pseg)]; dx = subxy(px,p0); dy = rot90(dx); for (i=CLAWPTS-1;i>=0;i--) { pts[i] = addxy(p0, addxy( alphaxy(zeroxy(),8-cv[i].x,dx,cv[i].x), alphaxy(zeroxy(),8-cv[i].y,dy,cv[i].y) )); } for (i=CLAWPTS-1;i>0;i--) fblinexy(pts[i],pts[i-1]); fblinexy(pts[0],pts[CLAWPTS-1]); } static void draw_shots(void) { SHOT *s; for (s=shots;s;s=s->link) (*s->type->draw)(s); } static void draw_enemy_chain(ENEMYCHAIN *c) { ENEMY *e; for (e=c->chain;e;e=e->link) (*e->type->draw)(e); } static void draw_enemies(void) { draw_enemy_chain(&epend); draw_enemy_chain(&erising); draw_enemy_chain(&eend); } static void render(void) { drain(); setcmap(drawbit); drawbit = ! drawbit; fb->pm = drawbit ? 0xf0 : 0x0f; fb->alu = ALU_NORMAL | ALU_FG; setfg(0); fbrect(0,0,1151,899); drain(); fb->alu = ALU_NORMAL | ALU_FG | ALU_DST; draw_tube(); draw_player(); draw_shots(); draw_enemies(); } static void initcolours(void) { colours[COL_K] = 0; colours[COL_R] = 1; colours[COL_G] = 2; colours[COL_Y] = 3; colours[COL_B] = 4; colours[COL_M] = 5; colours[COL_C] = 6; colours[COL_W] = 7; colours[COL_TUBE] = 4; colours[COL_CLAW] = 3; colours[COL_PENDING] = 1; colours[COL_FLIPPER] = 1; colours[COL_TANKER] = 5; colours[COL_SPIKE] = 2; colours[COL_SPIKER] = 2; colours[COL_SPIKETIP] = 7; colours[COL_PULSAR] = 7; colours[COL_PSRING] = 3; } static void entertube(int n) { int i; int j; tubeno = n; tube = &the_tube; the_tube.data = tubedata[n]; for (i=0;iipts[i] = alphaxy(tube->data.pts[i],TUBE_BACK,tube->data.centre,1-TUBE_BACK); for (i=0;isegmid[i] = alphaxy(tube->data.pts[i],1,tube->data.pts[j],1); tube->seglen[i] = hypotxy(subxy(tube->data.pts[i],tube->data.pts[j])); } } static void add_shot(int seg, double loc, const SHOTTYPE *type) { SHOT *s; s = malloc(sizeof(SHOT)); s->seg = seg; s->loc = loc; s->type = type; s->link = shots; (*s->type->new)(s); shots = s; } static void player_fire(void) { if (npshots < MAXPSHOTS) add_shot(pseg,1,&shottype_p); } static int checkkb(void) { struct firm_event ev; int r; r = read(kbfd,&ev,sizeof(ev)); if (r == sizeof(ev)) { rnd_tweak(&ev,sizeof(ev)); switch ((ev.id & 0x7f) | ((ev.value == VKEY_UP) ? 0x80 : 0)) { default: printf("kbd: %d ",ev.id&0x7f); printf((ev.value==VKEY_UP)?"up\n":"dn\n"); break; /* L1 */ case 1 : break; case 1|0x80: break; /* F1 */ case 5 : break; case 5|0x80: break; /* F2 */ case 6 : break; case 6|0x80: break; /* Alternate */ case 19 : break; case 19|0x80: break; /* R1 */ case 21 : break; case 21|0x80: break; /* R3 */ case 23 : break; case 23|0x80: break; /* L3 */ case 25 : break; case 25|0x80: break; /* 1 */ case 30 : break; case 30|0x80: break; /* 2 */ case 31 : break; case 31|0x80: break; /* 3 */ case 32 : break; case 32|0x80: break; /* 4 */ case 33 : break; case 33|0x80: break; /* 5 */ case 34 : break; case 34|0x80: break; /* 6 */ case 35 : break; case 35|0x80: break; /* 7 */ case 36 : break; case 36|0x80: break; /* 8 */ case 37 : break; case 37|0x80: break; /* Backspace */ case 43 : break; case 43|0x80: break; /* L5 */ case 49 : break; case 49|0x80: break; /* L7 */ case 72 : break; case 72|0x80: break; /* L8 */ case 73 : break; case 73|0x80: break; /* L9 */ case 95 : break; case 95|0x80: break; /* L10 */ case 97 : break; case 97|0x80: break; /* R13 */ case 112 : break; case 112|0x80: break; /* R14 */ case 113 : break; case 113|0x80: break; /* R15 */ case 114 : break; case 114|0x80: break; /* Caps */ case 119 : firing = 1; break; case 119|0x80: firing = 0; break; /* Left */ case 120 : zapped = 1; break; case 120|0x80: break; /* space */ case 121 : break; case 121|0x80: break; /* Right */ case 122 : break; case 122|0x80: break; } return(1); } return(0); } static void moveshots(void) { SHOT *s; SHOT **sp; sp = &shots; while ((s = *sp)) { if ((*s->type->move)(s)) { (*s->type->destroy)(s); *sp = s->link; free(s); } else { sp = &s->link; } } } static void destroy_enemy(ENEMY *e) { (*e->type->destroy)(e); free(e->priv); e->chain->count --; free(e); } static void tick_enemy_chain(ENEMYCHAIN *c) { ENEMY *e; ENEMY **ep; ep = &c->chain; while ((e = *ep)) { if ((*e->type->tick)(e)) { *ep = e->link; destroy_enemy(e); } else { ep = &e->link; } } } static void tickenemies(void) { tick_enemy_chain(&eend); tick_enemy_chain(&erising); tick_enemy_chain(&epend); } static void tick(void) { moveshots(); tickenemies(); if (firing) player_fire(); } static void draw_graphic_sz_pt(const GPT *pts, int npts, XY xhat, double sz, XY p0) { int i; int c; XY dx; XY dy; XY cur; XY prev; dx = scalexy(xhat,sz/hypotxy(xhat)); dy = rot90(dx); cur = zeroxy(); for (i=0;i { cur = addxy(p0,addxy( scalexy(dx,pts[i].pt.x), scalexy(dy,pts[i].pt.y) )); c = pts[i].colour; switch (c) { case COL_PSFILL: c = (npshots < MAXPSHOTS) ? COL_B : COL_R; break; case COL_SKIP: break <"draw">; break; } setfg(colours[c]); fblinexy(prev,cur); } while (0); } } static void draw_graphic(const GPT *pts, int npts, int seg, double loc, XY xhat) { draw_graphic_sz_pt(pts,npts,xhat,tube->seglen[seg]*loc, alphaxy(tube->segmid[seg],loc,tube->data.centre,1-loc)); } static const ENEMYTYPE *choose_type(void) { return(&enemytype_flip); } static void enemy_new(ENEMY *e, ENEMYCHAIN *chain) { e->priv = malloc(e->type->privsize); (*e->type->new)(e); e->chain = chain; e->link = chain->chain; chain->chain = e; chain->count ++; } static void add_enemy_at_segment(int seg) { ENEMY *e; e = malloc(sizeof(ENEMY)); e->seg = seg; e->loc = TUBE_BACK; e->type = choose_type(); enemy_new(e,&erising); } static void nil_destroy(ENEMY *e __attribute__((__unused__))) { } static void pshot_new(SHOT *s __attribute__((__unused__))) { npshots ++; } static void pshot_destroy(SHOT *s __attribute__((__unused__))) { npshots --; } static void pshot_draw(SHOT *s) { draw_graphic(&pshot_pts[0],PSHOT_GN,s->seg,s->loc,((XY){.x=1,.y=0})); } static int pshot_move(SHOT *s) { s->loc -= .03; return(s->locloc < .99) { e->loc += .001; } else if (e->loc < 1) { e->loc = 1; } else { } return(0); } static void flip_new(ENEMY *e) { FLIP_PRIV *p; p = e->priv; p->move = flip_move_rise; p->flip = 0; p->v1 = 0; p->v2 = 0; } static void flip_destroy(ENEMY *e) { FLIP_PRIV *p; p = e->priv; if (p->flip) free(p->flip); } static void flip_draw(ENEMY *e) { FLIP_PRIV *p; p = e->priv; if (p->flip) { } else { draw_graphic(&flip_pts[0],FLIP_GN,e->seg,e->loc,subxy(tube->data.pts[TUBEINC(e->seg)],tube->data.pts[e->seg])); } } static int flip_tick(ENEMY *e) { return((*((FLIP_PRIV *)e->priv)->move)(e)); } static const ENEMYTYPE enemytype_flip = { COL_FLIPPER, sizeof(FLIP_PRIV), flip_draw, flip_tick, flip_new, flip_destroy }; typedef struct pend_priv PEND_PRIV; struct pend_priv { double a; double da; int seg; double ws; int fixed; } ; static void pend_set_seg(PEND_PRIV *p) { int n; double ws; int s; n = tube->data.open ? TUBE_PTS-1 : TUBE_PTS; ws = p->a * n; s = (int)ws; ws -= s; if (s < 0) s = 0; else if (s >= n) s = n-1; if (ws < 0) ws = 0; else if (ws > 1) ws = 1; p->seg = s; p->ws = ws; } static void pend_new(ENEMY *e) { PEND_PRIV *p; p = e->priv; p->a = rndf(); p->da = (rndf()-.5) / 40; pend_set_seg(p); p->fixed = 0; } static void pend_draw(ENEMY *e) { PEND_PRIV *p; XY te; p = e->priv; te = alphaxy(tube->data.pts[p->seg],1-p->ws,tube->data.pts[TUBEINC(p->seg)],p->ws); setfg(colours[COL_PENDING]); fbpointxy(alphaxy(tube->data.centre,1-e->loc,te,e->loc)); } static int pend_tick(ENEMY *e) { PEND_PRIV *p; p = e->priv; e->loc += p->fixed ? .0003 : .0001; if (e->loc >= TUBE_BACK) { e->loc = TUBE_BACK; if (erising.count+eend.count < maxenemies) { add_enemy_at_segment(p->seg); return(1); } } else if (e->loc >= PICKSEG) { p->ws = .5; } else { p->a += p->da; if (p->a < 0) p->a += 1; else if (p->a >= 1) p->a -= 1; p->da += (rndf() - rndf()) / 1000; if (p->da < -.6) p->da += 1; else if (p->da > .6) p->da -= 1; pend_set_seg(p); } return(0); } static const ENEMYTYPE enemytype_pend = { COL_PENDING, sizeof(PEND_PRIV), pend_draw, pend_tick, pend_new, nil_destroy }; static void clear_shots(void) { while (shots) { SHOT *s; s = shots; shots = s->link; (*s->type->destroy)(s); free(s); } if (npshots) abort(); } static void clear_echain(ENEMYCHAIN *list) { ENEMY *e; while ((e = list->chain)) { list->chain = e->link; destroy_enemy(e); } if (list->count) abort(); } static void clear_enemies(void) { clear_echain(&epend); clear_echain(&erising); clear_echain(&eend); } static int level_enemycount(int l) { l=l; return(10); } static int level_maxenemies(int l) { l=l; return(4); } static void create_enemies(void) { ENEMY *e; int i; for (i=level_enemycount(level);i>0;i--) { e = malloc(sizeof(ENEMY)); e->type = &enemytype_pend; e->seg = -1; e->loc = rndf() * TUBE_BACK; enemy_new(e,&epend); } } static void set_level(int n) { clear_shots(); clear_enemies(); level = n; entertube((level-1)%NTUBES); create_enemies(); zapcount = 0; maxenemies = level_maxenemies(level); } static void initgame(void) { set_level(1); } int main(void); int main(void) { setupfb(); setupkb(); setupspinner(); initrandom(); initcolours(); initgame(); while (1) { getspinner(); checkkb(); if (wantframe) { wantframe = 0; tick(); render(); } } }