1: ; Quikman ROM cartridge for Commodore VIC20
2: ; written by Robert Hurst <robert@hurst-ri.us>
3: ; originally used Commodore VICMON (SYS 45056)
4: ; ported using the CA65 assembler (cc65.org)
5: ; updated version: 13-Dec-2008
6: ;
7: ; to assemble this source using cc65.org project:
8: ; ca65.exe --cpu 6502 --listing quikman-rom.s
9: ; ld65.exe -C doc/vic20-cartA.cfg -o quikman-rom.a0 quikman-rom.o
10: ;
11: ; to run the binary using viceteam.org project:
12: ; xvic -memory none -ntsc -sound -joydev1 2 -cartA quikman-rom.a0
13: ; to run the binary using mess.org project:
14: ; mess -view "Pixel Aspect (25:31)" vic20 -cart1 quikman-rom.a0
15: ;
16: ; pertinent VIC20 symbols
17: JIFFYH = $A0 ; jiffy-clock hi byte value
18: JIFFYM = $A1 ; jiffy-clock mid byte value
19: JIFFYL = $A2 ; jiffy-clock low byte value
20: CLRPAGE = $F4 ; color memory page (unexpanded = $97)
21: SCRNPAGE = $0288 ; screen memory page (unexpanded = $1E)
22: CTRLCSHIFT = $028D ; keyboard flag: control/commodore/shift
23: CASSBUFF = $033C ; cassette buffer
24: VIC = $9000 ; start of video interface chip registers
25: RESET = $FD22 ; warm startup
26: CHROUT = $FFD2 ; print character with cursor translation
27: GETIN = $FFE4 ; get a character from keyboard queue
28: ;
29: ; my symbol / memory map
30: ; = $00 ; base index (monster, sprite)
31: ; = $01 ; index x2
32: ; = $02 ; index x8
33: PPILLTIMER = $10 ; powerpill effectiveness timer
34: FRUITTIMER = $39 ; 0 - 250
35: FRUITFLAG = $3A ; zero or non-zero, if fruit has been activated
36: PPILLFLAG = $3B ; just ate a powerpill this turn (0=no)
37: CHOMP = $3C ; pointer into sound effect for fruit and fleeing monsters
38: CHEWING = $3D ; flag whether quikman just ate a dot or not
39: DIGIT = $3E ; award points at this digit place
40: POINTS = $3F ; how many points scored
41: OLDDIR = $40 ; direction sprite was last moving in
42: NEWDIR = $41 ; direction sprite wants to take, if valid by MAZEMOVE
43: JOYVAL = $42 ; last joystick read value
44: QMANDIR = $43 ; quikman's current direction (0=right,1=down,2=left,3=up)
45: FRAME = $44 ; frame number
46: LIVES = $45 ; 0 - 3
47: FLASHPILL = $46 ; powerpill blink counter (0-30)
48: EXTRAQMAN = $47 ; bonus quickman flag (0=unused)
49: DEMOQMAN = $48 ; spirit of quickman index (0-3)
50: FRUITLEVEL = $49 ; 0 - 12
51: DOTS = $4A ; 0 - 170
52: PENALTY = $4B ; $4B-$4E monsters are free-to-roam flag
53: ; = $4F ; $4F-$52 monsters current direction (0=right,1=down,2=left,3=up)
54: ; = $53 ; $53,$55,$57,$59 monster's knowledge of quikman's "X" coord was
55: ; = $54 ; $54,$56,$58,$5A monster's knowledge of quikman's "Y" coord was
56: MONMOVE = $61 ; $61-$64 monster array for its next best move
57: ; = $69 ; temporary var
58: FLEEINGSCORE= $70 ; fleeing monster score: 2, 4, 8, 16
59: ; = $F7 ; $F7/$F8,$F9,$FA are screen cell pointers for sprite's position
60: ; = $FE ; $FC/$FD,$FE/$FF are color cell pointers for same
61: ;
62: ; program indirects (my sprite registers)
63: SPRITE = $02A1 ; bitmask 0-7 controls sprite on/off
64: SPRITEX = $02A2 ; $02A2,A4,A6,A8,AA,AC,AE,B0 each "X" coordinate
65: SPRITEY = $02A3 ; $02A3,A5,A7,A9,AB,AD,AF,B1 each "Y" coordinate
66: SPRITECLR = $02B2 ; $02B2-$02B9 color
67: SPRITEIMG1 = $02BA ; $02BA-$02C1 low-byte of SPRITE image
68: SPRITEIMG2 = $02C2 ; $02C2-$02C9 hi-byte of SPRITE image
69: SPRITELAST = $02CC ; $02CC-$02DC keep last state of SPRITE registers
70: SAVEBACK = $02DD ; $02DD-$02E6 keep what's under the sprite's 2-cell
71: ;
72: ; other constants
73: FRUITCELL = $1F1B ; screen cell address of fruit
74: FRUITCELLCLR= $971B ; color cell address of fruit
75: ;
76: ; uses standard VIC20 (unexpanded)
77: .org $A000
78: .segment "STARTUP"
79: ;
80: ;*********************************************************************
81: ; Commodore Cartridge boot sequence
82: ; load address of startup & restore key:
83: .word MAIN
84: .word NMI
85: ; power-up signature: A0CBM
86: .byte $41, $30, $C3, $C2, $CD
87: ;
88: ;*********************************************************************
89: ; RESTORE key was pressed by a frustrated player ...
90: NMI:
91: CLD
92: PLA ; Y
93: TAY
94: PLA ; X
95: TAX
96: PLA ; A
97: JMP RESTART
98: ;
99: ;*********************************************************************
100: ; Starting entry point for this program
101: MAIN:
102: ; initialize VIC into usable state
103: JSR $FD8D ; ramtas Initialise System Constants (memory pointers)
104: JSR $FDF9 ; ioinit Initialise I/O (timers are enabled)
105: JSR $E518 ; cint1 Initialize I/O (VIC reset, must follow ramtas)
106: JSR $FF8A ; restor Restore Vectors (required)
107: ;JSR $FF8D ; vector Change Vectors For User (not required)
108: ;LDA #$7F
109: ;STA $911E ; disable NMI, so no reset key wanted?
110: ; my VIC chipset init
111: LDA VIC+$01
112: SBC #$04 ; adjust top scan line to accomodate extra row
113: STA VIC+$01
114: LDA #$80+$15 ; set for videoram @ $1E00 with 21-columns
115: STA VIC+$02 ; video matrix address + columns
116: LDA #$B0 ; $B0 = 10110000 = 24 rows + 8x8 height
117: STA VIC+$03 ; rows / character height
118: LDX #$FF ; set for $1C00
119: STX VIC+$05 ; use programmable char set
120: ;
121: ;*********************************************************************
122: ; SOFT reset entry point
123: RESTART:
124: ; my interrupt vector init
125: SEI
126: LDX #<BACKGROUND
127: LDY #>BACKGROUND
128: STX $0314
129: STY $0315
130: CLI
131: ; seed BASIC RAM with game data
132: LDA #<MAZEDATA
133: STA $FC
134: LDA #>MAZEDATA
135: STA $FD
136: LDA #$00
137: STA $FE
138: LDA #$1A
139: STA $FF
140: LDY #$00
141: @copy: LDA ($FC),Y
142: STA ($FE),Y
143: INY
144: BNE @copy
145: INC $FD
146: INC $FF
147: LDA $FF
148: CMP #$1E
149: BNE @copy
150: TYA
151: @snd: STA VIC+$0A,Y ; reset sound channels
152: INY
153: CPY #$04
154: BNE @snd
155: LDA #$8F ; brown & highest
156: STA VIC+$0E ; auxiliary color & volume
157: JSR RESTORE
158: LDX #$15
159: @msg: LDA BANNERMSG,X
160: STA $1FE3,X
161: LDA #$03
162: STA $97E3,X
163: DEX
164: BPL @msg
165: LDA #$09 ; brown button, white font, just like the F-key
166: STA $97E3+$0E
167: STA $97E3+$0F
168: ;
169: DEMO:
170: JSR INITVARS
171: LDY #$00
172: STY LIVES
173: JSR GAMEOVER
174: LDY #$0A
175: STY SPRITE
176: @loop: LDA FRAME
177: AND #$7F
178: BNE @slow
179: LDX VIC+$0F
180: INX
181: TXA
182: AND #$07
183: TAX
184: ORA #$08
185: STA VIC+$0F
186: JSR MAZEPAINT
187: @skip: INC FRUITLEVEL
188: LDX FRUITLEVEL
189: CPX #$0D
190: BCC @fruit
191: STX PPILLFLAG ; demo powerpill
192: LDX #$00
193: STX FRUITLEVEL
194: @fruit: LDA FRUIT,X
195: STA FRUITCELL
196: LDA FRUITCLR,X
197: STA FRUITCELLCLR
198: LDA #$40
199: STA FRUITTIMER
200: STA FRUITFLAG
201: @slow: LDX JIFFYL
202: INX
203: INX
204: @wait: CPX JIFFYL
205: BNE @wait
206: LDA SPRITE
207: EOR #$1E
208: STA SPRITE
209: JSR NPC ; demo mode
210: @scan: JSR SPRITES
211: JSR GETIN ; get keyboard
212: CMP #$85 ; got F1 ?
213: BEQ RESETGAME ; try again ...
214: LDA #$FF
215: STA $9122
216: LDA $9111
217: AND #$20 ; FIRE
218: BNE @loop
219: ;
220: ;*********************************************************************
221: RESETGAME:
222: LDA #$09
223: STA CHOMP
224: LDA #$03 ; start with 3-lives
225: STA LIVES
226: LDY #$FF ; -1 will become 0 at start of "next" level
227: STY FRUITLEVEL
228: JSR GAMEOVER ; clear status
229: LDX #$00 ; reset score
230: STX EXTRAQMAN ; reset bonus
231: @loop1: LDA #$B0 ; each digit to "0"
232: STA SCORE-$9200,X ; into savebuffer
233: INX ; do next digit
234: CPX #$06 ; all 6 of them
235: BNE @loop1
236: ;
237: STARTLVL:
238: JSR RESTORE ; initialize new level
239: ;
240: RESETCHR:
241: JSR INITVARS
242: LDA #$1C ; 1st page where quikman is on
243: STA SPRITEIMG2
244: LDA #$A8 ; start quikman off with a smug smile
245: STA SPRITEIMG1
246: LDA #$1F ; turn on sprites 0-4
247: STA SPRITE
248: LDX #$0C
249: @smug: LDA #$08
250: JSR PAUSE ; then he sees there are monsters ...
251: DEX
252: BPL @smug
253: LDX #$05
254: @loop2: LDA $C377,X ; print READY from ROM
255: AND #$BF
256: ORA #$80
257: STA $1F18,X
258: LDA #$07 ; make it yellow
259: STA $9718,X
260: LDA #$98 ; restore monster caged image (heh)
261: STA SPRITEIMG1,X
262: DEX
263: BNE @loop2 ; how geeky is that?
264: LDA #$C8 ; quikman gets ready
265: STA SPRITEIMG1
266: LDX #$18
267: @ready: LDA #$08
268: JSR PAUSE ; wait 2+ seconds
269: DEX
270: BPL @ready
271: LDX #$00
272: LDA #$20 ; erase READY
273: @loop4: STA $1F19,X
274: INX
275: CPX #$05
276: BNE @loop4
277: ;
278: ; zero $39 - $43
279: ZEROVARS:
280: LDX #$00
281: STX FRUITFLAG
282: STX PPILLFLAG
283: STX CHEWING
284: LDX #$02
285: STX QMANDIR ; start off going LEFT
286: STX JOYVAL ; preload last joystick value as going LEFT
287: ;
288: ;*********************************************************************
289: PLAYLOOP:
290: LDA CTRLCSHIFT ; is the player holding down any
291: BNE PLAYLOOP ; control, commodore, shift key(s)?
292: LDA FRUITCELL
293: CMP #$21 ; is there fruit on display?
294: BCC @warp
295: LDX FRUITLEVEL
296: LDA FRUITCLR,X ; restore fruit color
297: STA FRUITCELLCLR
298: @warp: LDA #$0C ; 10-levels to warp
299: SEC
300: SBC FRUITLEVEL ; progressive speed
301: BCC @cruz
302: CMP #$03
303: BCS @pace
304: @cruz: LDA #$03
305: @pace: TAY
306: @sleep: INX
307: BNE @sleep
308: DEY
309: BNE @sleep ; one 1000, two 1000, three 1000, ...
310: STY $00 ; quikman is sprite #0
311: STY $01
312: LDA QMANDIR
313: STA OLDDIR ; save last direction quikman was going in
314: LDX JOYVAL ; recall last joystick value
315: STY $9113
316: LDA #$7F
317: STA $9122
318: LDA $9120
319: AND #$80 ; JOY 3
320: BNE @joy0
321: LDX #$00
322: @joy0: LDA #$FF
323: STA $9122
324: LDY $9111
325: TYA
326: AND #$08
327: BNE @joy1
328: LDX #$01
329: @joy1: TYA
330: AND #$10
331: BNE @joy2
332: LDX #$02
333: @joy2: TYA
334: AND #$04
335: BNE @joy3
336: LDX #$03
337: @joy3: STX JOYVAL ; save
338: TXA
339: STA NEWDIR ; do the same for the joystick
340: JSR MAZEMOVE
341: BCS @skip1 ; is the direction valid?
342: LDA JOYVAL ; yes,
343: STA QMANDIR ; request quikman to move in direction of joystick
344: CLC
345: BCC @skip2
346: @skip1: LDA QMANDIR
347: STA NEWDIR
348: JSR MAZEMOVE ; keep the current direction going?
349: @skip2: LDA SPRITEX
350: BNE @skip3 ; is quikman at end of tunnel left?
351: LDA #$9E
352: STA SPRITEX ; put quikman at beginning of tunnel right
353: @skip3: CMP #$A0 ; is quikman at end of tunnel right?
354: BNE @skip4
355: LDA #$00
356: STA SPRITEX ; put quikman at beginning of tunnel left
357: @skip4:
358: LDX #$B0 ; closed mouth
359: LDA SPRITEX
360: ORA SPRITEY
361: AND #$02
362: BNE @anim
363: LDA QMANDIR ; take 0=right, 1=down, 2=left, 3=up value
364: ASL ; multiply by 8 to get address
365: ASL
366: ASL
367: CLC
368: ADC #$B8 ; add base offset
369: TAX
370: @anim: STX SPRITEIMG1
371: ;
372: LDX #$00 ; use X as a flag
373: LDA SPRITEX
374: AND #$07
375: CMP #$04
376: BNE @skip5 ; is quikman in the middle of a left/right cell?
377: INX
378: LDA QMANDIR ; yes, 0=right, 2=left
379: EOR #$02
380: TAY
381: BEQ @skip6 ; going left, use 1st saveback cell
382: DEY
383: BNE @skip6 ; going right, use overflow saveback cell
384: @skip5: LDA SPRITEY
385: AND #$07
386: CMP #$03
387: BNE @skip6 ; is quikman in the middle of an up/down cell?
388: INX
389: LDY QMANDIR
390: CPY #$01 ; going down, use overflow saveback cell
391: BEQ @skip6
392: LDY #$00 ; going up, use 1st saveback cell
393: @skip6: CPX #$00
394: BNE @skip7 ; does quikman have something in its mouth?
395: JMP NPCNEXT ; no, continue play
396: @skip7: LDA SAVEBACK,Y ; retrieve the character from quikman's saveback buffer
397: CMP #$20
398: BNE @skip8
399: JMP PLAYLOOP ; nothing to eat here, so move a bit faster
400: ; check what was just eaten ...
401: @skip8: TAX ; save that something in X
402: LDA #$20
403: STA SAVEBACK,Y ; replace the cell quikman is on with an empty space
404: CPX #$1E ; is it a dot?
405: BNE POWERUP
406: LDA #$01
407: STA POINTS ; score 1
408: STA CHEWING ; quikman has to chew this dot, monsters keep movin'
409: LDA #$0A ; score it @ 10-point digit
410: STA DIGIT
411: ;
412: POWERUP:
413: CPX #$21
414: BCC @skip1 ; is X < 33 ?
415: CPX #$29 ; no, is X >= 41 ?
416: BCS NPCNEXT ; ate a piece of fruit?
417: TXA ; YUMMY!
418: SEC
419: SBC #$21 ; strip off char code for score index
420: TAX
421: LDA FRUITSCORE,X
422: STA POINTS ; award points
423: LDA #$09
424: STA DIGIT ; in hundreds
425: STA CHOMP
426: CLC
427: BCC NPCNEXT
428: @skip1: CPX #$1F ; ate a powerpill?
429: BNE EATING ; no, but I did eat a dot
430: LDA #$05
431: STA POINTS ; award 5-points
432: LDA #$0A
433: STA DIGIT ; score @ 10-digit
434: STA PPILLFLAG
435: BEQ NPCNEXT ; powerpills are dots on steroids, account for it
436: ;
437: EATING: INC DOTS ; ate a dot, account for it
438: LDA DOTS
439: CMP #$AA ; are all dots eaten?
440: BNE NPCNEXT
441: ;=== achieved end of level ===
442: WONLEVEL:
443: LDA #$A8 ; quikman ends with a smug smile
444: STA SPRITEIMG1
445: LDA #$80
446: JSR PAUSE
447: LDA #$00
448: STA FRAME
449: @loop: DEC FRAME
450: LDA FRAME
451: AND #$07
452: TAX
453: JSR MAZEPAINT
454: LDA FRAME
455: AND #$1F
456: BNE @next
457: LDA SPRITE
458: EOR #$1E
459: STA SPRITE ; blink monsters
460: @next: LDX #$04
461: LDA #$70 ; ghost ends small
462: @tiny: STA SPRITEIMG1,X
463: DEX
464: BNE @tiny
465: JSR SPRITES
466: LDA FRAME
467: CMP #$06
468: BNE @loop ; continue until true blue
469: LDA #$40
470: JSR PAUSE
471: LDX #$03
472: @loop1: LDA PENALTY,X
473: LDY FRUITLEVEL
474: INY
475: @loop2: LSR
476: DEY
477: BNE @loop2
478: STA PENALTY,X ; after each level, the monsters dispatch quicker
479: DEX
480: BPL @loop1
481: JMP STARTLVL
482: ;
483: NPCNEXT:
484: JSR GETIN ; get keyboard
485: CMP #$03 ; got STOP ?
486: BEQ WONLEVEL ; CHEATER!!
487: JSR NPC
488: JSR SPRITES
489: JMP PLAYLOOP
490: ;
491: ;*********************************************************************
492: ; non-player characters & events
493: NPC:
494: LDA FRUITFLAG
495: BNE @skip3 ; is fruit already on display?
496: LDA DOTS
497: CMP #$4B ; has the 75th dot been eaten?
498: BEQ @skip1
499: CMP #$7D ; has the 125th dot been eaten?
500: BNE @skip3
501: @skip1: LDX FRUITLEVEL ; prepare thy bonus
502: CPX #$0C ; reach the last level?
503: BCC @skip2
504: LDX #$0C ; only the key is left, and it leaves a bad metallic after-taste
505: @skip2: LDA FRUIT,X
506: STA FRUITCELL ; display fruit
507: LDA FRUITCLR,X
508: STA FRUITCELLCLR
509: LDA #$FA ; 250-moves and counting
510: STA FRUITTIMER ; reset fruit timer
511: STA FRUITFLAG
512: @skip3: LDA FRUITTIMER ; fruit is on display
513: BEQ @skip4 ; nothing to do
514: DEC FRUITTIMER ; remove a tick
515: BNE @skip4 ; there is still time left
516: LDA #$20 ; time's up!
517: STA FRUITCELL ; no more fruit
518: @skip4: LDA DOTS
519: CMP #$4C ; has the 76th dot been eaten?
520: BEQ @skip5
521: CMP #$7E ; has the 126th dot been eaten?
522: BNE @skip6
523: @skip5: LDA #$00
524: STA FRUITFLAG ; more fruit potential on this level
525: ;
526: @skip6: LDA PPILLFLAG
527: BEQ KISSING ; just swallowed a powerpill?
528: LDA #$00 ; account for that action
529: STA PPILLFLAG
530: LDA #$02 ; start scoring @ 200-points
531: STA FLEEINGSCORE
532: LDX FRUITLEVEL
533: TXA
534: AND #$07
535: BEQ @break ; every 8-levels, keep timer up
536: CPX #$10
537: BCS @timer ; 16-levels of powerpill timing
538: TXA
539: @break: ASL ; x2
540: ASL ; x2
541: AND #$3F
542: EOR #$3F ; invert A
543: CPX #$05
544: BCC @timer ; timer good to the 1st apple
545: LSR ; 1/2
546: @timer: STA PPILLTIMER ; set powerpill timer
547: LDY #$04
548: @loop1: LDX PENALTY-1,Y
549: BNE @skip7 ; is monster waiting in cage already?
550: LDA #$06 ; no, make monster blue
551: STA SPRITECLR,Y
552: LDA #$A0 ; make monster fleeing (0)
553: STA SPRITEIMG1,Y
554: LDA $4E,Y
555: EOR #$02 ; and reverse its direction
556: STA $4E,Y
557: @skip7: DEY
558: BNE @loop1
559: ;
560: ; check all monsters if any are in contact with quikman
561: KISSING:
562: LDY #$08
563: KISSME:
564: LDA LIVES
565: BEQ NEXTKISS
566: LDA SPRITEX
567: CMP SPRITEX,Y
568: BNE @skip3
569: LDA SPRITEY
570: SEC
571: SBC SPRITEY,Y
572: BCS @skip2
573: EOR #$FF
574: @skip2: CMP #$05
575: BCS @skip3
576: LDX #$FF
577: BNE ENGAGED ; is quikman engaged with a monster?
578: @skip3: LDA SPRITEY
579: CMP SPRITEY,Y
580: BNE NEXTKISS
581: LDA SPRITEX
582: SEC
583: SBC SPRITEX,Y
584: BCS @skip4
585: EOR #$FF
586: @skip4: CMP #$05
587: BCC ENGAGED ; is quikman engaged with a monster?
588: ;
589: NEXTKISS:
590: DEY ; next monster
591: DEY ; X,Y coord pair check
592: BNE KISSME
593: JMP MONSTERS ; quikman is still freely running!
594: ;
595: ENGAGED:
596: TYA
597: LSR
598: TAX
599: LDA SPRITEIMG1,X
600: CMP #$70 ; is monster returning to cage?
601: BEQ NEXTKISS
602: CMP #$A0 ; is monster fleeing?
603: BNE DEAD ; no, quikman bites the dust
604: LDA #$09 ; ahah! caught a little sickly one!
605: STA DIGIT ; in hundreds
606: LDA FLEEINGSCORE
607: STA POINTS ; fleeing monster score
608: ASL ; next is worth x2 bonus
609: STA FLEEINGSCORE
610: TYA
611: PHA
612: TXA
613: PHA
614: LDA #$D0
615: STA VIC+$0A
616: LDA SPRITE
617: EOR SPRITEMASK,X
618: STA SPRITE
619: LDX PPILLTIMER
620: LDA #$01
621: JSR PAUSE
622: LDA #$A8 ; quikman smiles
623: STA SPRITEIMG1
624: LDA #$01
625: JSR PAUSE
626: LDA JIFFYL
627: CLC
628: ADC #$20
629: @loop: LDY JIFFYL
630: INY
631: @loop1: CPY JIFFYL
632: BNE @loop1
633: STX PPILLTIMER
634: INC VIC+$0A
635: CMP JIFFYL
636: BNE @loop ; wait up to a jiffy
637: LDA #$00
638: STA VIC+$0A
639: PLA
640: TAX
641: PLA
642: TAY
643: LDA #$70 ; monster runs back to cage
644: STA SPRITEIMG1,X
645: LDA SPRITE
646: EOR SPRITEMASK,X
647: STA SPRITE
648: DEX
649: LDA MONSTERCLR,X
650: STA SPRITECLR+1,X
651: JMP NEXTKISS ; is there another monster here?
652: ;
653: DEAD:
654: PLA ; remove quikman's call to NPC from stack
655: PLA ; because he just died . . .
656: LDX #$06
657: @oops: LDA #$10
658: JSR PAUSE
659: DEX
660: BPL @oops
661: ; death sequence
662: LDA #$01 ; only feature quikman dying
663: STA SPRITE
664: LDA #$B0 ; low-order byte of 1st quikman image
665: STA SPRITEIMG1
666: LDX #$01
667: @loop: LDA QUIKMANCLR,X ; reset monsters starting colors
668: STA SPRITECLR,X ; into their sprite color registers
669: LDA #$80 ; reset monsters as looking down
670: STA SPRITEIMG1,X
671: INX
672: CPX #$05
673: BNE @loop
674: LDA #$F0
675: STA VIC+$0C
676: LDA #$20
677: STA FRAME ; rotate quikman 8 times
678: @loop1: LDA SPRITEIMG1
679: CMP #$D0 ; are we at the 4th quikman image?
680: BCC @skip
681: INC VIC+$0C
682: LDA #$B0 ; reset to 1st quikman image
683: @skip: CLC
684: ADC #$08 ; advance to next image
685: STA SPRITEIMG1
686: DEC VIC+$0C ; wooosh!
687: DEC VIC+$0C
688: DEC VIC+$0C
689: LDA #$04
690: JSR PAUSE
691: DEC FRAME
692: BNE @loop1 ; repeat next sequence
693: LDX #$D8
694: STX SPRITEIMG1 ; explode!
695: LDA #$0A
696: JSR PAUSE
697: LDX #$E0
698: STX SPRITEIMG1 ; smoke!
699: LDA #$08
700: JSR PAUSE
701: LDX #$E8
702: STX SPRITEIMG1 ; dust!
703: LDA #$06
704: JSR PAUSE
705: LDA #$00
706: STA SPRITE
707: STA VIC+$0C
708: LDA #$64
709: JSR PAUSE
710: DEC LIVES
711: BEQ FINALITY ; any lives remaining?
712: LDX LIVES
713: LDA #$1B
714: STA $1FE4,X ; avatar explodes
715: LDA #$0A
716: JSR PAUSE
717: LDA #$1C
718: STA $1FE4,X ; avatar smoking
719: LDA #$08
720: JSR PAUSE
721: LDA #$1D
722: STA $1FE4,X ; avatar dusted
723: LDA #$01
724: STA SPRITE
725: LDA #$D8
726: STA SPRITEIMG1
727: JSR INITVARS
728: LDA #$06
729: JSR PAUSE
730: LDX LIVES
731: LDA #$20
732: STA $1FE4,X ; erase the avatar
733: JMP RESETCHR ; quikman still has life -- try again!
734: ;
735: FINALITY:
736: LDY #00
737: STY SPRITE
738: JSR GAMEOVER
739: LDA #$F0 ; 4-second pause
740: JSR PAUSE
741: JMP DEMO ; this game is really over now
742: ;
743: ;*********************************************************************
744: MONSTERS:
745: LDA #$04
746: STA $00 ; start with monster #4
747: ;
748: DOMONSTER:
749: LDA $00
750: TAY
751: ASL ; x2
752: STA $01
753: LDX PENALTY-1,Y
754: BEQ ITMOVES ; is this monster free to roam?
755: LDA FRAME
756: AND #$03
757: BEQ @anim
758: DEX ; no, countdown to freedom
759: STX PENALTY-1,Y
760: @anim: LDA #$98 ; reset monster as caged, but coming out
761: CPX #$10
762: BCC @eyes
763: TXA
764: AND #$08
765: BNE @skip
766: LDA #$78 ; reset monster as caged looking right
767: BNE @eyes
768: @skip: LDA #$88 ; reset monster as caged looking left
769: @eyes: STA SPRITEIMG1,Y
770: LDX $01
771: LDA SPRITEX,X
772: AND #$F8
773: STA SPRITEX,X
774: ;
775: NEXTMONSTER:
776: DEC $00 ; process next monster
777: BNE DOMONSTER
778: INC FRAME
779: INC $00
780: LDA SPRITEY+$02
781: CMP #$58 ; no bonus at cage row
782: BEQ @fini
783: AND #$07
784: ORA SPRITEX+$02
785: AND #$0F
786: BEQ DOMONSTER
787: LDA CHEWING
788: BNE @skip1 ; is quikman eating a dot?
789: @fini: RTS ; no, we're done
790: @skip1: LDA DOTS
791: AND #$01
792: BNE @fini ; quikman can swallow every other dot faster
793: JSR SPRITES ; yes, chasing monsters get another turn
794: JMP MONSTERS
795: ;
796: ITMOVES:
797: LDA SPRITEIMG1,Y
798: CMP #$70
799: BNE @go ; small ghost going home?
800: LDX $01 ; yes, get pairing index
801: LDA SPRITEX,X
802: CMP #$50
803: BNE @caged
804: LDA SPRITEY,X
805: CMP #$48
806: BNE @caged ; is monster above cage ($50,$48 coord) doorway ?
807: INC SPRITEY,X ; move into doorway
808: LDX #$01
809: STX $4E,Y ; make direction DOWN to go into cage
810: @caged: LDA SPRITEY,X
811: CMP #$58
812: BNE @skip1 ; is monster at cage row level?
813: TYA
814: ASL
815: ASL
816: ASL
817: CLC
818: ADC #$40
819: CMP SPRITEX,X ; is monster inside cage?
820: BNE @skip1
821: LDA #$98 ; restore monster caged image
822: STA SPRITEIMG1,Y
823: LDA CAGEDATA-1,Y
824: EOR #$FF
825: LSR
826: ADC #$20 ; compute waiting room time
827: STA PENALTY-1,Y ; monster is waiting
828: JMP NEXTMONSTER ; done moving
829: @go: LDA FRAME
830: AND #$01
831: BEQ @cont ; check for powerpill active?
832: LDA SPRITEIMG1,Y
833: CMP #$A0 ; this monster IS fleeing
834: BEQ NEXTMONSTER ; skip its turn
835: @cont: LDX $01 ; no, get pairing index
836: LDA SPRITEX,X
837: CMP #$50
838: BNE @skip1
839: LDA SPRITEY,X
840: CMP #$58
841: BNE @skip1 ; is monster in cage ($50,$58 coord) doorway ?
842: DEC SPRITEY,X ; move it a pixel UP to force it through the closed door
843: LDX #$03
844: STX $4E,Y ; make direction UP to get out of cage
845: BNE @skip3
846: @skip1: LDA SPRITEX,X
847: BNE @skip2 ; is monster against the left-side of the tunnel?
848: LDX $00
849: STA $4E,X ; force a change of direction to the right
850: @skip2: CMP #$9F ; is monster against the right-side of the tunnel?
851: BNE @skip3
852: LDX $00
853: LDA #$02
854: STA $4E,X ; force a change of direction to the left
855: @skip3: LDY #$00
856: LDX #$04
857: @loop1: STX MONMOVE-1,Y ; preset move priority as 0=right,1=down,2=left,3=up
858: INY
859: DEX
860: BNE @loop1
861: LDY $01 ; start of monster's calculated move
862: LDA SPRITEX,Y
863: AND #$07
864: BEQ @skip4 ; is monster horizontally aligned with a screen cell?
865: LDA SPRITEY,Y
866: AND #$07
867: BNE @skip5 ; is monster vertically aligned with a screen cell?
868: @skip4: JSR AI ; yes, check to see if a direction change is in its future
869: CLC
870: BCC @skip6
871: @skip5: LDX $4E,Y ; not in a position to make a direction change,
872: STX $61 ; so just keep monster going in its current direction
873: @skip6: LDY #$00
874: STY $04
875: @loop2: LDX $61,Y
876: TXA
877: LDX $00
878: EOR $4E,X
879: CMP #$02
880: BEQ @skip7 ; don't allow monsters to reverse direction on their own
881: LDX $61,Y
882: STX NEWDIR
883: LDY $00
884: LDX $4E,Y
885: STX OLDDIR
886: JSR MAZEMOVE ; validate
887: BCC MAKEMOVE ; is this a good move?
888: @skip7: INC $04
889: LDY $04
890: CPY #$04
891: BNE @loop2
892: LDY $00 ; reverse direction
893: LDA OLDDIR
894: EOR #$02
895: STA $4E,Y
896: JMP NEXTMONSTER
897: ;
898: MAKEMOVE:
899: LDY $00 ; commit to this move
900: LDX NEWDIR
901: STX $4E,Y ; save as monster's current direction
902: LDA SPRITEIMG1,Y
903: CMP #$70 ; running back to cage
904: BEQ @mommy ; to give birth again
905: CMP #$A0 ; fleeing from quikman
906: BEQ @anim
907: TXA
908: ASL ; multiply by 8 to get address
909: ASL
910: ASL
911: CLC
912: ADC #$78 ; add base offset
913: @anim: STA SPRITEIMG1,Y
914: @jmp: JMP NEXTMONSTER
915: @mommy:
916: LDY $01 ; start of monster's calculated move
917: LDA SPRITEX,Y
918: ORA SPRITEY,Y
919: AND #$02
920: BEQ @jmp
921: JMP ITMOVES
922: ;
923: ; monster's artificial intelligence
924: AI:
925: ; first, preload $61-$64 with "best" moves this monster can make
926: ; to give quikman the kiss of death
927: LDX $01
928: LDA $51,X ; retrieve this monster's "X" knowledge where quikman was
929: CMP SPRITEX,X ; aligned?
930: BNE @math1 ; nope, compute intersect
931: LDA JIFFYL
932: AND #$01
933: BEQ @skip1 ; flip a coin
934: @math1: SEC
935: SBC SPRITEX,X
936: BCS @skip1
937: LDY #$02
938: STY MONMOVE ; LEFT is better
939: LDY #$00
940: STY $64 ; RIGHT is worse
941: BEQ @skip2
942: @skip1: LDY #$00
943: STY MONMOVE ; RIGHT is better
944: LDY #$02
945: STY $64 ; LEFT is worse
946: @skip2: LDA $52,X ; retrieve this monster's "Y" knowledge where quikman was
947: CMP SPRITEY,X ; aligned?
948: BNE @math2 ; nope, compute intersect
949: LDA JIFFYL
950: AND #$01
951: BEQ @skip3 ; flip a coin
952: @math2: SEC
953: SBC SPRITEY,X
954: BCS @skip3
955: LDY #$03
956: STY $62 ; UP is 2nd best
957: LDY #$01
958: STY $63 ; DOWN is 3rd best
959: BNE AI2
960: @skip3: LDY #$01 ; DOWN is 2nd best
961: STY $62
962: LDY #$03 ; UP is 3rd best
963: STY $63
964: ;
965: ; next, prioritize monster move, based upon its current location in respect to
966: ; its knowledge where quikman was considered last.
967: AI2: LDX $01
968: TXA
969: ASL
970: ASL
971: ASL ; x8
972: CMP JIFFYL
973: BCS @skip3 ; ignore priority during this time window
974: LDA $51,X
975: SEC
976: SBC SPRITEX,X
977: BCS @skip1
978: EOR #$FF
979: @skip1: STA $69
980: LDA $52,X
981: SEC
982: SBC SPRITEY,X
983: BCS @skip2
984: EOR #$FF
985: @skip2: CMP $69
986: BCC @skip3 ; can monster improve upon order of choices?
987: LDX MONMOVE ; swap 1st & 2nd choices
988: LDY $62
989: STX $62
990: STY MONMOVE
991: LDY $63 ; swap 3rd & 4th choices
992: LDX $64
993: STY $64
994: STX $63
995: @skip3: LDY $00
996: LDA SPRITEIMG1,Y
997: CMP #$A0 ; is this monster fleeing?
998: BNE @fini ; no, chase!
999: LDX MONMOVE ; swap 1st & 3rd choices
1000: LDY $63
1001: STX $63
1002: STY MONMOVE
1003: @fini: RTS
1004: ;
1005: ; reset monsters to default starting location
1006: INITVARS:
1007: LDX #$00
1008: @loop1: LDA STARTPOS,X ; reset each sprite starting position
1009: STA SPRITEX,X
1010: INX
1011: CPX #$0A ; 5 sprites per X,Y coordinate pair
1012: BNE @loop1
1013: LDY #$00
1014: @loop2: LDX CAGEDATA,Y
1015: STX PENALTY,Y
1016: INY
1017: CPY #$10
1018: BNE @loop2
1019: RTS
1020: ;
1021: ; restore sound/screen
1022: RESTORE:
1023: INC FRUITLEVEL
1024: LDA #$00
1025: STA SPRITE ; turn off all sprites
1026: LDA #$0E ; black / blue
1027: STA VIC+$0F ; background / border color
1028: LDA #$93 ; Shift-HOME is clearscreen
1029: JSR CHROUT ; print it
1030: LDX #$06 ; blue
1031: JSR MAZEPAINT
1032: LDX #$15 ; skip 1st & last row
1033: @draw: LDA MAZEDATA,X
1034: STA $1E00,X
1035: LDA MAZEDATA+$E3,X
1036: STA $1EE3,X
1037: INX
1038: BNE @draw
1039: STX DOTS ; and no dots are eaten (yet)
1040: @loop2: LDA QUIKMANCLR,X ; reset monsters starting colors
1041: STA SPRITECLR,X ; into their sprite color registers
1042: LDA #$80 ; reset monsters as looking down @ quikman
1043: STA SPRITEIMG1,X
1044: LDA #$1C
1045: STA SPRITEIMG2,X
1046: INX
1047: CPX #$05
1048: BNE @loop2
1049: LDY LIVES ; paint lives remaining
1050: JSR GAMEOVER
1051: DEY
1052: BEQ @next
1053: @loop4: LDA #$19 ; quikman character
1054: STA $1FE4,Y ; bottom-left of screen
1055: LDA #$07 ; use yellow
1056: STA $97E4,Y ; and paint it
1057: DEY
1058: BNE @loop4
1059: @next: LDY FRUITLEVEL
1060: LDX #$00
1061: @loop5: CPY #$0C ; are we at the last level (key)?
1062: BCC @skip1
1063: LDY #$0C ; only keys remain
1064: @skip1: LDA FRUIT,Y ; fruit character
1065: STA $1FF1,X ; bottom right of screen
1066: LDA FRUITCLR,Y ; get its color
1067: STA $97F1,X ; and paint it
1068: CPY #$00 ; did we paint the cherry yet?
1069: BEQ BEGIN ; if so, we're done
1070: INX
1071: STX $FF
1072: LDA FRUITLEVEL
1073: SEC
1074: SBC $FF
1075: TAY
1076: CPX #$07 ; no more than 7 fruits to display
1077: BNE @loop5
1078: BEGIN: ;LDA LIVES ; allow new screen to be processed
1079: ; BEQ @cont
1080: ; LDA #$40
1081: ;@cont: JSR PAUSE ; by player
1082: RTS
1083: ;
1084: ; recolor maze with some new paint in X
1085: MAZEPAINT:
1086: LDY #$15
1087: @loop: LDA MAZEDATA,Y
1088: CMP #$29
1089: BCC @page2
1090: CMP #$3F
1091: BCS @page2
1092: LDA #$01
1093: TXA
1094: STA $9600,Y
1095: @page2: LDA MAZEDATA+$E3,Y
1096: CMP #$29
1097: BCC @skip
1098: CMP #$3F
1099: BCS @skip
1100: TXA
1101: STA $96E3,Y
1102: @skip: INY
1103: BNE @loop
1104: RTS
1105: ;
1106: ; if move is valid, carry flag will be clear on return
1107: MAZEMOVE:
1108: LDY $01 ; get X,Y coord index
1109: LDA OLDDIR ; get the last direction moving
1110: AND #$01 ; mask UP/DOWN
1111: BEQ @skip1 ; is direction LEFT/RIGHT?
1112: INY ; no, then fetch the "Y" coordinate
1113: @skip1: LDA SPRITEX,Y ; get one of sprite's coord
1114: AND #$07
1115: BEQ MAZEANY ; at a crossroad? check move in any 4-directions
1116: LDA NEWDIR
1117: CMP OLDDIR
1118: BEQ MYMOVE ; still want to move in the same direction?
1119: EOR OLDDIR
1120: CMP #$02
1121: BEQ MYMOVE ; is this a reverse direction request?
1122: SEC ; no new move made
1123: RTS
1124: ;
1125: MAZEANY:
1126: JSR SPRITEPREP
1127: LDA $F8 ; reset screen hi-byte back into saved maze data
1128: SEC
1129: SBC #$04
1130: STA $F8
1131: LDX NEWDIR
1132: CPX #$02
1133: BCS @skip2 ; is X (2=left) or (3=up)?
1134: LDA $F7 ; no
1135: CLC
1136: ADC PEEKAHEAD,X ; look (0=right) or (1=down)
1137: BCC @skip1
1138: INC $F8
1139: @skip1: STA $F7
1140: CLC
1141: BCC @skip4 ; go validate
1142: @skip2: LDA $F7
1143: SEC
1144: SBC PEEKAHEAD-2,X
1145: BCS @skip3 ; look (2=left) or (3=up)
1146: DEC $F8
1147: @skip3: STA $F7
1148: @skip4: LDY #$00 ; validate
1149: LDA ($F7),Y
1150: CMP #$29 ; is this direction into a maze wall?
1151: BCC MYMOVE ; good move?
1152: RTS
1153: ;
1154: ; continue this sprite's move in whatever is loaded in NEWDIR
1155: MYMOVE:
1156: LDA NEWDIR
1157: ASL ; 0=0, 1=2, 2=4, 3=6, 4=8
1158: TAX
1159: LDY $01
1160: LDA INERTIA,X
1161: CLC
1162: ADC SPRITEX,Y
1163: STA SPRITEX,Y
1164: LDA INERTIA+1,X
1165: CLC
1166: ADC SPRITEY,Y
1167: STA SPRITEY,Y
1168: CLC
1169: RTS
1170: ;
1171: ;*********************************************************************
1172: ; my very own sprite routines
1173: ; major custom hack for this maze game implementation
1174: SPRITES:
1175: LDA #$00 ; start with sprite #0
1176: STA $00 ; current sprite # to render
1177: @loop1: ASL
1178: STA $01 ; current sprite (x2) pairing index
1179: ASL
1180: ASL
1181: STA $02 ; current sprite (x8) image index
1182: LDX $00
1183: LDA SPRITE
1184: AND SPRITEMASK,X
1185: BEQ @skip2 ; nothing to do?
1186: LDA SPRITELAST ; what state was this sprite before?
1187: AND SPRITEMASK,X
1188: BEQ @skip1 ; it was "off"
1189: JSR ERASESPRITE ; was "on" before, and we still want it "on"
1190: @skip1: JSR SPRITEPREP ; new sprite, go turn it "on"
1191: JSR PREPMATRIX
1192: JSR RENDER
1193: JSR PLACEMATRIX
1194: JMP @next
1195: @skip2: LDA SPRITELAST
1196: AND SPRITEMASK,X
1197: BEQ @next ; still nothing to do? Then do nothing ...
1198: JSR ERASESPRITE ; make this sprite disappear
1199: @next: INC $00
1200: LDA $00
1201: CMP #$05 ; only 5-sprites needed in this game
1202: BNE @loop1
1203: LDX #$00
1204: @loop2: LDA SPRITE,X ; save copy of current sprite registers
1205: STA SPRITELAST,X
1206: INX
1207: CPX #$11 ; all 17 values, not including colors
1208: BNE @loop2
1209: RTS ; fini
1210: ;
1211: ; remove sprite from screen
1212: ERASESPRITE:
1213: JSR LASTSPRITEPREP
1214: JSR PREPMATRIX
1215: JSR RESTOREMATRIX
1216: RTS
1217: ;
1218: LASTSPRITEPREP:
1219: LDA $01 ; 0, 2, 4, 6, 8
1220: CLC
1221: ADC #<SPRITELAST
1222: BNE SPRITEPREP2
1223: ;
1224: ; prepares the following registers:
1225: ; $F7/$F8,$F9/$FA screen cell pointers for sprite position
1226: ; $FC/$FD,$FE/$FF color cell pointers for same
1227: SPRITEPREP:
1228: LDA $01 ; 0, 2, 4, 6, 8 index
1229: CLC
1230: ADC #<SPRITE
1231: SPRITEPREP2:
1232: TAX ; save this register index
1233: LDA $0201,X ; get "X" coordinate
1234: CMP #$A0
1235: BCC @skip1 ; is "X" at or beyond last column?
1236: SBC #$A0 ; yes, subtract 160-pixels wide
1237: @skip1: LSR ; and divide by 8-pixel width
1238: LSR
1239: LSR
1240: STA $F7 ; save column offset from left screen
1241: STA $FC ; save column offset from left color
1242: LDA SCRNPAGE ; get high order byte of screen memory page
1243: STA $F8
1244: LDA CLRPAGE ; get high order byte of screen color page
1245: AND #$FE ; make it and "even" number
1246: STA $FD ; save high order
1247: LDA $0202,X ; get "Y" coordinate
1248: CMP #$B8
1249: BCC @skip2 ; is "Y" at or beyond last row?
1250: SBC #$B8 ; yes, subtract 184-pixels high
1251: @skip2: LSR ; and divide by 8-pixel height
1252: LSR
1253: LSR
1254: TAY
1255: BEQ @fini ; if on top row, no math required
1256: LDA $F7 ; get column offset
1257: @loop1: CLC
1258: ADC #$15 ; add 21 for next row
1259: BCC @skip3 ; overflow to next page?
1260: INC $F8 ; yes, increment high order bytes
1261: INC $FD
1262: @skip3: STA $F7 ; save column offset
1263: STA $FC
1264: DEY
1265: BNE @loop1 ; do for each "row"
1266: @fini: LDA $F8 ; copy high-order bytes
1267: STA $FA ; for overflow sprite character
1268: LDA $FD ; do the same
1269: STA $FF ; for color
1270: ; determine whether overflow character is to the right or down
1271: LDA $0201,X ; get "X" coordinate
1272: AND #$07
1273: BEQ @vert ; 0 assumes moving up/down?
1274: LDA $F7 ; ok, moving left/right then ...
1275: CLC
1276: ADC #$01 ; make overflow character to the right
1277: BCC @savel
1278: @saveh: INC $FA
1279: INC $FF
1280: @savel: STA $F9 ; save character offset
1281: STA $FE ; save color offset
1282: RTS
1283: @vert: LDA $F7
1284: CLC
1285: ADC #$15 ; make overflow character below
1286: BCC @savel
1287: BCS @saveh
1288: ;
1289: ; prepares saveback buffers for restoring, should a larger-numbered sprite be
1290: ; overlapping any part of a smaller-numbered sprite
1291: PREPMATRIX:
1292: LDY #$00
1293: LDA ($F7),Y ; retrieve screen cell
1294: PHA
1295: LDY $01 ; 0, 2, 4, 6, 8 index
1296: LDA $0201,X
1297: AND #$07
1298: BNE @2cell
1299: LDA $0202,X
1300: AND #$07
1301: BEQ @start
1302: @2cell: LDY #$00
1303: LDA ($F9),Y ; retrieve overflow cell
1304: PHA
1305: LDY $01 ; 0, 2, 4, 6, 8 index
1306: INY
1307: @start: TYA
1308: TAX
1309: @loop: PLA
1310: @retry: CMP $01
1311: BCC @skip ; is A < ME ?
1312: CMP #$0A
1313: BCS @skip ; is A >= MAX ?
1314: ; there is a sprite # greater than us on top ...
1315: TAY
1316: LDA SAVEBACK,Y ; get > sprite# saveback info
1317: CLC
1318: BCC @retry
1319: @skip: STA SAVEBACK,X
1320: DEX
1321: CPX $01
1322: BEQ @loop
1323: RTS
1324: ;
1325: ; restores the sprite's saveback buffer to the screen squares it occupies
1326: ; erasure part 3
1327: RESTOREMATRIX:
1328: LDX $01
1329: LDA SAVEBACK,X ; recover character
1330: LDY #$00
1331: STA ($F7),Y ; restore to screen
1332: LDA #$01
1333: STA ($FC),Y ; just leave "white" behind
1334: LDA SPRITELAST+1,X
1335: AND #$07
1336: BNE @2cell
1337: LDA SPRITELAST+2,X
1338: AND #$07
1339: BEQ @fini
1340: @2cell: LDA SAVEBACK+1,X
1341: STA ($F9),Y ; restore to screen
1342: LDA #$01
1343: STA ($FE),Y ; only color overflow if X or Y are offset
1344: @fini: RTS
1345: ;
1346: ; render sprite within its character matrix by merging its image over its saveback
1347: ; $05/$06 points to graphic character
1348: RENDER:
1349: LDX $00 ; 0-4
1350: LDA SPRITEIMG1,X
1351: STA $05
1352: LDA SPRITEIMG2,X
1353: STA $06
1354: LDX $01 ; 0,2,4,6,8
1355: LDA SPRITEY,X
1356: AND #$07
1357: STA $03
1358: TAX ; X will hold the sprite's Y coord
1359: LDY #$00 ; erase temp image matrix area
1360: TYA
1361: @loop1: STA CASSBUFF+$20,Y
1362: INY
1363: CPY #$10 ; customized from 4 to 2 character cells
1364: BNE @loop1
1365: TAY ; copy 8x8 character image into temp matrix
1366: @loop2: LDA ($05),Y ; $05/$06 points to character matrix to draw
1367: STA CASSBUFF+$20,X
1368: INX
1369: INY
1370: CPY #$08
1371: BNE @loop2
1372: LDX $01
1373: LDA SPRITEX,X
1374: AND #$07 ; get modulos on X coordinate
1375: TAY
1376: BEQ @skip1 ; if its zero, no shifting required
1377: @loop3: LDX #$00
1378: @loop4: CLC
1379: ROR CASSBUFF+$20,X
1380: ROR CASSBUFF+$28,X
1381: INX
1382: CPX #$08
1383: BNE @loop4
1384: DEY
1385: BNE @loop3
1386: @skip1: STY $FB ; Y is always zero here
1387: @loop5: LDA $01 ; index x2
1388: CLC
1389: ADC $FB
1390: TAX ; X is sprite custom character
1391: LDA #$1C ; 1st page is where sprites are stored
1392: STA $06
1393: LDA SAVEBACK,X
1394: CMP #$80
1395: BCC @skip2 ; is character reversed?
1396: LDY #$80 ; yes, use start of ROM character set
1397: STY $06
1398: @skip2: AND #$1F ; get modulos of first 32-characters
1399: ASL ; and multiply by 8-pixel height
1400: ASL
1401: ASL
1402: STA $05 ; save as low-order byte index
1403: LDA SAVEBACK,X
1404: AND #$60 ; mask 01100000
1405: LSR ; divide by 16
1406: LSR
1407: LSR
1408: LSR
1409: LSR
1410: CLC
1411: ADC $06 ; add result to high-order page index
1412: STA $06
1413: LDY #$00
1414: LDA $FB
1415: ASL
1416: ASL
1417: ASL
1418: TAX
1419: @loop6: LDA ($05),Y ; copy 8x8 character image into behind matrix
1420: STA CASSBUFF+$30,X
1421: INX
1422: INY
1423: CPY #$08
1424: BNE @loop6
1425: INC $FB
1426: LDA $FB
1427: CMP #$02 ; only a 2-cell sprite now
1428: BNE @loop5
1429: LDY #$00
1430: LDA $02 ; 0, 8, 16, 24, 32
1431: ASL ; 0, 16, 32, 48, 64
1432: TAX
1433: @loop7: LDA CASSBUFF+$20,Y
1434: ORA CASSBUFF+$30,Y
1435: STA $1C00,X
1436: INX
1437: INY
1438: CPY #$10 ; customized from 4 to 2 character cells
1439: BNE @loop7
1440: RTS
1441: ;
1442: ; puts the sprite character matrix on the screen
1443: PLACEMATRIX:
1444: LDA $01 ; 0, 2, 4, 6, 8
1445: LDY #$00
1446: STA ($F7),Y
1447: LDX $00
1448: LDA SPRITECLR,X
1449: STA ($FC),Y
1450: LDX $01
1451: LDA SPRITEX,X
1452: AND #$07
1453: BNE @color
1454: LDA SPRITEY,X
1455: AND #$07
1456: BEQ @fini
1457: @color: LDA ($FC),Y
1458: STA ($FE),Y ; only color overflow if X & Y are offset
1459: INX
1460: TXA
1461: STA ($F9),Y
1462: @fini: RTS
1463: ;
1464: ;*********************************************************************
1465: ; This section is dedicated to background processing, accomplished
1466: ; via the keyboard IRQ service, called 60-times per second (jiffy).
1467: BACKGROUND:
1468: CLD
1469: LDA $1EDC
1470: CMP #$C5
1471: BNE @esc
1472: LDA #$09
1473: STA $96DC ; paint the cage door
1474: @esc: LDA JIFFYL
1475: AND #$07
1476: BNE FLASH
1477: LDA PPILLTIMER ; drain powerpill
1478: BEQ FLASH ; is there still power left?
1479: CMP #$1F ; yes ... but are they
1480: BCS DRAIN ; getting confidence back?
1481: AND #$03 ; yes, let's warn quikman
1482: BNE DRAIN
1483: LDY #$04
1484: @pp1: LDA SPRITEIMG1,Y
1485: CMP #$A0 ; is monster fleeing?
1486: BNE @pp2
1487: LDA SPRITECLR,Y
1488: EOR #$07 ; flash white / blue
1489: STA SPRITECLR,Y
1490: @pp2: DEY
1491: BNE @pp1
1492: DRAIN: DEC PPILLTIMER
1493: BNE FLASH
1494: LDY #$04
1495: @loop: LDA MONSTERCLR-1,Y
1496: STA SPRITECLR,Y ; restore all monsters to their default colors
1497: LDA SPRITEIMG1,Y
1498: CMP #$70 ; is monster already going home?
1499: BEQ @next
1500: LDA #$98 ; restore monster chasing image
1501: STA SPRITEIMG1,Y
1502: @next: DEY
1503: BNE @loop
1504: FLASH: LDA FLASHPILL ; powerpill flash
1505: CMP #$1E ; 30-jiffies?
1506: BNE @skip1
1507: LDX #$00 ; reset counter
1508: STX FLASHPILL
1509: @loop1: LDA $1CF8,X ; custom graphic char
1510: EOR $8288,X ; rom graphic char
1511: STA $1CF8,X ; redraw 8x8 char cell
1512: INX
1513: CPX #$08
1514: BNE @loop1
1515: LDA #$FE ; render monster feet
1516: EOR $1C9F ; custom graphic char
1517: STA $1C9F ; redraw caged monster
1518: STA $1CA7 ; redraw fleeing monster
1519: STA $1C8F ; looking left
1520: STA $1C97 ; looking up
1521: LDA #$7F ; render monster feet
1522: EOR $1C7F ; custom graphic char
1523: STA $1C7F ; looking right
1524: STA $1C87 ; looking down
1525: @skip1: INC FLASHPILL
1526: LDA LIVES
1527: BNE @hi ; playing?
1528: LDA JIFFYL ; manufacture a moving quikman 'spirit'
1529: BEQ @hi
1530: LDA JIFFYM
1531: AND #$03
1532: BNE @hi
1533: INC DEMOQMAN
1534: LDA DEMOQMAN
1535: AND #$03
1536: ASL ; x2 for pair
1537: TAY ; for the monsters to 'chase'
1538: LDA CAGEDATA+$08,Y
1539: STA SPRITEX
1540: LDA CAGEDATA+$09,Y
1541: STA SPRITEY
1542: @hi: LDX #$00 ; yes
1543: @loop3: LDA SCORE-$9200,X ; check current score against high score
1544: CMP MAZEDATA-$9200+$0F,X
1545: BCC @top ; is quikman beating the high score?
1546: BNE @skip4 ; yes!
1547: INX
1548: CPX #$06
1549: BNE @loop3
1550: @skip4: LDX #$00
1551: @loop4: LDA SCORE-$9200,X ; woot!
1552: STA MAZEDATA-$9200+$0F,X
1553: INX
1554: CPX #$06
1555: BNE @loop4
1556: @top: LDX #$14 ; refresh top line
1557: @loop5: LDA MAZEDATA-$9200,X
1558: STA $1E00,X
1559: DEX
1560: BPL @loop5
1561: ADDPTS: LDY POINTS ; award points to score on screen
1562: BEQ CLRPTS
1563: @loop1: LDX DIGIT
1564: @loop2: LDA MAZEDATA-$9200,X
1565: CMP #$B9 ; reach "9" ?
1566: BEQ @skip2
1567: INC MAZEDATA-$9200,X ; ding!
1568: DEY
1569: BNE @loop1
1570: BEQ CLRPTS
1571: @skip2: LDA #$B0
1572: STA MAZEDATA-$9200,X ; wrap to "0"
1573: DEX ; and increment next order
1574: BNE @loop2
1575: CLRPTS: STY POINTS
1576: LDX #$00
1577: LDY #$00
1578: @loop6: LDA PENALTY,X
1579: BNE @next ; not aware while caged
1580: LDA SPRITEIMG1+1,X
1581: CMP #$70
1582: BNE @go ; small ghost going home?
1583: LDA #$50
1584: STA $53,Y
1585: LDA #$48
1586: STA $54,Y
1587: BNE @next
1588: @go: LDA DOTS
1589: CMP #$A6 ; make them all "smart" with 5-dots or less
1590: BCS @skip5
1591: LDA CAGEDATA,X
1592: BEQ @skip5 ; is monster "smart"? Red one is ...
1593: CMP JIFFYL ; no, so check as often as it waits
1594: BNE @next ; is its wait time equal to the jiffy clock?
1595: @skip5: LDA SPRITEX ; update this monster's awareness to where quikman is
1596: STA $53,Y
1597: LDA SPRITEY
1598: STA $54,Y
1599: @next: INY
1600: INY
1601: INX
1602: CPX #$04
1603: BNE @loop6
1604: ;
1605: wahka: LDA CHEWING
1606: BEQ @skip1
1607: LDA #$91 ; start with an odd frequency
1608: STA VIC+$0C ; ignite a voice
1609: @skip1: LDA #$00 ; dot is swallowed
1610: STA CHEWING
1611: LDA VIC+$0C
1612: BEQ @next1 ; is this voice mute?
1613: LDA VIC+$0C
1614: AND #$01
1615: BEQ @skip3 ; is it even?
1616: LDA VIC+$0C
1617: CLC
1618: ADC #$10 ; increase tone
1619: CMP #$F1
1620: BCC @skip2 ; is voice too high?
1621: SEC
1622: SBC #$01 ; make it even
1623: @skip2: STA VIC+$0C
1624: CLC
1625: BCC @next1 ; goto next effect
1626: @skip3: LDA VIC+$0C
1627: SEC
1628: SBC #$10 ; drain tone
1629: STA VIC+$0C
1630: @next1: LDX CHOMP
1631: BEQ @skip4
1632: LDA JIFFYL
1633: AND #$01
1634: BNE @skip4
1635: LDA SNDBIT,X ; load tone data
1636: STA VIC+$0B
1637: DEC CHOMP
1638: @skip4: LDA EXTRAQMAN
1639: BNE @fini ; already got bonus life
1640: LDA SCORE+1-$9200
1641: CMP #$B1 ; did quikman just score 10,000-points?
1642: BNE @fini
1643: STA EXTRAQMAN
1644: LDX LIVES
1645: LDA #$19
1646: STA $1FE4,X
1647: LDA #$07
1648: STA $97E4,X
1649: INC LIVES ; reward
1650: @fini: JMP $EABF ; jump to hardware IRQ
1651: ;
1652: ; Pass A for number of jiffies to wait, while preserving X
1653: PAUSE: PHA
1654: TXA
1655: PHA
1656: JSR SPRITES ; redraw sprites
1657: PLA
1658: TAX
1659: PLA
1660: CLC
1661: ADC JIFFYL
1662: @loop: CMP JIFFYL
1663: BNE @loop
1664: RTS
1665: ;
1666: ; Y > 0 erase; Y = 0 display
1667: GAMEOVER:
1668: LDX #$08
1669: @loop: LDA GOTEXT,X ; GAME OVER
1670: CPY #$00
1671: BEQ @dead
1672: LDA #$20 ; space
1673: @dead: STA $1F17,X ; print character
1674: STA $1B17,X
1675: LDA #$02 ; red
1676: STA $9717,X
1677: DEX
1678: BPL @loop
1679: RTS
1680: ;
1681: ;*********************************************************************
1682: ; READ-ONLY DATA
1683: CAGEDATA: ; knowledge cycle time
1684: .byte $00, $33, $76, $F9
1685: ; right, up, left, right
1686: .byte $00, $03, $02, $00
1687: ; coordinate to consider going to upon release or demo
1688: .byte $A0, $28, $00, $28, $A0, $88, $00, $88
1689: GOTEXT: ; GAME OVER
1690: .byte $87, $81, $8D, $85, $A0, $8F, $96, $85, $92
1691: INERTIA: ; maintain direction
1692: .byte $01, $00, $00, $01, $FF, $00, $00, $FF
1693: PEEKAHEAD: ;
1694: .byte $01, $15
1695: SNDBIT: ; yummy sound effect
1696: .byte $00, $00, $C0, $B8, $B0, $A8, $B0, $B8, $C0, $C8
1697: STARTPOS: ;
1698: .byte $50, $88, $50, $48, $50, $58, $60, $58, $40, $58
1699: SPRITEMASK: ; really?
1700: .byte $01, $02, $04, $08, $10
1701: ;
1702: ;*********************************************************************
1703: ; Maze data (copied to RAM: $1A00 - $1BFF)
1704: ; Screen size: 24-rows by 21-columns
1705: .res $AC00 - *
1706: MAZEDATA:
1707: SCORE = * + $06
1708: .byte $93, $83, $8F, $92, $85, $BA, $B0, $B0, $B0, $B0, $B0, $B0, $A0, $A0, $A0, $B0, $B2, $B0, $B0, $B0, $B0
1709: .byte $37, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3D, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $38
1710: .byte $39, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $39, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $39
1711: .byte $39, $1E, $2F, $30, $1E, $2F, $2B, $2B, $30, $1E, $39, $1E, $2F, $2B, $2B, $30, $1E, $2F, $30, $1E, $39
1712: .byte $39, $1F, $2D, $2E, $1E, $2D, $2C, $2C, $2E, $1E, $32, $1E, $2D, $2C, $2C, $2E, $1E, $2D, $2E, $1F, $39
1713: .byte $39, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $39
1714: .byte $39, $1E, $33, $34, $1E, $31, $1E, $33, $3A, $3A, $3D, $3A, $3A, $34, $1E, $31, $1E, $33, $34, $1E, $39
1715: .byte $39, $1E, $1E, $1E, $1E, $39, $1E, $1E, $1E, $1E, $39, $1E, $1E, $1E, $1E, $39, $1E, $1E, $1E, $1E, $39
1716: .byte $35, $3A, $3A, $38, $1E, $3B, $3A, $3A, $34, $20, $32, $20, $33, $3A, $3A, $3C, $1E, $37, $3A, $3A, $36
1717: .byte $20, $20, $20, $39, $1E, $39, $20, $20, $20, $20, $20, $20, $20, $20, $20, $39, $1E, $39, $20, $20, $20
1718: .byte $3A, $3A, $3A, $36, $1E, $32, $20, $37, $3A, $29, $C5, $2A, $3A, $38, $20, $32, $1E, $35, $3A, $3A, $3A
1719: .byte $20, $20, $20, $20, $1E, $20, $20, $39, $20, $20, $20, $20, $20, $39, $20, $20, $1E, $20, $20, $20, $20
1720: .byte $3A, $3A, $3A, $38, $1E, $31, $20, $35, $3A, $3A, $3A, $3A, $3A, $36, $20, $31, $1E, $37, $3A, $3A, $3A
1721: .byte $20, $20, $20, $39, $1E, $39, $20, $20, $20, $20, $20, $20, $20, $20, $20, $39, $1E, $39, $20, $20, $20
1722: .byte $37, $3A, $3A, $36, $1E, $32, $20, $33, $3A, $3A, $3D, $3A, $3A, $34, $20, $32, $1E, $35, $3A, $3A, $38
1723: .byte $39, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $39, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $39
1724: .byte $39, $1E, $33, $38, $1E, $33, $3A, $3A, $34, $1E, $32, $1E, $33, $3A, $3A, $34, $1E, $37, $34, $1E, $39
1725: .byte $39, $1F, $1E, $39, $1E, $1E, $1E, $1E, $1E, $1E, $20, $1E, $1E, $1E, $1E, $1E, $1E, $39, $1E, $1F, $39
1726: .byte $3B, $34, $1E, $32, $1E, $31, $1E, $33, $3A, $3A, $3D, $3A, $3A, $34, $1E, $31, $1E, $32, $1E, $33, $3C
1727: .byte $39, $1E, $1E, $1E, $1E, $39, $1E, $1E, $1E, $1E, $39, $1E, $1E, $1E, $1E, $39, $1E, $1E, $1E, $1E, $39
1728: .byte $39, $1E, $33, $3A, $3A, $3E, $3A, $3A, $34, $1E, $32, $1E, $33, $3A, $3A, $3E, $3A, $3A, $34, $1E, $39
1729: .byte $39, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $1E, $39
1730: .byte $35, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $3A, $36
1731: BANNERMSG: ; ©2008 RHURST F1START
1732: .byte $3F, $B2, $B0, $B0, $B8, $A0, $92, $88, $95, $92, $93, $94, $A0, $A0, $00, $01, $93, $94, $81, $92, $94
1733: FRUITSCORE:
1734: .byte $01, $03, $05, $07, $0A, $14, $1E, $32
1735: ;
1736: ;*********************************************************************
1737: ; Custom character data (copied to RAM: $1C00 - $1DFF)
1738: .assert * = $AE00, error, "Graphics not at $AE00"
1739: .byte $FF, $EA, $EF, $EF, $EB, $EF, $EF, $FF ; @ [F - then sprite #0
1740: .byte $FC, $EC, $EC, $EC, $EC, $EC, $EC, $FC ; A 1] - then sprite #0
1741: .byte $38, $7C, $FE, $92, $FE, $FE, $FE, $AA ; B sprite #1 - red
1742: .byte $00, $00, $00, $00, $00, $00, $00, $00 ; C
1743: .byte $38, $7C, $FE, $92, $FE, $FE, $FE, $AA ; D sprite #2 - green
1744: .byte $00, $00, $00, $00, $00, $00, $00, $00 ; E
1745: .byte $38, $7C, $FE, $92, $FE, $FE, $FE, $AA ; F sprite #3 - cyan
1746: .byte $00, $00, $00, $00, $00, $00, $00, $00 ; G
1747: .byte $38, $7C, $FE, $92, $FE, $FE, $FE, $AA ; H sprite #4 - yellow
1748: .byte $00, $00, $00, $00, $00, $00, $00, $00 ; I
1749: ;
1750: QUIKMANCLR: ; yellow
1751: .byte $07
1752: MONSTERCLR: ; red, green, cyan, yellow
1753: .byte $02, $05, $03, $07
1754: FRUIT: ; cherry, strawberry, 2-peach, 2-apple, 2-pineapple, 2-tbird, 2-bell, key
1755: .byte $21, $22, $23, $23, $24, $24, $25, $25, $26, $26, $27, $27, $28
1756: FRUITCLR: ; red, red, 2-yellow, 2-red, 2-green, 2-magenta, 2-yellow, cyan
1757: .byte $02, $02, $07, $07, $02, $02, $05, $05, $04, $04, $07, $07, $03
1758: ;
1759: ; resume graphic character data
1760: .res $AE70 - *
1761: .byte $00, $38, $7C, $54, $7C, $7C, $54, $00 ; N small ghost
1762: .byte $1C, $3E, $7F, $64, $7F, $7F, $7F, $55 ; O ghost right
1763: .byte $1C, $3E, $7F, $7F, $49, $7F, $7F, $55 ; P ghost down
1764: .byte $38, $7C, $FE, $26, $FE, $FE, $FE, $AA ; Q ghost left
1765: .byte $38, $7C, $FE, $FE, $FE, $FE, $FE, $AA ; R ghost up
1766: .byte $38, $7C, $FE, $92, $FE, $82, $FE, $AA ; S ghost caged
1767: .byte $38, $7C, $FE, $92, $FE, $82, $FE, $54 ; T ghost fleeing
1768: .byte $3C, $7E, $BD, $FF, $BD, $C3, $7E, $3C ; U smiley
1769: .byte $3C, $7E, $FF, $FF, $FF, $FF, $7E, $3C ; V pacman closed
1770: .byte $3E, $7C, $F8, $F0, $F0, $F8, $7C, $3E ; W pacman right
1771: .byte $3C, $7E, $FF, $FF, $E7, $C3, $81, $00 ; X pacman down
1772: .byte $7C, $3E, $1F, $0F, $0F, $1F, $3E, $7C ; Y pacman left
1773: .byte $00, $81, $C3, $E7, $FF, $FF, $7E, $3C ; Z pacman up
1774: .byte $00, $10, $10, $6C, $10, $10, $00, $00 ; [ explosion
1775: .byte $10, $44, $28, $C6, $28, $44, $10, $00 ; # smoke
1776: .byte $92, $44, $00, $82, $00, $44, $92, $00 ; ] dust
1777: .byte $00, $00, $00, $18, $18, $00, $00, $00 ; ^ dot
1778: .byte $00, $3C, $7E, $7E, $7E, $7E, $3C, $00 ; <- powerpill (animated)
1779: .byte $00, $00, $00, $00, $00, $00, $00, $00 ;$20 empty space
1780: .byte $04, $08, $18, $24, $62, $F7, $F2, $60 ; ! cherry
1781: .byte $10, $7C, $FE, $AA, $D6, $AA, $54, $28 ; " strawberry
1782: .byte $20, $10, $7C, $FE, $FE, $FE, $7C, $38 ; # peach
1783: .byte $08, $10, $7C, $FE, $FE, $FE, $7C, $28 ; $ apple
1784: .byte $08, $10, $38, $38, $7C, $FE, $FE, $6C ; % pear
1785: .byte $10, $30, $92, $FE, $7C, $38, $10, $28 ; & tbird
1786: .byte $10, $38, $7C, $7C, $7C, $7C, $FE, $10 ; ' bell
1787: .byte $18, $24, $18, $08, $08, $18, $08, $18 ; ( key
1788: .byte $00, $FF, $01, $01, $01, $01, $FE, $00 ; ) doorway east
1789: .byte $00, $FF, $80, $80, $80, $80, $7F, $00 ; * doorway west
1790: .byte $00, $FF, $00, $00, $00, $00, $00, $00 ; + maze wall h-top
1791: .byte $00, $00, $00, $00, $00, $00, $FF, $00 ; , maze wall h-bottom
1792: .byte $40, $40, $40, $40, $40, $20, $1F, $00 ; - maze wall s-w corner
1793: .byte $02, $02, $02, $02, $02, $04, $F8, $00 ; . maze wall s-e corner
1794: .byte $00, $1F, $20, $40, $40, $40, $40, $40 ; / maze wall n-w corner
1795: .byte $00, $F8, $04, $02, $02, $02, $02, $02 ; 0 maze wall n-e corner
1796: .byte $00, $18, $24, $42, $42, $42, $42, $42 ; 1 maze wall north
1797: .byte $42, $42, $42, $42, $42, $24, $18, $00 ; 2 maze wall south
1798: .byte $00, $1F, $20, $40, $40, $20, $1F, $00 ; 3 maze wall west
1799: .byte $00, $F8, $04, $02, $02, $04, $F8, $00 ; 4 maze wall east
1800: .byte $42, $41, $40, $40, $40, $20, $1F, $00 ; 5 maze wall s-w elbow
1801: .byte $42, $82, $02, $02, $02, $04, $F8, $00 ; 6 maze wall s-e elbow
1802: .byte $00, $1F, $20, $40, $40, $40, $41, $42 ; 7 maze wall n-w elbow
1803: .byte $00, $F8, $04, $02, $02, $02, $82, $42 ; 8 maze wall n-e elbow
1804: .byte $42, $42, $42, $42, $42, $42, $42, $42 ; 9 maze wall vertical
1805: .byte $00, $FF, $00, $00, $00, $00, $FF, $00 ; : maze wall horizontal
1806: .byte $42, $41, $40, $40, $40, $40, $41, $42 ; ; maze wall west tee
1807: .byte $42, $82, $02, $02, $02, $02, $82, $42 ; < maze wall east tee
1808: .byte $00, $FF, $00, $00, $00, $00, $81, $42 ; = maze wall north tee
1809: .byte $42, $81, $00, $00, $00, $00, $FF, $00 ; > maze wall south tee
1810: .byte $3C, $42, $99, $A1, $A1, $99, $42, $3C ; ? (C)copyright symbol