/* Alvin Albrecht 8.2003 */ /* Compile using Z88DK and SPLIB2: zcc +zx -vn pacmen.c -o pacmen.bin -lsplib2 -lndos */ #include #include #include #include #pragma output STACKPTR=61440 #define INACTIVE 200 /* SPRITE PACK LIBRARY VARIABLES COPIED FROM HEADER FILE */ extern struct sp_Rect *sp_ClipStruct; #asm LIB SPCClipStruct ._sp_ClipStruct defw SPCClipStruct #endasm extern uchar *sp_NullSprPtr; #asm LIB SPNullSprPtr ._sp_NullSprPtr defw SPNullSprPtr #endasm extern uchar *sp_KeyStartRepeat; #asm XREF SPkeystartrepeat ._sp_KeyStartRepeat defw SPkeystartrepeat #endasm extern uchar *sp_KeyRepeatPeriod; #asm XREF SPkeyrepeatperiod ._sp_KeyRepeatPeriod defw SPkeyrepeatperiod #endasm /* KEYBOARD SCANNING HOOK */ extern uchar *lastk; /* system variable LAST_K */ #asm ._lastk defw 23560; #endasm uchar count; #asm defw 0 /* two free bytes before start of a hook */ #endasm void keyboard_isr(void) { uchar c; if ((c = sp_GetKey()) != 0) *lastk = c; if (count != 0) count--; } /* MEMORY ALLOCATION POLICY */ void *my_malloc(uint bytes) /* using sprite pack library's dynamic block */ { /* memory allocator, but could also use C's */ if (bytes == 6) /* malloc. */ return sp_BlockAlloc(0); return sp_BlockAlloc(1); } void *u_malloc = my_malloc; void *u_free = sp_FreeBlock; /* SPRITES */ extern uchar pac0[]; /* sprite graphics appear following main() */ extern uchar ptr0[]; extern uchar sel0[]; extern int offsets[]; /* distance between successive pacman sprite frames */ #asm ._offsets defw _pac2-_pac0 /* these labels are assembler labels (see below main) */ defw _pac4-_pac2 /* so this array is defined using assembler rather than */ defw _pac6-_pac4 /* C where these labels would not be visible */ defw _pac8-_pac6 defw _pac6-_pac8 defw _pac4-_pac6 defw _pac2-_pac4 defw _pac0-_pac2 #endasm struct sp_SS *ptr; struct sp_SS *sel; struct sp_List *pacmen; struct sp_List dup; struct pacstate { /* details about an individual pacman */ struct sp_SS *man; /* the sprite pointer */ uchar count; /* how long until the animation frame changes */ uchar period; /* rate at which frame changes */ uchar frame; /* which frame are we in now? */ char dx, dy; /* signed movement rate in pixels */ }; /* BACKGROUND TILES */ struct sp_PSS ps; /* a print string struct used by sp_PrintString */ uchar buff[33]; uchar dust[] = {0x08,0x40,0x02,0x10,0x00,0x41,0x08,0x80}; uchar intro[] = "\x14\x06" \ "\x16\x01\x01 PACMEN DEMO " \ "\x14\x28" \ "\x16\x03\x01 C = Create " \ "\x16\x04\x01 D = Delete " \ "\x14\x05" \ "\x16\x06\x01 Select Using " \ "\x14\x28" \ "\x16\x08\x01 J = Kemp Joy M " \ "\x16\x09\x01 K = Kemp Mouse " \ "\x16\x0a\x01 M = AMX Mouse " \ "\x16\x0b\x01 S = Key Mouse " \ "\x16\x0c\x01 [ QAOP space ] " \ "\x16\x0e\x01 0 = Prev Pman " \ "\x16\x0f\x01 1 = Next Pman " \ "\x14\x05" \ "\x16\x11\x01 Modify Pacman " \ "\x14\x28" \ "\x16\x13\x01 T = Type " \ "\x16\x14\x01 Xx = Change dx " \ "\x16\x15\x01 Yy = Change dy " \ "\x16\x16\x01 Ff = Anim Prd " \ "\x16\x03\x13Entr = Pause" \ "\x16\x04\x13Z = Z80 Trbo"; /* MOUSE DATA */ struct sp_MD deltas[] = { {8, 0x0100, 0x0100}, /* 8 times, 1 pixel dx, 1 pixel dy */ {8, 0x0300, 0x0300}, /* 8 times, 3 pixel dx, 3 pixel dy */ {8, 0x0500, 0x0500}, /* 8 times, 5 pixel dx, 5 pixel dy */ {255, 0x0800, 0x0800} /* Final delta must have a count of 255 */ }; struct sp_UDK mousekeys; /* user defined keys structure */ struct sp_UDM mouse; /* user defined mouse structure */ struct point { uint x; uchar y; } pt; /* holds mouse location */ void *mousein; /* which mouse to read */ uint pause; /* the pause key */ uint z80; /* the 'turbo' key */ /* SPRITE + POINTER COLLISION DETECTION */ struct sp_LargeRect r; int collide(struct point *pt, struct pacstate *p) { r.bottom = r.top = 8*p->man->row + p->man->ver_rot; r.bottom += 7; r.right = r.left = 8*p->man->col + p->man->hor_rot; r.right += 7; if (sp_IntPtLargeRect(pt->x, pt->y, &r)) return_c(1); return_nc(0); } /* DISPLAY SPRITE DATA */ struct sp_Rect sr; void displayData(void) { struct pacstate *p; if ((p = sp_ListCurr(pacmen)) != NULL) { sp_PrintString(&ps, "\x16\x08\x13\x14\x0c PACMAN "); sprintf(buff, "\x16\x0a\x17\x14\x21#%u ", p->man->plane-1); sp_PrintString(&ps, buff); sprintf(buff, "\x16\x0b\x13Type - "); switch (p->man->type) { case sp_MASK_SPRITE: strcat(buff, "Mask"); break; case sp_OR_SPRITE: strcat(buff, "Or "); break; case sp_XOR_SPRITE: strcat(buff, "Xor "); break; default: strcat(buff, "Load"); break; } sp_PrintString(&ps, buff); sprintf(buff, "\x16\x0c\x15DX - %u ", (p->dx < 0) ? (-p->dx) : (p->dx)); sp_PrintString(&ps, buff); sprintf(buff, "\x16\x0d\x15DY - %u ", (p->dy < 0) ? (-p->dy) : (p->dy)); sp_PrintString(&ps, buff); sprintf(buff, "\x16\x0e\x14Prd - %u ", p->period); sp_PrintString(&ps, buff); } else { sr.row_coord = 8; sr.col_coord = 19; sr.height = 7; sr.width = 12; sp_ClearRect(&sr, INK_BLUE | PAPER_GREEN, ' ', sp_CR_TILES); sp_Invalidate(&sr, sp_ClipStruct); } } /* BLANK OUT LAST COLUMN IN SPRITES */ uchar n; void blankSpr(struct sp_CS *cs) { if (n == 0) cs->graphic = sp_NullSprPtr; else n--; } /* CHANGE SPRITE CHAR TYPE */ void changeType(struct sp_CS *cs) { cs->spr_attr = (cs->spr_attr & 0x3f) | n; } main() { uint old_x; uchar old_y, b, old_b; struct pacstate *p; #asm di #endasm sp_InitIM2(0xf1f1); sp_CreateGenericISR(0xf1f1); sp_RegisterHook(255, keyboard_isr); sp_MouseAMXInit(0, 2); #asm ei #endasm sp_TileArray(' ', dust); sp_Initialize(INK_WHITE | PAPER_BLACK, ' '); sp_Border(BLACK); sp_AddMemory(1, 130, 14, sp_AddMemory(1, 255, 14, sp_AddMemory(0, 64, 6, 0xd000))); ps.flags = sp_PSS_INVALIDATE; ps.bounds = sp_ClipStruct; *sp_KeyStartRepeat = *sp_KeyRepeatPeriod = 45; ptr = sp_CreateSpr(sp_MASK_SPRITE, 3, ptr0, 0, TRANSPARENT); sp_AddColSpr(ptr, ptr0, TRANSPARENT); n = 3; sp_IterateSprChar(ptr, blankSpr); sel = sp_CreateSpr(sp_MASK_SPRITE, 2, sel0, 1, INK_YELLOW | PAPER_BLACK); sp_AddColSpr(sel, sel0, INK_YELLOW | PAPER_BLACK); n = 2; sp_IterateSprChar(sel, blankSpr); pacmen = sp_ListCreate(); mousekeys.left = sp_LookupKey('o'); mousekeys.right = sp_LookupKey('p'); mousekeys.up = sp_LookupKey('q'); mousekeys.down = sp_LookupKey('a'); mousekeys.fire = sp_LookupKey(' '); mouse.keys = &mousekeys; /* user-defined keys for keyboard joystick */ mouse.joyfunc = sp_JoyKeyboard; /* mouse will use keyboard joystick */ mouse.delta = deltas; /* acceleration profile for mouse */ mousein = sp_MouseSim; /* use keyboard mouse by default */ pause = sp_LookupKey(13); /* pause key is 'enter' */ z80 = sp_LookupKey('z'); /* use-assembler key */ sr.row_coord = 1; sr.col_coord = 1; sr.height = 22; sr.width = 16; sp_ClearRect(&sr, INK_CYAN | PAPER_BLACK, ' ', sp_CR_TILES); sp_PrintString(&ps, intro); displayData(); old_b = 0xff; count = INACTIVE; while(1) { sp_UpdateNow(); /* process keyboard commands */ p = sp_ListCurr(pacmen); switch (getk()) { /* select previous pacman */ case '1': sp_ListPrev(pacmen); displayData(); break; /* select next pacman */ case '0': sp_ListNext(pacmen); displayData(); break; /* select AMX mouse */ case 'M': case 'm': mousein = sp_MouseAMX; sp_SetMousePosAMX(pt.x, pt.y); break; /* select Kempston mouse */ case 'K': case 'k': mousein = sp_MouseKempston; sp_SetMousePosKempston(pt.x, pt.y); break; /* simulated keyboard mouse */ case 'S': case 's': mousein = sp_MouseSim; mouse.joyfunc = sp_JoyKeyboard; sp_SetMousePosSim(&mouse, pt.x, pt.y); break; /* simulated kempston joystick mouse */ case 'J': case 'j': mousein = sp_MouseSim; mouse.joyfunc = sp_JoyKempston; sp_SetMousePosSim(&mouse, pt.x, pt.y); break; /* delete selected pacman */ case 'd': case 'D': if (((p = sp_ListRemove(pacmen)) != NULL) || ((p = sp_ListTrim(pacmen)) != NULL)) { sp_MoveSprAbs(p->man, sp_ClipStruct, 0, 0, 32, 0, 0); sp_DeleteSpr(p->man); /* only delete sprites not on screen!!! */ (u_free)(p); displayData(); } break; /* create another pacman */ case 'c': case 'C': if ((b = sp_ListCount(pacmen)) < 62) { p = (struct pacstate *)((u_malloc)(sizeof(struct pacstate))); p->man = sp_CreateSpr((rand()&3) << 6, 2, pac0, b+2, TRANSPARENT); sp_AddColSpr(p->man, pac0, TRANSPARENT); n = 2; sp_IterateSprChar(p->man, blankSpr); sp_MoveSprAbs(p->man, sp_ClipStruct, 0, 11, 15, 0, 0); p->frame = 0; p->count = p->period = (rand() & 7); p->dx = (rand() & 7) - 3; p->dy = (rand() & 7) - 3; sp_ListAppend(pacmen, p); displayData(); } break; /* adjust selected pacman's sprite type */ case 'T': case 't': if (p != NULL) { n = p->man->type += 0x40; sp_IterateSprChar(p->man, changeType); displayData(); } break; /* adjust selected pacman's horizontal speed */ case 'X': if (p != NULL) { p->dx += (p->dx > 0) ? (1) : (-1); displayData(); } break; case 'x': if (p != NULL) { p->dx += (p->dx > 0) ? (-1) : (1); displayData(); } break; /* adjust selected pacman's vertical speed */ case 'Y': if (p != NULL) { p->dy += (p->dy > 0) ? (1) : (-1); displayData(); } break; case 'y': if (p != NULL) { p->dy += (p->dy > 0) ? (-1) : (1); displayData(); } break; /* adjust selected pacman's animation rate */ case 'F': if (p != NULL) { if (p->period++ < p->count) p->count = p->period; displayData(); } break; case 'f': if (p != NULL) { if (p->period-- < p->count) p->count = p->period; displayData(); } break; default: break; } /* Move Pacmen */ if (!sp_KeyPressed(pause)) { if (sp_KeyPressed(z80)) { /* machine code version of C in 'else' branch -- this is 'turbo mode' */ /* the name 'turbo mode' is an exaggeration as the speedup is only marginal */ #asm LIB SPListFirst, SPListNext, SPMoveSprRel, SPClipStruct ld hl,(_pacmen) inc hl inc hl ld a,(hl) inc hl ld d,(hl) inc hl ld e,(hl) push af ; save pacmen list state push de ; save pacmen list current pointer ld iy,SPClipStruct ; iy = SPClipStruct, parameter to SPMoveSprRel later ld hl,(_pacmen) call SPListFirst ; de = first item in pacmen list jp nc, doneloop .forloop ; de = current item in list = struct pacstate * ex de,hl ld a,(hl) defb $dd ld l,a inc hl ld a,(hl) defb $dd ld h,a ; ix = pacstate.man = struct sp_SS * inc hl ld a,(hl) ; if pacstate.count == 0 ... or a jr nz, countNotZero inc hl ld a,(hl) dec hl ld (hl),a ; pacstate.count = pacstate.period inc hl inc hl ld a,(hl) ; a = pacstate.frame ex de,hl add a,a ld l,a ld h,0 ld bc,_offsets add hl,bc ld c,(hl) inc hl ld b,(hl) ; bc = animation offset ex de,hl ld a,(hl) inc a and $07 ld (hl),a ; pacstate.frame = (pacstate.frame + 1) & 0x07 inc hl ; hl = &pacstate.dx jp rejoin .countNotZero dec (hl) ; pacstate.count-- inc hl inc hl inc hl ; hl = &pacstate.dx ld bc,0 ; bc = 0 = no animation offset .rejoin push hl ; save pacstate.dx ld d,(hl) ; d = pixel dx inc hl ld e,(hl) ; e = pixel dy ld hl,0 ; hl = 0 = no character-size movement call SPMoveSprRel ; move sprite, params in ix,iy,bc,de,hl pop hl ; hl = pacstate.dx ld a,(ix+1) cp 31 jr c, dxokay ; if column coordinate > 30 ld a,(hl) neg ld (hl),a ; change sprite horizontal direction .dxokay inc hl ld a,(ix+0) cp 23 jr c, dyokay ; if row coordinate > 22 ld a,(hl) neg ld (hl),a ; change sprite vertical direction .dyokay ld hl,(_pacmen) call SPListNext ; get next pacman into de (struct pacstate *) jp c, forloop .doneloop pop de ; restore pacmen list state & current pointer pop af ld hl,(_pacmen) inc hl inc hl ld (hl),a inc hl ld (hl),d inc hl ld (hl),e #endasm } else { memcpy(&dup, pacmen, sizeof(struct sp_List)); /* to save list's current pointer */ for (p = sp_ListFirst(pacmen); p != NULL; p = sp_ListNext(pacmen)) { if (p->count == 0) { p->count = p->period; sp_MoveSprRel(p->man, sp_ClipStruct, offsets[p->frame], 0, 0, p->dx, p->dy); p->frame = (p->frame + 1) & 7; } else { p->count--; sp_MoveSprRel(p->man, sp_ClipStruct, 0, 0, 0, p->dx, p->dy); } if (p->man->row > 22) p->dy = -p->dy; if (p->man->col > 30) p->dx = -p->dx; } memcpy(pacmen, &dup, sizeof(struct sp_List)); /* restores list's current pointer */ } } /* process mouse action */ (mousein)(&mouse, &pt.x, &pt.y, &b); /* if mouse is touched, make sure it is displayed */ if ((old_x != pt.x) || (old_y != pt.y) || (b != 0xff)) { count = INACTIVE; sp_MoveSprAbs(ptr, sp_ClipStruct, 0, pt.y/8, pt.x/8, pt.x&7, pt.y&7); old_x = pt.x; old_y = pt.y; } /* if left button pressed */ if ((b & sp_BUT1) == 0) { /* if left button just pressed now */ if (old_b != b) { /* see if a pacman was clicked */ sp_ListFirst(pacmen); if (sp_ListSearch(pacmen, collide, &pt) != NULL) old_b = b; displayData(); } else if ((p = sp_ListCurr(pacmen)) != NULL) { /* button was pressed, pacman selected so drag */ sp_MoveSprAbs(p->man, sp_ClipStruct, 0, ptr->row, ptr->col, \ ptr->hor_rot, ptr->ver_rot); } } else old_b = b; if ((count == 0) && (ptr->col != 32)) sp_MoveSprAbs(ptr, sp_ClipStruct, 0, 0, 32, 0, 0); /* draw selection rectangle */ if ((p = sp_ListCurr(pacmen)) != NULL) { sp_MoveSprAbs(sel, sp_ClipStruct, 0, p->man->row, p->man->col, \ p->man->hor_rot, p->man->ver_rot); } else if (sel->col != 32) sp_MoveSprAbs(sel, sp_ClipStruct, 0, 0, 32, 0, 0); } } #asm defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 ._pac0 defb @00011000, @10000001 defb @00111100, @00000000 defb @01111110, @00000000 defb @01111110, @00000000 defb @01111110, @00000000 defb @01111110, @00000000 defb @00111100, @00000000 defb @00011000, @10000001 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 ._pac2 defb @00011000, @10000001 defb @00111100, @00000000 defb @01111110, @00000000 defb @01111100, @00000000 defb @01100000, @00000000 defb @01111100, @00000000 defb @00111100, @00000000 defb @00011000, @10000001 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 ._pac4 defb @00011000, @10000001 defb @00111100, @00000000 defb @01111110, @00000000 defb @01111000, @00000000 defb @01100000, @00000011 defb @01111000, @00000001 defb @00111100, @00000000 defb @00011000, @10000001 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 ._pac6 defb @00011000, @10000001 defb @00111100, @00000000 defb @01111000, @00000001 defb @01110000, @00000011 defb @01100000, @00000111 defb @01110000, @00000001 defb @00111100, @00000001 defb @00011000, @10000001 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 ._pac8 defb @00011000, @10000011 defb @00111000, @00000011 defb @01110000, @00000011 defb @01100000, @00000111 defb @01100000, @00001111 defb @01100000, @00000011 defb @00111000, @00000011 defb @00011000, @10000011 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 ._ptr0 defb @00000000, @00111111 defb @01000000, @00011111 defb @01100000, @00001111 defb @01110000, @00000111 defb @01111000, @00000011 defb @01111100, @00000001 defb @01111110, @00000000 defb @01101000, @00000000 defb @01001000, @00000011 defb @00000100, @00110001 defb @00000100, @11110001 defb @00000010, @11111000 defb @00000010, @11111000 defb @00000000, @11111100 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 ._sel0 defb @11000011, @00011000 defb @10000001, @00011000 defb @00000000, @00111100 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @00111100 defb @10000001, @00011000 defb @11000011, @00011000 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 defb @00000000, @11111111 #endasm