1;;==========================================================================;;
2;; Joe Zbiciak's 4-TRIS, A "Falling Tetrominoes" Game for Intellivision.    ;;
3;; Copyright 2000, Joe Zbiciak, intvnut AT gmail.com.                       ;;
4;; http://spatula-city.org/~im14u2c/intv/                                   ;;
5;;==========================================================================;;
6
7;* ======================================================================== *;
8;*  This program is free software; you can redistribute it and/or modify    *;
9;*  it under the terms of the GNU General Public License as published by    *;
10;*  the Free Software Foundation; either version 2 of the License, or       *;
11;*  (at your option) any later version.                                     *;
12;*                                                                          *;
13;*  This program is distributed in the hope that it will be useful,         *;
14;*  but WITHOUT ANY WARRANTY; without even the implied warranty of          *;
15;*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *;
16;*  General Public License for more details.                                *;
17;*                                                                          *;
18;*  You should have received a copy of the GNU General Public License       *;
19;*  along with this program; if not, write to the Free Software             *;
20;*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *;
21;* ======================================================================== *;
22;*                   Copyright (c) 2000, Joseph Zbiciak                     *;
23;* ======================================================================== *;
24
25        CFGVAR  "name" = "4-Tris"
26        CFGVAR  "author" = "Joe Zbiciak"
27        CFGVAR  "year" = 2000
28        CFGVAR  "music_by" = "Pyotr Ilyich Tchaikovsky"
29        CFGVAR  "music_by" = "Bobby McFarrin"
30        CFGVAR  "license" = "GPLv2+"
31        CFGVAR  "publisher" = "SDK-1600"
32
33        ROMW    16              ; Just for the heck of it.
34        ORG     $5000           ; Standard Mattel cartridge memory map
35
36;------------------------------------------------------------------------------
37; Magic Constants
38;------------------------------------------------------------------------------
39EDGEL   EQU     15              ; Left edge column
40EDGER   EQU     24              ; Right edge column
41EDGEB   EQU     21              ; Bottom edge row
42MOVEINC EQU     5               ; Per-downward-move score increment
43DANGER  EQU     6               ; Trigger point at which music goes 2x
44COPYR   EQU     $13A            ; Character # for Copyright circle-C
45
46NXTPCX  EQU     33              ; X coord where next piece is shown
47NXTPCY  EQU     11              ; Y coord where next piece is shown
48
49LVLROW  EQU     4               ; Row 'Level' banner is displayed on
50LVLCOL  EQU     2               ; Column 'Level' banner is displayed on
51LVLLOC  EQU     $200 + 20*(LVLROW - 1) + LVLCOL
52
53LINROW  EQU     7               ; Row 'Lines' banner is displayed on
54LINCOL  EQU     2               ; Column 'Lines' banner is displayed on
55LINLOC  EQU     $200 + 20*(LINROW - 1) + LINCOL
56
57NXTROW  EQU     (NXTPCY - 3)/2  ; Row 'Next' banner is displayed on
58NXTCOL  EQU     (NXTPCX - 3)/2  ; Column 'Next' banner is displayed on
59NXTLOC  EQU     $200 + 20*(NXTROW - 1) + NXTCOL
60
61;------------------------------------------------------------------------------
62; Magic memory locations
63;------------------------------------------------------------------------------
64VBLANK  EQU     $20             ; Vertical-blank Handshake
65COLSTK  EQU     $21             ; Color-stack/FGBG switch
66CS0     EQU     $28             ; Color Stack 0
67CS1     EQU     $29             ; Color Stack 1
68CS2     EQU     $2A             ; Color Stack 2
69CS3     EQU     $2B             ; Color Stack 3
70CB      EQU     $2C             ; Color for border
71ISRVEC  EQU     $100            ; ISR jump vector
72
73;               $130 .. $136    ; Save area used by DRAWPIECE/TESTPIECE
74
75TMPPC   STRUCT  $137            ; Temporary storage
76@@C     EQU     $ + 0           ; (R0) Color of current piece  UNUSED
77@@X     EQU     $ + 1           ; (R1) Pivot X coordinate for current piece
78@@Y     EQU     $ + 2           ; (R2) Pivot Y coordinate for current piece
79@@N     EQU     $ + 3           ; (R3) Piece number for current piece.
80        ENDS
81
82CURPC   STRUCT  $13B            ; Current active piece
83@@C     EQU     $ + 0           ; (R0) Color of current piece
84@@X     EQU     $ + 1           ; (R1) Pivot X coordinate for current piece
85@@Y     EQU     $ + 2           ; (R2) Pivot Y coordinate for current piece
86@@N     EQU     $ + 3           ; (R3) Piece number for current piece.
87        ENDS
88
89PXQ_L0  EQU     $140            ; Putpixel Queue length -- not committed
90PXQ_L1  EQU     $141            ; Putpixel Queue length -- committed
91PXQ_XY  EQU     $142 ;..$161    ; Putpixel Queue XY data, interleaved.
92
93VOLSV   EQU     $170 ;..$172    ; Volume save-area during pause
94PAUSED  EQU     $173            ; Flag saying we paused.
95
96OVRFLO  EQU     $1A0            ; Number of overflows observed
97WASDOWN EQU     $1A1            ; Flag: This movement was 'down'
98WASHAND EQU     $1A2            ; Flag: This movement was 'hand'
99SLEVEL  EQU     $1A8            ; Current game level number
100LEVEL   EQU     $1A9            ; Current game level number
101SFXPRIO EQU     $1AA            ; Priority of currently playing sound
102SCRPOS  EQU     $1AB            ; Position onscreen to display the score
103SCRDIG  EQU     $1AC            ; 10 - Number of digits to display in score
104SHOWNXT EQU     $1AD            ; Show next piece
105NXTPC   EQU     $1AE            ; Next Piece
106REPEAT  EQU     $1AF            ; Flag:  Set to keypress we're repeating
107LHAND   EQU     $1B0            ; Last hand-controller input
108LHKPD   EQU     $1B1            ; Last input was pad if non-zero
109MUTE    EQU     $1B2            ; Last input was pad if non-zero
110CSAVE   EQU     $1B3            ; Global 'c' save area
111TSKDQ   EQU     $1C0 ;..$1DF    ; $1C0..$1DF:  Task data queue
112XSAVE   EQU     $1E0            ; Global 'x' save area
113YSAVE   EQU     $1E1            ; Global 'y' save area
114               ;$1E2 .. $1E6, reserved
115DIDNXT  EQU     $1EB            ; Next Piece was displayed some time this piece
116HEIGHT  EQU     $1EC            ; Height of blocks in well.
117TSKACT  EQU     $1ED            ; Number of highest numbered task + 1
118TSKQHD  EQU     $1EE            ; Head pointer for task queue (0..15)
119TSKQTL  EQU     $1EF            ; Tail pointer for task queue (0..15)
120PSG0    EQU     $1F0 ;..$1FD    ; PSG base address
121CTRL0   EQU     $1FE            ; Right hand controller
122CTRL1   EQU     $1FF            ; Left hand controller
123
124MAXTSK  EQU     4               ; Right now allow 4 active tasks
125TSKQ    EQU     $320 ;..$32F    ; $320..$32F:  Task queue
126TSKTBL  EQU     $330 ;..$33F    ; $330..$33F:  Task table  (four tasks)
127SNDSTRM EQU     $340 ;..$347    ; $340..$347:  Sound stream table
128PREVBL  EQU     $348            ; Pre-VBLANK routine address
129HANDFN  EQU     $34A            ; Keypad dispatch function table.
130SCRCOL  EQU     $34B            ; Color that score is displayed in
131WTIMER  EQU     $34C            ; Countdown timer for WAIT
132REGSV   EQU     $34D ;..$351    ; $34D..$351:  Register save area for SLEEP
133PSCORL  EQU     $352            ; Player's score  (lo 16 bits)
134PSCORH  EQU     $353            ; Player's score  (hi 16 bits)
135DSCORL  EQU     $354            ; Displayed score (lo 16 bits)
136DSCORH  EQU     $355            ; Displayed score (hi 16 bits)
137LINES   EQU     $356            ; Number of lines cleared so far
138MSCORE  EQU     $358            ; Total of per-move scores
139TMP     EQU     $35D
140RANDLO  EQU     $35E            ; Low word of random number generator
141RANDHI  EQU     $35F            ; High word of random number generator
142
143SPEED   EQU     TSKTBL + 3      ; Piece movement speed (IN TASK TABLE!)
144
145GROM    EQU     $3000
146GRAM    EQU     $3800
147
148
149;------------------------------------------------------------------------------
150ROMHDR: BIDECLE ZERO            ; MOB picture base   (points to NULL list)
151        BIDECLE ZERO            ; Process table      (points to NULL list)
152        BIDECLE START           ; Program start address
153        BIDECLE ZERO            ; Bkgnd picture base (points to NULL list)
154        BIDECLE ONES            ; GRAM pictures      (points to NULL list)
155        BIDECLE T1TLE           ; Cartridge title/date
156        DECLE   $03C0           ; No ECS title, run code after title,
157                                ; ... no clicks
158ZERO:   DECLE   $0000           ; Screen border control
159        DECLE   $0000           ; 0 = color stack, 1 = f/b mode
160ONES:   DECLE   1, 1, 1, 1, 1   ; Initial color stack / border color.
161;------------------------------------------------------------------------------
162
163;;==========================================================================;;
164;;  SNDTBL                                                                  ;;
165;;  Contains pointers and parameters for all sound effects and music.       ;;
166;;==========================================================================;;
167SNDTBL  PROC
168@@bip   DECLE   BIP,    $2744   ; Played when player moves a piece
169@@byump DECLE   BYUMP,  $2744   ; Played when the player places a piece
170@@bomb  DECLE   BOMB,   $27CC   ; Played when clearing a line
171@@bomb4 DECLE   BOMB4,  $3FFF   ; Played when clearing FOUR lines
172@@boom2 DECLE   BOOM2,  $3FFF   ; Played when well overflows
173@@ding3 DECLE   DING3,  $35EE   ; Played to signal changing level
174
175@@title DECLE   NUTMARCH, $3FFF ; Title screen music
176@@game  DECLE   CHINDNCE, $3FFF ; In-game music
177@@over  DECLE   BEHAPPY,  $3FFF ; Game-over music
178
179@@silence
180        DECLE   SILENCE, $3FFF  ; Silence!
181        ENDP
182
183FXBIP   EQU     SNDTBL.bip   - SNDTBL
184FXBOMB  EQU     SNDTBL.bomb  - SNDTBL
185FXBOMB4 EQU     SNDTBL.bomb4 - SNDTBL
186FXBOOM2 EQU     SNDTBL.boom2 - SNDTBL
187FXDING3 EQU     SNDTBL.ding3 - SNDTBL
188FXBYUMP EQU     SNDTBL.byump - SNDTBL
189
190M_TITLE EQU     SNDTBL.title - SNDTBL
191M_GAME  EQU     SNDTBL.game  - SNDTBL
192M_OVER  EQU     SNDTBL.over  - SNDTBL
193
194FX_OFF  EQU     SNDTBL.silence - SNDTBL
195
196;;==========================================================================;;
197;;  TITLE / START                                                           ;;
198;;                                                                          ;;
199;;  This contains the title string and the startup code.  We pre-empt the   ;;
200;;  EXEC's initialization sequence by setting the "Special Copyright" bit   ;;
201;;  in location $500C.  This causes the code at 'START' to run before the   ;;
202;;  built-in title screen is completely displayed.                          ;;
203;;                                                                          ;;
204;;  The Startup code does very little.  Mainly, it sets the Interrupt       ;;
205;;  Service Routine vector to point to our _real_ initialization routine,   ;;
206;;  INIT.  This is done because we can only get to GRAM and STIC registers  ;;
207;;  during the vertical retrace, and vertical retrace is signaled by an     ;;
208;;  interrupt.  (Actually, we can have access to GRAM/STIC for longer       ;;
209;;  if we don't hit the STIC 'handshake' at location $20, but then the      ;;
210;;  display blanks.  During INIT, the display does blank briefly.)          ;;
211;;==========================================================================;;
212TITLE:  BYTE    100, "*-TRIS", 0 ; Title: 4-TRIS, Copyright 2000
213        ; Intercept/preempt EXEC initialization and just do our own.
214        ; We call no EXEC routines in this game.
215START:
216        MVI     $1FF,   R2
217        AND     $1FE,   R2
218
219        CLRR    R4              ; Prepare to zero all system RAM, PSG0,& STIC.
220        MVII    #$20,   R1      ; $00...$1F. (The STIC)
221        JSRD    R5,     FILLZERO
222
223        ADDI    #8,     R4      ; $28...$32. (The rest of the STIC)
224        MVII    #11,    R1
225        CALL    FILLZERO
226
227        MVII    #$F0,   R4      ; $F0...$35D. We spare the rand seed values
228        MVII    #$26D,  R1      ; in $35E..$35F to add some randomness.
229        CALL    FILLZERO
230
231        COMR    R2
232        MVO     R2,     LHAND   ; Set up initial hand-controller state
233
234        MVII    #INIT,   R0     ; Our initialization routine
235        MVII    #ISRVEC, R4     ; ISR vector
236        MVO@    R0,     R4      ; Write low half
237        SWAP    R0              ;
238        MVO@    R0,     R4      ; Write high half
239        MVI     RANDLO, R0      ; Get whatever garbage data is in memory in the
240        MVI     RANDHI, R1      ; random number generator fields
241
242        MOVR    PC,     R5      ; Loop starting at the next instruction.
243@@spin:
244        INCR    R0              ; Seed random number generator while
245        DECR    R1              ; we wait for our first interrupt.
246        MVO     R0,     RANDLO
247        MVO     R1,     RANDHI
248        EIS
249        ; This falls through to STUB which causes
250        ; our loop (saves a couple DECLEs.)
251
252;;==========================================================================;;
253;;  STUB                                                                    ;;
254;;  Null routine used in dispatchers where no behavior is defined/desired.  ;;
255;;==========================================================================;;
256STUB:   JR      R5              ; Stub routine
257
258
259;;==========================================================================;;
260;;  INIT                                                                    ;;
261;;  Initializes the ISR, etc.  Gets everything ready to run.                ;;
262;;  This is called via the ISR dispatcher, so it's safe to bang GRAM from   ;;
263;;  here, too.                                                              ;;
264;;                                                                          ;;
265;;   -- Zero out memory to get started                                      ;;
266;;   -- Set up variables that need to be set up here and there              ;;
267;;   -- Set up GRAM image                                                   ;;
268;;   -- Drop into the main game state-machine.                              ;;
269;;==========================================================================;;
270INIT:   PROC
271        DIS
272        MVII    #$2F0,  R6              ; Reset the stack pointer
273
274        MVI     COLSTK, R1              ; Force display to color-stack mode
275
276        ; Make sure random number generator is non-zero.
277        MOVR    PC,     R0              ; The PC is guaranteed to be non-zero.
278        XOR     RANDLO, R0
279        BEQ     @@randok                ; If the XOR result is zero, we're ok,
280        MVO     R0,     RANDLO          ; otherwise make RANDLO non-zero.
281@@randok:
282
283        ; Stub out all of the task hooks
284        MVII    #STUB,  R0              ; Prepare to stub out some hooks.
285        MVO     R0,     PREVBL          ; Initial pre-VBL task is NULL
286
287        MVII    #MAINISR, R0            ; Point ISR vector to our ISR
288        MVO     R0,     ISRVEC          ; store low half of ISR vector
289        SWAP    R0                      ;
290        MVO     R0,     ISRVEC+1        ; store high half of ISR vector
291
292        ; Do the face?
293        MVII    #$D9,   R0
294        CMP     $1FE,   R0
295        BEQ     DOFACE
296        CMP     $1FF,   R0
297        BEQ     DOFACE
298
299        ; Default the GRAM image to be same as GROM.
300        MVII    #GROM,  R5              ; Point R5 at GROM
301        MVII    #GRAM,  R4              ; Point R4 at GRAM
302        MVII    #$200,  R0
303@@gromcopy:
304        MVI@    R5,     R1
305        MVO@    R1,     R4
306        DECR    R0
307        BNEQ    @@gromcopy
308
309        ; Copy our GRAM font into GRAM overtop of default.
310        CALL    LOADFONT
311        DECLE   FONT
312
313        ; Ok, everything's ready to roll now.
314        EIS
315
316        ;;==================================================================;;
317        ;;  Game phases:                                                    ;;
318        ;;   -- Title screen                                                ;;
319        ;;   -- (optional) Sound Test                                       ;;
320        ;;   -- In Game                                                     ;;
321        ;;   -- Game over                                                   ;;
322        ;;                                                                  ;;
323        ;;  The INIT code cycles between these gae phases by first          ;;
324        ;;  calling the subroutine which sets up the tasks for a given      ;;
325        ;;  game phase, and then calling the main event loop which          ;;
326        ;;  runs the game.  This assumes that all tasks are stopped when    ;;
327        ;;  the MAINLOOP exits.  Calling SCHEDEXIT accomplishes a clean     ;;
328        ;;  exit from MAINLOOP by scheduling an exit while stopping all     ;;
329        ;;  other tasks.  (Neat, eh?)                                       ;;
330        ;;==================================================================;;
331@@loop:
332        CALL    TITLESCREEN     ; Set up 'Title Screen'
333        CALL    MAINLOOP        ; Do title screen.
334        MVI     SLEVEL, R0      ; If starting level == 0, do 'Sound Test'
335        TSTR    R0
336        BEQ     @@soundtest
337
338        CALL    INGAME          ; Set up 'In Game'
339        CALL    MAINLOOP        ; Do the Game.
340
341        CALL    GAMEOVER        ; Set up the 'Game Over' sequence.
342        CALL    MAINLOOP        ; Let it run.
343
344        B       @@loop          ; Start over at the title screen.
345
346@@soundtest:
347        CALL    SOUNDTEST       ; Sound-test requested.  Set that up.
348        CALL    MAINLOOP        ; Run the sound-test.
349        B       @@loop          ; Go back to title screen when done.
350        ENDP
351
352;;==========================================================================;;
353;;  LOADFONT -- Load a compressed FONT into GRAM.                           ;;
354;;                                                                          ;;
355;;  Note:  This should be called only when the STIC is already in CPU-      ;;
356;;  controlled mode, such as from an interrupt handler.  Loading a large    ;;
357;;  font may cause the screen to blank for one or more frames.              ;;
358;;                                                                          ;;
359;;  Font data is broken up into spans of characters that are copied         ;;
360;;  into GRAM.  Each span is defined as follows.                            ;;
361;;                                                                          ;;
362;;    Span Header:  2 Decles                                                ;;
363;;        DECLE   Skip Length (in bytes of GRAM memory)                     ;;
364;;        DECLE   Span Length (in bytes of GRAM memory)                     ;;
365;;    Span Data -- up to Span Length Decles.                                ;;
366;;                                                                          ;;
367;;  Span Data is run-length encoded using the upper two bits of the         ;;
368;;  decle to specify the run length.  Valid run lengths are 0..3,           ;;
369;;  with 0 meaning "just copy this byte to the GRAM", and 3 meaning         ;;
370;;  "copy this byte to GRAM and make three more copies in the locations     ;;
371;;  afterwards".  To see what I mean, look at the font data in              ;;
372;;  "font.asm".                                                             ;;
373;;                                                                          ;;
374;;  The run length encoding does not change the value used for              ;;
375;;  'span length'.  The span length is always given in terms of             ;;
376;;  # of GRAM locations, and not number of decles in the FONT data.         ;;
377;;                                                                          ;;
378;;  The font is terminated with a span of length 0.                         ;;
379;;                                                                          ;;
380;;  INPUTS:                                                                 ;;
381;;  R5 -- Points to word (1 decle in 16-bit ROM) containing ptr to font     ;;
382;;        info.  Code returns after this word.                              ;;
383;;                                                                          ;;
384;;  OUTPUTS:                                                                ;;
385;;  R0, R1, R4, R5 trashed.                                                 ;;
386;;  GRAM is updated according to the font specification.                    ;;
387;;                                                                          ;;
388;;==========================================================================;;
389LOADFONT PROC
390        MVI@    R5,     R0
391        PSHR    R5
392        MOVR    R0,     R5
393
394        MVII    #GRAM,  R4              ; Point R4 at GRAM
395
396@@gramloop:
397        MVI@    R5,     R0              ; Get skip & span len. (in GRAM bytes)
398        TSTR    R0                      ; Quit if skip/span == 0.
399        BEQ     @@gramdone
400
401        MOVR    R0,     R1
402        ANDI    #$7F8,  R0              ; Extract span length.
403        XORR    R0,     R1              ; Clear away span bits from word
404        SWAP    R1                      ; Extrack skip value.
405        ADDR    R1,     R4              ; Skip our output pointer.
406        SLR     R0,     1               ; Divide count by 2.
407
408@@charloop:
409        MVI@    R5,     R1              ; Get two bytes
410        MVO@    R1,     R4              ; Put the first byte
411        SWAP    R1                      ; Put the other byte into position
412        MVO@    R1,     R4              ; Put the second byte
413        DECR    R0                      ; Sheesh, do I have to spell this out?
414        BNEQ    @@charloop              ; inner loop
415        B       @@gramloop              ; outer loop
416
417@@gramdone:
418        PULR    PC
419        ENDP
420
421;;==========================================================================;;
422;;  MAINISR                                                                 ;;
423;;  This is the main interrupt service routine.  It has to perform the      ;;
424;;  following tasks:                                                        ;;
425;;                                                                          ;;
426;;   -- Run any miscellaneous "pre-VBLANK" task requested by main program.  ;;
427;;   -- Hit the vertical blank handshake to keep screen enabled.            ;;
428;;   -- Update any active sound streams.                                    ;;
429;;   -- Drain and pixels queued up in the Pixel Queue.                      ;;
430;;   -- Count down task timers and schedule tasks when timers expire.       ;;
431;;   -- Count down the 'busy-wait' timer if it is set.                      ;;
432;;   -- Detect a "Pause" request and pause the game if needed.              ;;
433;;                                                                          ;;
434;;  This particular program currently does not sequence GRAM at all after   ;;
435;;  the initial bootup.  Therefore, we can hit the VBLANK handshake almost  ;;
436;;  immediately after entering the ISR.  We do support a pre-VBLANK         ;;
437;;  routine though for other purposes, such as setting color-stack regs.    ;;
438;;                                                                          ;;
439;;  Our code does not rely on any EXEC routines at all, except the ISR      ;;
440;;  dispatch routine which saves and restores all the registers.  (On a     ;;
441;;  real Intellivision, we have no choice.  On an emulator, that routine    ;;
442;;  can be replaced with a separate non-EXEC routine which performs a       ;;
443;;  similar dispatch, if we want to distribute this program without an      ;;
444;;  EXEC ROM image but with an emulator.)                                   ;;
445;;                                                                          ;;
446;;==========================================================================;;
447MAINISR PROC
448
449        PSHR    R5                      ; Save return address
450
451        ;; Call out to user-defined pre-VBLANK routine.
452        ;; This routine should point to the stub if no routine is to
453        ;; be called.  (simplifies the code w/out really slowing it down.)
454        MVII    #@@rl0, R5      ; Point to @@rl0
455        MVI     PREVBL, PC      ; Call subroutine
456@@rl0:
457        ;; Hit the VBLANK handshake now
458        MVO     R0,     $20     ; Allow STIC to display this frame.
459
460        ;; Update sound streams.  All sound streams update at 60Hz.
461        ;; Stream #0 can update at "60Hz * 2" if double-speed mode is on.
462        CALL    SNDPRIO         ; Make sure music & sfx play nice
463
464        ; Update music
465        CALL    DOSNDSTREAM     ; Update the sound stream
466        DECLE   SNDSTRM         ; Point to the music stream
467
468        ; Update sfx
469        CALL    DOSNDSTREAM     ; Update the sound stream
470        DECLE   SNDSTRM+4       ; Point to the sfx stream
471
472        ; If well is too tall, play music fast.
473        MVII    #DANGER,  R0
474        CMP     HEIGHT,   R0
475        BLT     @@slowmusic
476        MVII    #SNDSTRM, R4
477        CALL    DOSNDSTREAM     ; Update the sound stream
478        DECLE   SNDSTRM         ; Point to the music stream
479@@slowmusic:
480
481        MVII    #RANDHI,R1
482        MVI     SNDSTRM+1, R2
483        XOR@    R1,     R2
484        MVO@    R2,     R1      ; Throw some bits into the random bucket
485
486        ;; Drain any pixels that were queued and committed.
487        CALL    DRAINPXQ
488
489        ;; Count down task timers and schedule tasks.  We have up to MAXTSK
490        ;; active tasks at one time.  We decrement all counters by two.
491        ;;   -- If the count is initially <= 0, the task is inactive and
492        ;;      is skipped.
493        ;;   -- If the count goes to zero, the task is triggered and its
494        ;;      count is reinitialized.
495        ;;   -- If the count goes negative, the task is triggered and its
496        ;;      count is not reinitialized.
497        ;; This allows us to have one-shot and repeating tasks pretty
498        ;; easily.  Repeating tasks clear bit 0 of their period count,
499        ;; and one-shot tasks set bit 0.
500        ;;
501        ;; When tasks are triggered, their procedure address is written
502        ;; to the task queue which is drained by the non-ISR main task.
503        ;; The task queue is a small circular buffer in 16-bit memory with
504        ;; head and tail pointers in 8-bit memory.  The circular buffer is
505        ;; 16 entries large.  I hope this is enough.  Overflow can cause
506        ;; strange effects.
507        ;;
508        ;; Task control table entry layout:
509        ;;
510        ;;   Word 0:  Function Ptr
511        ;;   Word 1:  Instance data
512        ;;   Word 2:  Current down-count
513        ;;   Word 3:  Reinit count
514
515        MVI     TSKACT, R0      ; Iterate over active tasks
516        TSTR    R0
517        BEQ     @@notasks
518        MVII    #TSKTBL+2, R3   ; Point to the task control table
519
520@@taskloop:
521        MVI@    R3,     R1      ; Get tasks's current tick count
522        TSTR    R1              ; Is it <= 0?
523        BLE     @@nexttask      ; ... yes? Skip it.
524
525        MOVR    R1,     R2
526        ADD     RANDLO, R2
527        MVO     R2,     RANDLO  ; Throw some bits into the random bucket
528
529        SUBI    #2,     R1      ; Count it down.
530        BNEQ    @@noreinit      ; If it went to zero, reinit count
531
532        INCR    R3
533        MVI@    R3,     R1      ; Get new period for task
534        DECR    R3
535        SUBR    R2,     R2      ; Z = 1, S = 0 (causes the BGT to not be taken)
536@@noreinit:
537        MVO@    R1,     R3      ; Store new period.
538        BGT     @@nexttask      ; If this count didn't expire, ...
539                                ;  ... go to the next task
540@@q:
541        ; Schedule this task by adding it to the task queue
542        MOVR    R3,     R4
543        SUBI    #2,     R4
544        MVI@    R4,     R2      ; Get task function pointer
545        MVI     TSKQHD, R1      ; Get task queue head
546        INCR    R1              ; Move to next slot
547        ANDI    #$F,    R1      ; Stay within 16-entry circ buffer
548        CMP     TSKQTL, R1
549        BEQ     @@overflow      ; Drop this task if queue overflows. (!)
550
551        MOVR    R1,     R5
552        ADDR    R1,     R5      ; R5 is for task data queue
553        ADDI    #TSKDQ, R5      ; Point to task data queue
554
555        MVO     R1,     TSKQHD  ; Store updated task queue head
556
557        ADDI    #TSKQ,  R1      ; Point to actual task queue
558        MVO@    R2,     R1      ; Store the function pointer
559        MVI@    R4,     R2      ; Get task instance data
560        MVO@    R2,     R5      ; Store low half of instance data
561        SWAP    R2,     1
562        MVO@    R2,     R5      ; Store high half of instance data
563
564@@nexttask:
565        ADDI    #4,     R3      ; Point to next task struct
566        DECR    R0
567        BNEQ    @@taskloop      ; Loop until done with all tasks
568        B       @@notasks
569
570@@overflow:
571        MVI     OVRFLO, R0
572        INCR    R0
573        MVO     R0,     OVRFLO
574
575@@notasks:
576
577@@wtimer:
578        ;; Count down the wait-timer, if there is one
579        MVI     WTIMER, R5
580        DECR    R5
581        BMI     @@expired
582        MVO     R5,     WTIMER
583@@expired:
584
585        ;; Check to see if the user has requested a pause.
586        MVII    #$5A,   R0      ; Keypad 1 + Keypad 9
587        CMP     $1FE,   R0      ; Pause on first controller?
588        BEQ     @@pausing
589        CMP     $1FF,   R0      ; Pause on second controller?
590        BEQ     @@pausing
591
592        PULR    PC              ; Return
593
594@@pausing:
595        DIS                     ; Disable interrupts while paused.
596        ; Grab PSG volume settings aside and force volume to zero.
597        MVII    #VOLSV, R2
598        MVII    #$1FB,  R3
599        MOVR    R2,     R5
600        MOVR    R3,     R4
601
602        MVO     R4,     PAUSED  ; Set 'we paused' flag
603        MVI@    R4,     R0      ; Channel a
604        MVO@    R0,     R5
605        MVI@    R4,     R0      ; Channel b
606        MVO@    R0,     R5
607        MVI@    R4,     R0      ; Channel c
608        MVO@    R0,     R5
609
610        CLRR    R1
611        MOVR    R3,     R4
612        MVO@    R1,     R4
613        MVO@    R1,     R4
614        MVO@    R1,     R4
615
616        CLRC
617        CALL    @@waithand      ; Wait for both controllers to be released.
618        SETC
619        CALL    @@waithand      ; Wait for either controller to be pressed
620        CLRC
621        CALL    @@waithand      ; Wait for both controllers to be released.
622
623        MOVR    R3,     R5
624        MOVR    R2,     R4
625        MVI@    R4,     R0      ; Channel a
626        MVO@    R0,     R5
627        MVI@    R4,     R0      ; Channel b
628        MVO@    R0,     R5
629        MVI@    R4,     R0      ; Channel c
630        MVO@    R0,     R5
631        EIS
632        PULR    PC
633
634@@waitpress:
635        BNEQ    @@waitdone
636@@waithand:
637        MVII    #100,   R1      ; Debounce factor
638@@waitloop:
639        MVII    #$1FE,  R4
640        SDBD
641        MVI@    R4,     R0
642        COMR    R0
643        BC      @@waitpress
644        BNEQ    @@waithand
645@@waitdone:
646        DECR    R1
647        BNEQ    @@waitloop
648        JR      R5
649
650        ENDP
651
652;;==========================================================================;;
653;;  MAINLOOP                                                                ;;
654;;  The main loop runs asynchronously from the MAINISR, running tasks as    ;;
655;;  they get scheduled.  There is also a background task that runs when     ;;
656;;  nothing else is in the queue.                                           ;;
657;;                                                                          ;;
658;;   -- Grab task from task queue, if any                                   ;;
659;;       -- run that task                                                   ;;
660;;       -- look for next task                                              ;;
661;;   -- run background task once if no tasks available                      ;;
662;;                                                                          ;;
663;;  The background task scans hand controllers.  I do this because          ;;
664;;  hand-controller scanning could be fairly expensive.  Also, scanning     ;;
665;;  hand controllers may cause new tasks to get scheduled (eg. the triggers ;;
666;;  for various hand controller events) and I don't want to overrun the     ;;
667;;  task queue.                                                             ;;
668;;                                                                          ;;
669;;  The background task also updates the random number generator state.  I  ;;
670;;  do this to make the random numbers a bit more random since the game     ;;
671;;  loading varies according to what's going on.                            ;;
672;;==========================================================================;;
673MAINLOOP        PROC
674
675        PSHR    R5              ; Save return address, since any
676                                ; task is allowed to exit the main
677                                ; game loop, say, when switching
678                                ; between game phases.
679                                ;
680                                ; There are three game phases:
681                                ;  -- Title Screen
682                                ;  -- In Game
683                                ;  -- Game Over.
684
685        MVII    #EDGEB, R1      ; Set the well height so that music
686        MVO     R1,     HEIGHT  ; plays correctly
687        MVO     R1,     MUTE    ; Also, un-mute music.
688
689@@loop:
690        MOVR    PC,     R5      ; Set our return address to @@loop
691        DECR    R5              ; This is 2 decles vs. 4 for SDBD/MVII
692
693        ; Run next scheduled task from queue
694        DIS                     ; Shut off interrupts
695        MVI     TSKQTL, R1      ; Get the tail of the queue
696        CMP     TSKQHD, R1      ; Are there tasks in the queue?
697        BEQ     @@bktsk         ; No:  Do background task
698
699        INCR    R1              ; Pop task from queue by
700        ANDI    #$F,    R1      ; ... moving the queue tail
701        MVO     R1,     TSKQTL  ; Store new task-queue tail
702        MOVR    R1,     R4
703        ADDR    R4,     R4
704        ADDI    #TSKDQ, R4
705        SDBD
706        MVI@    R4,     R2      ; Load instance data for task
707        ADDI    #TSKQ,  R1      ; Point to task queue
708        MVI@    R1,     R0      ; Get function pointer from queue
709
710        ; Dispatch to user's task.  Make sure interrupts are enabled again
711        EIS                     ; Done with critical section
712        JR      R0              ; Jump to subroutine
713        ; Note that this implicitly loops back to @@oloop here
714
715@@bktsk:
716        EIS                     ; Done with critical section
717        CALL    NEXTRAND        ; Update random number state
718        CALL    SCANHAND        ; Scan the hand controllers.
719
720EC_LOC  EQU     $CF00
721EC_MAG  EQU     $69
722EC_POLL EQU     $CF01
723
724        MVI     EC_LOC, R0
725        CMPI    #EC_MAG,R0
726        BNEQ    @@loop
727        CALL    EC_POLL
728
729        B       @@loop          ; Run around the inner loop
730
731        ENDP
732
733;;==========================================================================;;
734;;  QTASK                                                                   ;;
735;;  Add a task to the task queue.                                           ;;
736;;  NOTE: CALL THIS WITH INTERRUPTS OFF!!!!  (eg. use JSRD).  Interrupts    ;;
737;;  will be enabled before return.                                          ;;
738;;                                                                          ;;
739;;  INPUTS:                                                                 ;;
740;;  R0 -- Task function pointer                                             ;;
741;;  R1 -- Task instance data                                                ;;
742;;  R5 -- Return address.                                                   ;;
743;;                                                                          ;;
744;;  OUTPUTS:                                                                ;;
745;;  R0 -- intact.                                                           ;;
746;;  R1 -- swapped                                                           ;;
747;;  R2, R4 -- trashed.                                                      ;;
748;;==========================================================================;;
749QTASK   PROC
750        MVI     TSKQHD, R2      ; Get head of task queue
751        INCR    R2              ; Move to next queue slot
752        ANDI    #$F,    R2      ; Stay in circular buffer
753        CMP     TSKQTL, R2      ; Are we overflowing the queue?
754        BEQ     @@overflow      ; Yes ... don't overflow it!
755        MVO     R2,     TSKQHD  ; Store new task queue head
756
757        MOVR    R2,     R4      ; Generate instance data pointer
758        ADDR    R2,     R4      ; (remember mult by two for data ptr)
759        ADDI    #TSKQ,  R2      ; Point into task queue
760        MVO@    R0,     R2      ; Store function pointer to task q
761; moved addi for STIC interruptibility fix
762        ADDI    #TSKDQ, R4      ; Point into task data queue
763        MVO@    R1,     R4      ; Store instance data to data q
764        SWAP    R1              ; ....  cont'd
765        MVO@    R1,     R4      ; ....  cont'd
766        EIS                     ; Re-enable ints after crit section
767        JR      R5              ; Return.
768
769@@overflow:
770        MVI     OVRFLO, R2
771        INCR    R2
772        MVO     R2,     OVRFLO
773
774        EIS                     ; Re-enable ints after crit section
775        JR      R5              ; Return.
776
777        ENDP
778
779;;==========================================================================;;
780;;  SCHEDEXIT                                                               ;;
781;;  Flushes the task queue and schedules the exit task.                     ;;
782;;                                                                          ;;
783;;  INPUTS:                                                                 ;;
784;;  R5 -- Return address.                                                   ;;
785;;==========================================================================;;
786SCHEDEXIT:      PROC
787        PSHR    R5              ; Push return address.
788        JSRD    R5,STOPALLTASKS ; Flush the pending task queue
789        MVII    #@@exit, R0     ; Schedule the exit task ...
790        PULR    R5
791        B       QTASK           ; ... and chain the return.
792@@exit: PULR    PC
793        ENDP
794
795;;==========================================================================;;
796;;  STOPALLTASKS                                                            ;;
797;;  Stops all tasks by setting the task count to zero and by                ;;
798;;  flushing the task queue (sets head/tail to 0).  Returns old task count. ;;
799;;  NOTE: CALL THIS WITH INTERRUPTS OFF!!!!  (eg. use JSRD)  Interrupts     ;;
800;;  are NOT re-enabled by this function.                                    ;;
801;;                                                                          ;;
802;;  INPUTS:                                                                 ;;
803;;  R5 -- Return address                                                    ;;
804;;                                                                          ;;
805;;  OUTPUTS:                                                                ;;
806;;  R0 -- Previous TSKACT                                                   ;;
807;;  R1 -- Cleared                                                           ;;
808;;  All tasks stopped.                                                      ;;
809;;==========================================================================;;
810STOPALLTASKS    PROC
811        MVI     TSKACT, R0
812        CLRR    R1
813        MVO     R1,     TSKACT
814        MVO     R1,     TSKQHD
815        MVO     R1,     TSKQTL
816        DECR    R1
817        MVO     R1,     TSKTBL+2        ; period == -1 for task 0
818        MVO     R1,     TSKTBL+6        ; period == -1 for task 1
819        MVO     R1,     TSKTBL+10       ; period == -1 for task 2
820        NOP                             ; (interruptible, for STIC)
821        MVO     R1,     TSKTBL+14       ; period == -1 for task 3
822        JR      R5
823
824        ENDP
825
826;;==========================================================================;;
827;;  STARTTASK                                                               ;;
828;;  Puts a task into a task slot for general timer-based scheduling         ;;
829;;                                                                          ;;
830;;  INPUTS:                                                                 ;;
831;;  R5 -- Invocation record:                                                ;;
832;;    DECLE -- Task number                                                  ;;
833;;    DECLE -- Task function pointer                                        ;;
834;;    DECLE -- Task initial period * 2. If bit 0==1, this is one-shot task. ;;
835;;    DECLE -- Task period re-init. Useful if first delay!=recurring delay. ;;
836;;  R2 -- Task instance data (optional)                                     ;;
837;;                                                                          ;;
838;;  OUTPUTS:                                                                ;;
839;;  Task record is set up.  (User still needs to update TSKACT to make      ;;
840;;  the task records active, if necessary.)                                 ;;
841;;                                                                          ;;
842;;  Registers R0, R4 are trashed.                                           ;;
843;;==========================================================================;;
844STARTTASK       PROC
845        MVI@    R5,     R0
846        SLL     R0,     2       ; R0 = R0 * 4 (four words/entry)
847        MVII    #TSKTBL,R4      ; R4 = &TSKTBL[0]
848        ADDR    R0,     R4      ; R4 = &TSKTBL[n]
849        DIS                     ; Entering critical section.
850        MVI@    R5,     R0      ; Get Function pointer
851        MVO@    R0,     R4      ; ... and write it
852        MVO@    R2,     R4      ; Write task instance data
853        MVI@    R5,     R0      ; Get task period
854        MVO@    R0,     R4      ; ... and write it
855        MVI@    R5,     R0      ; Get task period reinit
856        MVO@    R0,     R4      ; ... and write it
857        EIS
858        JR      R5
859        ENDP
860
861;;==========================================================================;;
862;;  STOPTASK                                                                ;;
863;;  Stops a given task by setting its period to -1.  Task can be            ;;
864;;  retriggered/restarted by calling RETRIGGERTASK.                         ;;
865;;                                                                          ;;
866;;  INPUTS:                                                                 ;;
867;;  R3 -- Task number.                                                      ;;
868;;                                                                          ;;
869;;  OUTPUTS:                                                                ;;
870;;  Task is disabled.                                                       ;;
871;;  R0 == -1, R3 points to task delay count.                                ;;
872;;==========================================================================;;
873STOPTASK        PROC
874        SLL     R3,     2       ; R3 = R3 * 4 (four words/entry)
875        ADDI    #TSKTBL+2, R3   ; R3 = &TSKTBL[n].delay
876        CLRR    R0
877        DECR    R0              ; R0 == -1.
878        MVO@    R0,     R3      ; TSKTBL[n].delay = -1
879        JR      R5
880        ENDP
881
882;;==========================================================================;;
883;;  RETRIGGERTASK                                                           ;;
884;;  Restarts a task by copying its delay re-init field to its delay field.  ;;
885;;  Can be used on one-shot tasks or tasks which have been stopped with     ;;
886;;  'StopTask'.                                                             ;;
887;;                                                                          ;;
888;;  INPUTS:                                                                 ;;
889;;  R3 -- Task number.                                                      ;;
890;;                                                                          ;;
891;;  OUTPUTS:                                                                ;;
892;;  R0 -- Task delay                                                        ;;
893;;  R3 -- points to task delay count                                        ;;
894;;==========================================================================;;
895
896RETRIGGERTASK:  PROC
897        SLL     R3,     2       ; R3 = R3 * 4 (four words/entry)
898        ADDI    #TSKTBL+3, R3   ; R3 = &TSKTBL[n].reload
899        DIS
900        MVI@    R3,     R0      ; Get reload value
901@@chain:
902        DECR    R3              ; Offset 2 is delay count
903        MVO@    R0,     R3
904        EIS
905        JR      R5
906
907        ENDP
908
909;;==========================================================================;;
910;;  WAIT                                                                    ;;
911;;  Busy-waits for the number of ticks specified in R0.                     ;;
912;;                                                                          ;;
913;;  INPUTS:                                                                 ;;
914;;  R0 -- Number of ticks to wait                                           ;;
915;;  R5 -- Return address                                                    ;;
916;;                                                                          ;;
917;;  OUTPUTS:                                                                ;;
918;;  R0 -- cleared                                                           ;;
919;;==========================================================================;;
920WAIT    PROC
921        MVI@    R5,     R0
922        MVO     R0,     WTIMER
923        CLRR    R0
924@@loop:
925        CMP     WTIMER, R0
926        BNEQ    @@loop
927        JR      R5
928        ENDP
929
930
931;;==========================================================================;;
932;;  SLEEP                                                                   ;;
933;;  Causes a one-shot process to sleep by scheduling a second one-shot      ;;
934;;  process to reawake it.  This is VERY DIFFICULT TO USE, and can be       ;;
935;;  used by ONE PROCESS ONLY at a time.                                     ;;
936;;                                                                          ;;
937;;  SPAWN                                                                   ;;
938;;  Same as sleep, only the sleeping task isn't a one-shot.  It keeps       ;;
939;;  retriggering automatically at the designated interval, or can be        ;;
940;;  retriggered manually if the period is odd.                              ;;
941;;                                                                          ;;
942;;  INPUTS:                                                                 ;;
943;;  R5 -- Invocation record, in following format:                           ;;
944;;        Period/sleep time  1 Decle  (Must be ODD for SLEEP)               ;;
945;;        Process # to use   1 Decle                                        ;;
946;;  Top of Stack -- Return address from _this_ process back to scheduler.   ;;
947;;  Task will wake at address after invocation record.                      ;;
948;;                                                                          ;;
949;;  OUTPUTS:                                                                ;;
950;;  R0, R1, R2, R3, R4 restored                                             ;;
951;;  R5 points to your return address                                        ;;
952;;  Top of stack has return address for scheduler                           ;;
953;;==========================================================================;;
954SLEEP:  PROC
955SPAWN:
956        MVO     R4,     REGSV+4 ; Save R4
957        MVII    #REGSV, R4
958        MVO@    R0,     R4      ; Save R0
959        MVO@    R1,     R4      ; Save R1
960        MVO@    R2,     R4      ; Save R2
961        MVO@    R3,     R4      ; Save R3
962
963        MVI@    R5,     R2      ; Get sleep time
964        MVI@    R5,     R3      ; Get PID
965
966        SLL     R3,     2       ; R3 = R3 * 4 (four words/entry)
967        MVII    #TSKTBL,R4      ; R4 = &TSKTBL[0]
968        ADDR    R3,     R4      ; R4 = &TSKTBL[n]
969        MVII    #@@wake,R0
970        MVO@    R0,     R4      ; Address to wake up at
971        MVO@    R5,     R4      ; Instance data (R2 restore)
972        MVO@    R2,     R4
973        MVO@    R2,     R4
974
975        PULR    PC
976
977@@wake:
978        PSHR    R5              ; Remember return address
979        MOVR    R2,     R5
980        MVII    #REGSV, R4      ; Point to register save area
981        MVI@    R4,     R0      ; Restore R0
982        MVI@    R4,     R1      ; Restore R1
983        MVI@    R4,     R2      ; Restore R2
984        MVI@    R4,     R3      ; Restore R3
985        MVI@    R4,     R4      ; Restore R4
986
987        JR      R5              ; Go there
988
989        ENDP
990
991;;==========================================================================;;
992;;  DOFACE                                                                  ;;
993;;==========================================================================;;
994DOFACE  PROC
995        DIS
996        MVII    #$2F0,  SP
997
998        MVII    #@@loadface, R0
999        MVO     R0,     PREVBL
1000        SWAP    R0
1001        MVO     R0,     PREVBL+1
1002
1003        CLRR    R0
1004        MVII    #$F0,   R1
1005        MVII    #$200,  R4
1006        CALL    FILLMEM
1007
1008        MVII    #$200 + 8 + 20, R4
1009        MVII    #FACE,  R5
1010        MVII    #9,     R0
1011        MVO     R0,     CB
1012        MVO     R0,     CS0
1013@@f_oloop:
1014        MVII    #6,     R2
1015@@f_iloop:
1016        MVI@    R5,     R1
1017        MVO@    R1,     R4
1018        DECR    R2
1019        BNEQ    @@f_iloop
1020        ADDI    #14,    R4
1021        DECR    R0
1022        BNEQ    @@f_oloop
1023
1024        CALL    DRAWSTRING3
1025        DECLE   0,      $200 + 20*10
1026                ;01234567890123456789
1027        BYTE    $4A, $6F, $65, $20, $5A, $62, $69, $63, $69, $61, $6B
1028        BYTE    $20, $73, $61, $79, $73, $20, $48, $49, $21, $0A, $00
1029
1030        EIS
1031        DECR    PC
1032
1033@@loadface:
1034        MVII    #@@shunt, R0
1035        MVO     R0,     PREVBL
1036        SWAP    R0
1037        MVO     R0,     PREVBL+1
1038
1039        MVII    #9,     R0
1040        MVO     R0,     CS0
1041        MVO     R0,     CB
1042
1043        CALL    LOADFONT
1044        DECLE   FACEFONT
1045@@shunt:
1046        MVO     R0,     $20
1047        PULR    PC
1048        ENDP
1049
1050;;==========================================================================;;
1051;;  Colored squares pixel-manipulation routines.                            ;;
1052;;  J. Zbiciak, July 1999                                                   ;;
1053;;==========================================================================;;
1054
1055; Data tables for pixel routines
1056MSKTBL:
1057        DECLE   $0007, $0038, $01C0, $2600
1058
1059CLRTBL:
1060        DECLE   $0000, $0249, $0492, $06DB
1061        DECLE   $2124, $236D, $25B6, $27FF
1062
1063
1064;;==========================================================================;;
1065;;  PIXCALC -- Pixel Calculation:  Convert x,y into addr,mask               ;;
1066;;                                                                          ;;
1067;;  INPUTS:                                                                 ;;
1068;;  R1 -- X coordinate                                                      ;;
1069;;  R2 -- Y coordinate                                                      ;;
1070;;  R5 -- Return address                                                    ;;
1071;;                                                                          ;;
1072;;  OUTPUTS:                                                                ;;
1073;;  R1 -- Pixel mask                                                        ;;
1074;;  R2 -- Address in display memory.                                        ;;
1075;;  R3, R4 -- Clobbered.                                                    ;;
1076;;==========================================================================;;
1077PIXCALC         PROC
1078        SLL     R2,     1       ; y' = y << 1
1079        MOVR    R2,     R4      ; mskidx = y << 1
1080        ANDI    #2,     R4      ; mskidx = (y << 1) & 2
1081        SUBR    R4,     R2      ; y' = (y & ~1) << 1
1082        MOVR    R2,     R3      ; save y'
1083        SLL     R2,     2       ; y'' = (y & ~1) << 3
1084        ADDR    R3,     R2      ; y''' = y' + y'' = (y >> 1) * 20
1085        SARC    R1,     1       ; c = x & 1;  x' = x >> 1
1086        ADCR    R4              ; mskidx = ((y << 1) & 2) + (x & 1)
1087        ADDR    R1,     R2      ; ofs = x' + y''' = (x >> 1) + (y >> 1) * 20
1088        ADDI    #$200,  R2      ; ptr = ofs + $200 = &BACKTAB[ofs] ==> R2
1089        ADDI    #MSKTBL,R4      ; mskptr = MSKTBL + mskidx' = &MSKTBL[mskidx]
1090        MVI@    R4,     R1      ; mask = *mskptr ==> R1
1091        JR      R5              ; return
1092
1093        ENDP
1094
1095;;==========================================================================;;
1096;;  PUTPIXEL -- Put pixel at x, y coordinates with color 'c' (0-7)          ;;
1097;;                                                                          ;;
1098;;  INPUTS:                                                                 ;;
1099;;  R0 -- Color                                                             ;;
1100;;  R1 -- X coordinate                                                      ;;
1101;;  R2 -- Y coordinate                                                      ;;
1102;;  R5 -- Return address                                                    ;;
1103;;                                                                          ;;
1104;;  OUTPUTS:                                                                ;;
1105;;  R1 -- Word stored at R2                                                 ;;
1106;;  R2 -- Display memory address                                            ;;
1107;;  R0, R3, R4, R5 -- Clobbered.                                            ;;
1108;;==========================================================================;;
1109
1110PUTPIXEL        PROC
1111        PSHR    R5              ; Save return address
1112        MVII    #PUTPIXELR+1,R5 ; Return to actual pixel draw below
1113        B       PIXCALC         ; Convert x,y into mask,addr
1114
1115;;==========================================================================;;
1116;;  PUTPIXELR -- Put pixel 'raw': accepts address, mask, and color          ;;
1117;;               (Alternate entry point for PUTPIXEL.)                      ;;
1118;;                                                                          ;;
1119;;  INPUTS:                                                                 ;;
1120;;  R0 -- Color                                                             ;;
1121;;  R1 -- Pixel mask                                                        ;;
1122;;  R2 -- Display memory address                                            ;;
1123;;                                                                          ;;
1124;;  OUTPUTS:                                                                ;;
1125;;  R1 -- Word stored at R2                                                 ;;
1126;;  R2 -- Display memory address                                            ;;
1127;;  R0,R5 -- Clobbered.                                                     ;;
1128;;==========================================================================;;
1129PUTPIXELR:
1130        PSHR    R5              ; Save return address
1131        MOVR    R0,     R5
1132        ADDI    #CLRTBL,R5      ; clrptr = CLRTBL + c' = &CLRTBL[c]
1133        MVI@    R5,     R0      ; color = CLRTBL[c]
1134        ANDR    R1,     R0      ; color = color & mask  (select desired pix)
1135        COMR    R1              ; invert mask
1136        AND@    R2,     R1      ; pix = BACKTAB & ~mask (clear pixel)
1137        ADDR    R0,     R1      ; pix' = pix + color    (merge pixels)
1138        MVO@    R1,     R2      ; BACKTAB[ofs] = pix'   (write pixels)
1139        PULR    PC              ; return.
1140
1141        ENDP
1142
1143;;==========================================================================;;
1144;;  PIXELCLIP                                                               ;;
1145;;  Returns with C==1 if pixel is onscreen                                  ;;
1146;;                                                                          ;;
1147;;  INPUTS:                                                                 ;;
1148;;  R1 -- X coordinate                                                      ;;
1149;;  R2 -- Y coordinate                                                      ;;
1150;;  R5 -- Return address                                                    ;;
1151;;                                                                          ;;
1152;;  OUTPUTS:                                                                ;;
1153;;  Carry == 1 if onscreen, 0 if offscreen.                                 ;;
1154;;==========================================================================;;
1155
1156PIXELCLIP       PROC
1157        TSTR    R1              ; Off left?
1158        BMI     @@offscr        ; Yes:  Set carry and exit
1159        TSTR    R2              ; Off top?
1160        BMI     @@offscr        ; Yes:  Set carry and exit
1161        CMPI    #39,    R1      ; Off right?
1162        BGT     @@offscr        ; Yes:  Set carry and exit
1163        CMPI    #23,    R2      ; Off bottom?
1164        BGT     @@offscr        ; Yes:  Set carry and exit
1165        SETC                    ; No to all: Clear carry and exit
1166        JR      R5
1167@@offscr
1168        CLRC
1169        JR      R5
1170        ENDP
1171
1172;;==========================================================================;;
1173;;  GETPIXELS -- Calls GETPIXEL, but saves/restores X, Y                    ;;
1174;;                                                                          ;;
1175;;  INPUTS:                                                                 ;;
1176;;  R1 -- X coordinate                                                      ;;
1177;;  R2 -- Y coordinate                                                      ;;
1178;;  R5 -- Return addr.                                                      ;;
1179;;                                                                          ;;
1180;;  OUTPUTS                                                                 ;;
1181;;  R0 -- Color                                                             ;;
1182;;  R1 -- X coordinate                                                      ;;
1183;;  R2 -- Y coordinate                                                      ;;
1184;;  R3, R4, R5 -- Clobbered.                                                ;;
1185;;==========================================================================;;
1186GETPIXELS       PROC
1187        PSHR    R5
1188@@chain:
1189        MVO     R1,     XSAVE
1190        MVO     R2,     YSAVE
1191        CALL    GETPIXEL
1192        MVI     XSAVE,  R1
1193        MVI     YSAVE,  R2
1194        PULR    PC
1195        ENDP
1196
1197
1198;;==========================================================================;;
1199;;  PUTPIXELS -- Calls PUTPIXEL, but saves/restores X, Y                    ;;
1200;;                                                                          ;;
1201;;  INPUTS:                                                                 ;;
1202;;  R0 -- Color                                                             ;;
1203;;  R1 -- X coordinate                                                      ;;
1204;;  R2 -- Y coordinate                                                      ;;
1205;;  R5 -- Return addr.                                                      ;;
1206;;                                                                          ;;
1207;;  OUTPUTS                                                                 ;;
1208;;  R0 -- Color                                                             ;;
1209;;  R1 -- X coordinate                                                      ;;
1210;;  R2 -- Y coordinate                                                      ;;
1211;;  R3, R4, R5 -- Clobbered.                                                ;;
1212;;==========================================================================;;
1213PUTPIXELS       PROC
1214        PSHR    R5
1215@@chain:
1216        MVO     R1,     XSAVE
1217        MVO     R2,     YSAVE
1218        MVO     R0,     CSAVE
1219        CALL    PUTPIXEL
1220        MVI     CSAVE,  R0
1221        MVI     XSAVE,  R1
1222        MVI     YSAVE,  R2
1223        PULR    PC
1224        ENDP
1225
1226;;==========================================================================;;
1227;;  GETPIXELSC                                                              ;;
1228;;  Calls GETPIXELS or returns R0 unchanged if pixel is off screen          ;;
1229;;                                                                          ;;
1230;;  INPUTS:                                                                 ;;
1231;;  R0 -- Color to return if off-screen                                     ;;
1232;;  R1 -- X coordinate                                                      ;;
1233;;  R2 -- Y coordinate                                                      ;;
1234;;  R5 -- Return addr.                                                      ;;
1235;;                                                                          ;;
1236;;  OUTPUTS                                                                 ;;
1237;;  R0 -- Color                                                             ;;
1238;;  R1 -- X coordinate                                                      ;;
1239;;  R2 -- Y coordinate                                                      ;;
1240;;  R3, R4, R5 -- Clobbered.                                                ;;
1241;;==========================================================================;;
1242GETPIXELSC      PROC
1243        PSHR    R5              ; Save return address
1244        CALL    PIXELCLIP       ; See if pixel is onscreen
1245        ADCR    PC              ; If it is, then skip the return
1246        PULR    PC              ; Otherwise return.
1247        B       GETPIXELS.chain ; If onscreen chain over to GETPIXELS
1248        ENDP
1249
1250
1251;;==========================================================================;;
1252;;  GETPIXEL -- Get pixel at x, y address                                   ;;
1253;;                                                                          ;;
1254;;  INPUTS:                                                                 ;;
1255;;  R1 -- X coordinate                                                      ;;
1256;;  R2 -- Y coordinate                                                      ;;
1257;;                                                                          ;;
1258;;  OUTPUTS:                                                                ;;
1259;;  R0 -- Color                                                             ;;
1260;;  R2 -- Display memory address                                            ;;
1261;;  R1, R3, R4, R5 -- Clobbered.                                            ;;
1262;;==========================================================================;;
1263GETPIXEL        PROC
1264        PSHR    R5              ; Save return address
1265        MVII    #GETPIXELR+1,R5 ; Return to actual pixel read below
1266        B       PIXCALC         ; Convert x,y into mask,addr
1267
1268;;==========================================================================;;
1269;;  GETPIXELR -- Get pixel 'raw': accepts address, mask                     ;;
1270;;               (Alternate entry point for getpixel)                       ;;
1271;;  INPUTS:                                                                 ;;
1272;;  R1 -- Pixel mask                                                        ;;
1273;;  R2 -- Display memory address                                            ;;
1274;;                                                                          ;;
1275;;  OUTPUTS:                                                                ;;
1276;;  R0 -- Color                                                             ;;
1277;;  R2 -- Display memory address                                            ;;
1278;;  R1, R5 -- Clobbered.                                                    ;;
1279;;==========================================================================;;
1280GETPIXELR:
1281        PSHR    R5              ; Save return address
1282        MVI@    R2,     R0      ; pix = BACKTAB[ofs]
1283        ANDR    R1,     R0      ; pix' = pix & mask
1284        MOVR    R0,     R1      ;
1285        SWAP    R0,     1       ;
1286        ANDI    #$20,   R0      ;
1287        ADDR    R1,     R0      ; merge oddball bit from lower right pixel
1288        SLL     R1,     2       ;
1289        SWAP    R1,     1       ;
1290        ADDR    R1,     R0      ; Fold upper, lower pairs of pixels
1291        MOVR    R0,     R1      ;
1292        SLR     R0,     1       ;
1293        SLR     R0,     2       ;
1294        ADDR    R1,     R0      ; Fold left, right pixels
1295        ANDI    #7,     R0      ; Select remaining pixel
1296        PULR    PC              ; return
1297
1298        ENDP
1299
1300
1301;;==========================================================================;;
1302;;  PUTPIXELSCQ                                                             ;;
1303;;                                                                          ;;
1304;;  Queues a pixel for display if it's onscreen.  This routine does not     ;;
1305;;  draw the pixel directly, but rather puts it in a queue of pixels that   ;;
1306;;  are waiting to be drawn.  This queue is emptied during the interrupt    ;;
1307;;  service routine after the queue is "committed" for display with a call  ;;
1308;;  to COMMITPXQ.                                                           ;;
1309;;                                                                          ;;
1310;;  The pixel queue serves as a mechanism for reducing/eliminating the      ;;
1311;;  flicker associated with redrawing a game piece.  Without the queue,     ;;
1312;;  piece movements could result in a significant amount of flicker.        ;;
1313;;  To further reduce flicker, the queue insert routine attempts to         ;;
1314;;  optimize multiple 'PUTPIXELs' to the same location, with the last       ;;
1315;;  PUTPIXEL taking precedence.                                             ;;
1316;;                                                                          ;;
1317;;  Because this queue is intended for drawing game pieces only, it is      ;;
1318;;  not very large.  No checking is performed for queue overflow, either.   ;;
1319;;  Also, because double-buffering is not performed, this routine will      ;;
1320;;  block if there is a committed pixel queue waiting to be drawn.          ;;
1321;;                                                                          ;;
1322;;  INPUTS:                                                                 ;;
1323;;  R0 -- Color                                                             ;;
1324;;  R1 -- X coordinate                                                      ;;
1325;;  R2 -- Y coordinate                                                      ;;
1326;;                                                                          ;;
1327;;  OUTPUTS                                                                 ;;
1328;;  R0 -- Color                                                             ;;
1329;;  R1 -- X coordinate                                                      ;;
1330;;  R2 -- Y coordinate                                                      ;;
1331;;  R3, R4, R5 -- Clobbered.                                                ;;
1332;;==========================================================================;;
1333PUTPIXELSCQ     PROC
1334        PSHR    R5              ; Save return address
1335        CALL    PIXELCLIP       ; See if pixel is onscreen
1336        ADCR    PC              ; If it is, then skip the return
1337        PULR    PC              ; Otherwise return.
1338
1339        ; Merge Y coordinate and color up-front, since we store
1340        ; them merged in the queue.
1341        MOVR    R0,     R3      ; Work on a copy of our color
1342        SWAP    R3
1343        SLR     R3,     2
1344        SLR     R3,     1       ; R3 = color << 5
1345        XORR    R3,     R2      ; Put color in bits 5..7 of R2
1346
1347        DIS     ; Critical section: Queue management.
1348        ; First, check to see if any pixels are queued at all.  If there
1349        ; aren't any, just jump straight to the queue-insert.
1350        MVI     PXQ_L0, R3
1351        TSTR    R3
1352        BEQ     @@insert_eis
1353
1354        ; Next, check to see if there are any committed pixels waiting
1355        ; to be drawn in the pixel queue.  Wait until the queue is drained.
1356        CLRR    R3
1357        CMP     PXQ_L1, R3      ; If there are no committed pixels, we
1358        EIS     ; Interrupts can be enabled now.
1359        BEQ     @@check         ; still need to check for redundant pixels.
1360@@drain:
1361        CMP     PXQ_L1, R3      ; Otherwise, let the queue drain and jump
1362        BNEQ    @@drain         ; straight to the insert since it's empty
1363        B       @@insert
1364
1365@@check:
1366        ; If we reach here, we have a non-empty pixel queue, and no
1367        ; committed pixels.  Step through the queue to see if we have
1368        ; any redundant pixels.
1369        MVII    #PXQ_XY - 1, R4 ; Point at start of Queue minus 1
1370        MVI     PXQ_L0, R3      ; Get queue length
1371
1372@@notx: INCR    R4              ; Where we branch when X didn't match.
1373@@noty: DECR    R3              ; Decrement loop count
1374        BMI     @@insert        ; Get out of here when it expires.
1375@@rloop:
1376        CMP@    R4,     R1      ; Compare X coords
1377        BNEQ    @@notx          ; X miscompared.
1378
1379        MOVR    R2,     R5
1380        XOR@    R4,     R5
1381        ANDI    #$1F,   R5      ; Cmp Y coords (lower 5 bits of queue entry)
1382        BNEQ    @@noty          ; Y miscompared
1383
1384        ; If we get here, the coordinates match, so just update the color
1385        ; and exit the queue insertion process entirely.
1386        DECR    R4
1387        B       @@styc          ; Store Y coordinate and color, and leave
1388
1389@@insert_eis:
1390        EIS
1391@@insert:
1392        ; If we get here, then this is not a duplicate pixel, so we need
1393        ; to extend the queue.  Warning:  No bounds checking is done here.
1394        MVI     PXQ_L0, R3
1395        MOVR    R3,     R4      ; Copy the old queue length to R4
1396        ADDR    R3,     R4      ; Multiply it by 2
1397        INCR    R3
1398        MVO     R3,     PXQ_L0
1399        ADDI    #PXQ_XY, R4     ; Point R4 at pixel queue
1400
1401@@stx:  MVO@    R1,     R4      ; Store the X coordinate
1402@@styc: MVO@    R2,     R4      ; Store the Y coordinate and color
1403
1404        ANDI    #$1F,   R2      ; Strip the color from the Y + color.
1405
1406        PULR    PC              ; Return.
1407
1408        ENDP
1409
1410;;==========================================================================;;
1411;;  DRAINPXQ                                                                ;;
1412;;  Drains the pixel queue (intended to be called from an ISR).             ;;
1413;;                                                                          ;;
1414;;  INPUTS:                                                                 ;;
1415;;  R5 -- Return address                                                    ;;
1416;;  Pixels in the committed pixel queue                                     ;;
1417;;                                                                          ;;
1418;;  OUTPUTS:                                                                ;;
1419;;  R0..R5 clobbered.                                                       ;;
1420;;==========================================================================;;
1421DRAINPXQ        PROC
1422        PSHR    R5
1423        DIS                     ; Critical section: Grab committed pixels.
1424        MVI     PXQ_L1, R3
1425        TSTR    R3
1426        BEQ     @@leave
1427        CLRR    R1
1428        MVO     R1,     PXQ_L0  ; Clear count of queued pixels.
1429        MVO     R1,     PXQ_L1  ; Clear count of comitted pixels.
1430        EIS
1431        MVII    #PXQ_XY, R4
1432@@pixloop:
1433        MVI@    R4,     R1      ; Get X coordinate
1434        MVI@    R4,     R2      ; Get Y coordinate and color
1435        MOVR    R2,     R0      ; Separate Y coordinate and color
1436        SLL     R0,     2
1437        SLL     R0,     1
1438        SWAP    R0              ; Color is now in 3 lsbs.
1439        ANDI    #$7,    R0      ; Mask away undesired bits in color
1440        ANDI    #$1F,   R2      ; Mask away undesired bits in Y coord.
1441        PSHR    R3
1442        PSHR    R4
1443        CALL    PUTPIXEL        ; Display the pixel
1444        PULR    R4
1445        PULR    R3
1446        DECR    R3              ; Decrement our loop count
1447        BNEQ    @@pixloop       ; Keep looping until we've displayed them all
1448@@leave:
1449        EIS
1450        PULR    PC
1451        ENDP
1452
1453;;==========================================================================;;
1454;;  COMMITPXQ                                                               ;;
1455;;  Commits queued pixels in the pixel queue to be displayed.               ;;
1456;;                                                                          ;;
1457;;  INPUTS:                                                                 ;;
1458;;  R5 -- Return address                                                    ;;
1459;;  Pixels in the pixel queue.                                              ;;
1460;;                                                                          ;;
1461;;  OUTPUTS:                                                                ;;
1462;;  Pixels are committed for display.                                       ;;
1463;;==========================================================================;;
1464COMMITPXQ       PROC
1465        PSHR    R5              ; Save return address on stack.
1466        DIS                     ; Disable interrupts (critical section)
1467        MVI     PXQ_L1, R5      ; See if there are already committed pixels
1468        TSTR    R5              ; ... waiting to be drawn.  (oops!)
1469        BNEQ    @@leave         ; Yes.. then leave.
1470        MVI     PXQ_L0, R5      ; No?  Get our count of non-committed pixels.
1471        MVO     R5,     PXQ_L1  ; Store it to our count of committed pixels.
1472@@leave:
1473        EIS                     ; Done with critical section.
1474        PULR    PC              ; Return to caller
1475        ENDP
1476
1477;;==========================================================================;;
1478;;  PIECE                                                                   ;;
1479;;  The Piece Shape Table                                                   ;;
1480;;                                                                          ;;
1481;;  Shape encoding for the game pieces                                      ;;
1482;;                                                                          ;;
1483;;    The game pieces are encoded as a series of moves from the "center     ;;
1484;;    point".  Blocks are placed _after_ each move.  All pieces are         ;;
1485;;    composed of four moves except piece 6 (the T) which requires 5        ;;
1486;;    moves, and piece 5 which uses an extra move so that it is centered    ;;
1487;;    in the "next piece" box.                                              ;;
1488;;                                                                          ;;
1489;;    Each move is encoded in two bits.  The LS bit specifies the axis      ;;
1490;;    (up/down or left/right), and the MS bit specifies the sign of         ;;
1491;;    the direction on that axis (1==+1 and 0==-1).  Moves are processed    ;;
1492;;    from the LS bit pair up to the MS bit pair.  This encoding allows     ;;
1493;;    each piece to be described in exactly one decle.                      ;;
1494;;                                                                          ;;
1495;;    NOTE:  The fifth move may NOT be "up".  The rotation code             ;;
1496;;    distiguishes between four-move and five-move pieces by whether the    ;;
1497;;    upper two bits are zero.  If they're both zero, it's a four-move      ;;
1498;;    piece, otherwise the two bits specify the fifth move.  (Recall that   ;;
1499;;    00 corresponds to "up".)                                              ;;
1500;;                                                                          ;;
1501;;    Right now, rotations are performed in a brute-force manner:  We       ;;
1502;;    just encode all of the rotations in our data set here.  It's          ;;
1503;;    seems to be alot cheaper than writing the code to rotate pieces,      ;;
1504;;    although I can't be sure unless I do them both.  At the very least,   ;;
1505;;    the brute-force approach is alot easier.                              ;;
1506;;                                                                          ;;
1507;;  Notation:  pXrY is "Piece #X, Rotation #Y".  X == 0..6, Y == 0..3.      ;;
1508;;                                                                          ;;
1509;;             up#, dn#, lf# and rt# indicate a movement in a direction     ;;
1510;;             on a given move number.                                      ;;
1511;;                                                                          ;;
1512;;             Pieces are shown in the comments in their initial            ;;
1513;;             orientation.  The block marked with %% is the "pivot"        ;;
1514;;             block.                                                       ;;
1515;;==========================================================================;;
1516PIECE   PROC
1517
1518; handy constants for defining the moves.
1519@@up0   EQU     0000000000b
1520@@dn0   EQU     0000000010b
1521@@lf0   EQU     0000000001b
1522@@rt0   EQU     0000000011b
1523
1524@@up1   EQU     0000000000b
1525@@dn1   EQU     0000001000b
1526@@lf1   EQU     0000000100b
1527@@rt1   EQU     0000001100b
1528
1529@@up2   EQU     0000000000b
1530@@dn2   EQU     0000100000b
1531@@lf2   EQU     0000010000b
1532@@rt2   EQU     0000110000b
1533
1534@@up3   EQU     0000000000b
1535@@dn3   EQU     0010000000b
1536@@lf3   EQU     0001000000b
1537@@rt3   EQU     0011000000b
1538
1539;;up4           not allowed
1540@@dn4   EQU     1000000000b
1541@@lf4   EQU     0100000000b
1542@@rt4   EQU     1100000000b
1543
1544; Piece 0:  The Long Bar
1545;
1546;  ###
1547;  ###
1548;  %%%
1549;  %%%
1550;  ###
1551;  ###
1552;  ###
1553;  ###
1554;
1555@@p0r0  BYTE    @@up0 + @@dn1 + @@dn2 + @@dn3
1556@@p0r1  BYTE    @@lf0 + @@rt1 + @@rt2 + @@rt3
1557@@p0r2  BYTE    @@up0 + @@dn1 + @@dn2 + @@dn3
1558@@p0r3  BYTE    @@lf0 + @@rt1 + @@rt2 + @@rt3
1559
1560; Piece 1:  The Square Block
1561;
1562;  ###%%%
1563;  ###%%%
1564;  ######
1565;  ######
1566;
1567@@p1r0  BYTE    @@dn0 + @@lf1 + @@up2 + @@rt3
1568@@p1r1  BYTE    @@dn0 + @@lf1 + @@up2 + @@rt3
1569@@p1r2  BYTE    @@dn0 + @@lf1 + @@up2 + @@rt3
1570@@p1r3  BYTE    @@dn0 + @@lf1 + @@up2 + @@rt3
1571
1572; Piece 2:  The L shape
1573;
1574;  ###%%%###
1575;  ###%%%###
1576;  ###
1577;  ###
1578;
1579@@p2r0  BYTE    @@rt0 + @@lf1 + @@lf2 + @@dn3
1580@@p2r1  BYTE    @@dn0 + @@up1 + @@up2 + @@lf3
1581@@p2r2  BYTE    @@lf0 + @@rt1 + @@rt2 + @@up3
1582@@p2r3  BYTE    @@up0 + @@dn1 + @@dn2 + @@rt3
1583
1584; Piece 3: The Reverse-L shape
1585;
1586;  ###%%%###
1587;  ###%%%###
1588;        ###
1589;        ###
1590;
1591@@p3r0  BYTE    @@lf0 + @@rt1 + @@rt2 + @@dn3
1592@@p3r1  BYTE    @@up0 + @@dn1 + @@dn2 + @@lf3
1593@@p3r2  BYTE    @@rt0 + @@lf1 + @@lf2 + @@up3
1594@@p3r3  BYTE    @@dn0 + @@up1 + @@up2 + @@rt3
1595
1596; Piece 4: The S shape
1597;
1598;     %%%###
1599;     %%%###
1600;  ######
1601;  ######
1602;
1603@@p4r0  BYTE    @@rt0 + @@lf1 + @@dn2 + @@lf3
1604@@p4r1  BYTE    @@up0 + @@dn1 + @@rt2 + @@dn3
1605@@p4r2  BYTE    @@rt0 + @@lf1 + @@dn2 + @@lf3
1606@@p4r3  BYTE    @@up0 + @@dn1 + @@rt2 + @@dn3
1607
1608; Piece 5: The Z shape
1609;
1610;  ###SSS          Note: SSS brick is the actual starting brick.
1611;  ###SSS
1612;     %%%###
1613;     %%%###
1614;
1615@@p5r0  DECLE   @@dn0 + @@rt1 + @@lf2 + @@up3 + @@lf4
1616@@p5r1  DECLE   @@dn0 + @@up1 + @@dn2 + @@lf3 + @@dn4
1617@@p5r2  DECLE   @@dn0 + @@rt1 + @@lf2 + @@up3 + @@lf4
1618@@p5r3  DECLE   @@dn0 + @@up1 + @@dn2 + @@lf3 + @@dn4
1619
1620; Piece 6: The T shape
1621;
1622;  ###%%%###
1623;  ###%%%###
1624;     ###
1625;     ###
1626;
1627@@p6r0  DECLE   @@lf0 + @@rt1 + @@dn2 + @@up3 + @@rt4
1628@@p6r1  DECLE   @@up0 + @@dn1 + @@lf2 + @@rt3 + @@dn4
1629@@p6r2  DECLE   @@lf0 + @@rt1 + @@up2 + @@dn3 + @@rt4
1630@@p6r3  DECLE   @@up0 + @@dn1 + @@rt2 + @@lf3 + @@dn4
1631
1632        ENDP
1633
1634
1635;;==========================================================================;;
1636;;  PUTPIECEREC                                                             ;;
1637;;  Writes a piece record to memory.                                        ;;
1638;;                                                                          ;;
1639;;  PUTPIECEREC.NC                                                          ;;
1640;;  Writes a piece record to memory, except color.                          ;;
1641;;                                                                          ;;
1642;;  INPUTS:                                                                 ;;
1643;;  R0 -- piece color                                                       ;;
1644;;  R1 -- X coord of pivot                                                  ;;
1645;;  R2 -- Y coord of pivot                                                  ;;
1646;;  R3 -- piece number/rotation                                             ;;
1647;;  R4 -- Address of piece information record (+1 if PUTPIECEREC.NC)        ;;
1648;;  R5 -- Return address                                                    ;;
1649;;                                                                          ;;
1650;;  OUTPUTS:                                                                ;;
1651;;  R0..R3, R5 -- not modified                                              ;;
1652;;  R4 -- points just past end of of piece information record               ;;
1653;;==========================================================================;;
1654PUTPIECEREC     PROC
1655        MVO@    R0,     R4      ; Put piece color
1656@@NC:
1657        MVO@    R1,     R4      ; Put piece pivot X coordinate
1658        MVO@    R2,     R4      ; Put piece pivot Y coordinate
1659        MVO@    R3,     R4      ; Put piece number/rotation
1660
1661        JR      R5              ; Return
1662        ENDP
1663
1664;;==========================================================================;;
1665;;  GETPIECEREC                                                             ;;
1666;;  Reads a piece record from memory.                                       ;;
1667;;                                                                          ;;
1668;;  GETPIECEREC.NC                                                          ;;
1669;;  Reads a piece record from memory, except color.                         ;;
1670;;                                                                          ;;
1671;;  INPUTS:                                                                 ;;
1672;;  R4 -- Address of piece information record (+1 if GETPIECEREC.NC)        ;;
1673;;  R5 -- Return address                                                    ;;
1674;;                                                                          ;;
1675;;  OUTPUTS:                                                                ;;
1676;;  R0 -- piece color (unless GETPIECEREC.NC)                               ;;
1677;;  R1 -- X coord of pivot                                                  ;;
1678;;  R2 -- Y coord of pivot                                                  ;;
1679;;  R3 -- piece number/rotation                                             ;;
1680;;==========================================================================;;
1681GETPIECEREC     PROC
1682        MVI@    R4,     R0      ; Get piece color
1683@@NC:
1684        MVI@    R4,     R1      ; Get piece pivot X coordinate
1685        MVI@    R4,     R2      ; Get piece pivot Y coordinate
1686
1687        ; Sign-extend R1 and R2 since they're in 8-bit RAM.
1688        MVII    #$7F,   R3      ; Generate constant $FF80
1689        COMR    R3              ; R3 is "magic sign-extend constant"
1690
1691        ADDR    R3,     R1      ; Propogate sign-bit info to upper-half
1692        XORR    R3,     R1      ; R1 is fully sign-extended here.
1693
1694        ADDR    R3,     R2      ; Propogate sign-bit info to upper-half
1695        XORR    R3,     R2      ; R2 is fully sign-extended here.
1696
1697        MVI@    R4,     R3      ; Get piece number/rotation
1698
1699        JR      R5              ; Return
1700        ENDP
1701
1702;;==========================================================================;;
1703;;  DRAWPIECE                                                               ;;
1704;;  Draws a piece on the screen in the desired color (0..7).                ;;
1705;;                                                                          ;;
1706;;  TESTPIECE                                                               ;;
1707;;  Determines if a piece can be drawn in a given location by tracing its   ;;
1708;;  path and doing GETPIXELSC calls.                                        ;;
1709;;                                                                          ;;
1710;;  INPUTS:                                                                 ;;
1711;;  R0 -- If DRAWPIECE, color of piece to draw (0..7)                       ;;
1712;;  R1 -- X coordinate of pivot                                             ;;
1713;;  R2 -- Y coordinate of pivot                                             ;;
1714;;  R3 -- Piece number                                                      ;;
1715;;  R5 -- Return address                                                    ;;
1716;;                                                                          ;;
1717;;  OUTPUTS:                                                                ;;
1718;;  R0 -- If TESTPIECE, Zero == OK, non-zero == not OK; else, trashed.      ;;
1719;;  R4 -- Minimum Y coordinate                                              ;;
1720;;  R5 trashed.                                                             ;;
1721;;                                                                          ;;
1722;;  $130..$135 used for temporary storage.                                  ;;
1723;;==========================================================================;;
1724DRAWPIECE       PROC
1725@@colsv EQU     $130            ; Piece color save area
1726@@pvxsv EQU     $131            ; Pivot X coordinate save area
1727@@pvysv EQU     $132            ; Pivot Y coordinate save area
1728@@pnrsv EQU     $133            ; Piece number/rotation save area
1729@@pcwsv EQU     $134            ; Loop count save area
1730@@cntsv EQU     $135            ; Loop count save area
1731@@miny  EQU     $136
1732
1733        ADDR    R0,     R0      ; Shift color left by 1
1734        INCR    R0              ; Set the LSB on the color
1735        INCR    PC              ; Skip the CLRR R0
1736TESTPIECE:
1737        CLRR    R0              ; Start with color = 0, LSB = 0
1738
1739        PSHR    R5              ; Save return address
1740        MVII    #@@colsv, R4    ; Point R4 at save area
1741        CALL    PUTPIECEREC
1742
1743        MVO     R2,     @@miny  ; Start with Min Y == pivot point
1744
1745        ; Convert piece number to control word.
1746        ADDI    #PIECE, R3      ; Point into PIECE array entry for piece
1747        MVI@    R3,     R3      ; Get the piece control word
1748
1749        MVII    #3,     R4      ; At least four moves in piece
1750
1751@@loop:
1752        MOVR    R3,     R0      ; Copy Piece Control Word
1753        ANDI    #2,     R0      ; Get sign for move (set == +1, clear == -1)
1754        DECR    R0              ; R0 is either +1/-1 according to move
1755        SARC    R3,     2       ; Shift move away from PCW. Carry==axis
1756        BC      @@xaxis         ; Carry Clear == Y-Axis, Carry Set == X-Axis
1757        ADDR    R0,     R2      ; Add move to Y axis
1758        ; See if this Y coordinate is our new min-Y coord
1759        CMP     @@miny, R2
1760        BGE     @@cont
1761        MVO     R2,     @@miny
1762
1763        INCR    PC              ; Skip add-to-X (next instruction)
1764@@xaxis:
1765        ADDR    R0,     R1      ; Add move to X axis
1766@@cont:
1767
1768        ; Note, at most 8 bits remain in PCW here, so we save in 8-bit loc.
1769        MVO     R3,     @@pcwsv ; Save remaining Piece Control Word (max 8 bit)
1770        MVO     R4,     @@cntsv ; Save our loop trip count
1771        MVI     @@colsv, R0     ; Get saved color
1772        SARC    R0,     1       ; If bit-0 set, this is DRAW, else TEST
1773        BC      @@draw          ;
1774@@test:
1775        ; Note R0 should be 0 here. This treats off-screen pixels as black,
1776        ; which is what we want.
1777        CMPI    #EDGEL, R1      ; Are we off left?
1778        BLT     @@notok         ; Yes:  Bad!
1779        CMPI    #EDGER, R1      ; Are we off left?
1780        BGT     @@notok         ; Yes:  Bad!
1781        CALL    GETPIXELSC      ; Get pixel, with clipping.
1782        CMPI    #7,     R0      ; Ignore color 7, since we use that
1783        BEQ     @@pixelok       ;    for the active piece.
1784        TSTR    R0              ; Otherwise, make sure it's black.
1785        BEQ     @@pixelok       ;
1786        B       @@notok         ; Didn't pass the tests?  It's not ok then.
1787@@draw:
1788        CALL    PUTPIXELSCQ     ; Put the pixel on the screen, with clipping.
1789@@pixelok:
1790
1791        MVI     @@pcwsv, R3     ; Get our saved piece number
1792        MVI     @@cntsv, R4     ; Get our loop trip count.
1793        DECR    R4
1794        BPL     @@loop          ; Loop as long as R4 > 0
1795
1796        CLRR    R4
1797        TSTR    R3              ; See if this is a 5-move piece.
1798        BNEQ    @@loop          ; Loop one last time if it is.
1799
1800        MVI     @@colsv, R0     ; Restore color register (if TESTPIECE is ok,
1801        SLR     R0,     1       ; ...OR if this is DRAWPIECE).
1802        INCR    PC              ; (skip the MOVR.)
1803
1804@@notok:                        ; Else, skip the restore if TESTPIECE and
1805        MOVR    PC,     R0      ; ...pixel is not ok, so force R0 non-zero
1806
1807        MVII    #@@pvxsv, R4    ; Point to save area + 1
1808        CALL    GETPIECEREC.NC
1809        MVI     @@miny, R4      ; Get minimum Y value into R4
1810
1811        PULR    PC              ; Return to the caller
1812        ENDP
1813
1814
1815;;==========================================================================;;
1816;;  GETPIECECOLOR                                                           ;;
1817;;  Gets Piece Color, given the piece number/rotation.                      ;;
1818;;                                                                          ;;
1819;;  INPUTS:                                                                 ;;
1820;;  R3 -- Piece number/rot.  Bits 0..1 are rotation, Bits 2..4 are piece #  ;;
1821;;  R5 -- Return address                                                    ;;
1822;;                                                                          ;;
1823;;  OUTPUTS:                                                                ;;
1824;;  R0 -- Piece color                                                       ;;
1825;;==========================================================================;;
1826GETPIECECOLOR   PROC
1827        PSHR    R3              ; Save R3 so that it's not clobbered
1828        SLR     R3,     2       ; Generate index into color table
1829        ADDI    #@@color,R3     ; R3 = PCCOLOR[piece]
1830        MVI@    R3,     R0      ; Get the piece color into R0
1831        PULR    R3              ; Restore R3
1832        JR      R5              ; Return.
1833
1834@@color BYTE    $2, $1, $3, $4, $5, $6, $1
1835        ENDP
1836
1837;;==========================================================================;;
1838;;  PICKPIECE                                                               ;;
1839;;  Grabs the 'next piece' and makes it the current piece.  Then picks a    ;;
1840;;  new 'next piece'.                                                       ;;
1841;;==========================================================================;;
1842PICKPIECE       PROC
1843        PSHR    R5
1844@@chain:
1845@@randloop:
1846        MVII    #4,     R2      ; Generate some new random bits
1847
1848        CALL    NEXTRANDX
1849        ANDI    #$1C,   R0      ; Mask to [0..7] * 4
1850        BEQ     @@randloop      ; If it comes up 0, try again
1851
1852        SUBI    #4,     R0      ; Make into piece #0..#6
1853
1854        CALL    NEXTCLEAR       ; Clear previous 'next-piece'
1855        MVI     NXTPC,  R3      ; Get previous 'next-piece'.
1856        MVO     R0,     NXTPC   ; Save new 'next-piece'.
1857
1858        MVI     SHOWNXT, R1     ; Are we showing next piece?
1859        MVO     R1,     DIDNXT  ; Initialize our "showed next piece" flag
1860        TSTR    R1
1861        BEQ     @@noshow
1862
1863        PSHR    R3              ; Save R3 -- new current piece number
1864        CALL    NEXTSHOW        ; Show next piece
1865        PULR    R3              ; Restore R3
1866
1867@@noshow:
1868        CALL    GETPIECECOLOR   ; Get the current piece's color.
1869        MVO     R0,     CS1     ; Force color-stack to piece's color
1870        MVO     R0,     CS3     ; Force color-stack to piece's color
1871
1872        MVII    #EDGEL+5, R1    ; Start out in middle of well...
1873        MVII    #3,       R2    ; ...just a little off top of screen
1874        COMR    R2              ; (Y = -3)
1875        MVII    #CURPC.C, R4    ; Write to "current piece" record.
1876        CALL    PUTPIECEREC     ; Make this the current piece.
1877
1878        PULR    PC              ; Return.
1879        ENDP
1880
1881;;==========================================================================;;
1882;;  CLEARWELL                                                               ;;
1883;;  Clears the well in a dramatic fashion                                   ;;
1884;;==========================================================================;;
1885CLEARWELL       PROC
1886@@bot   EQU     $1E2
1887        PSHR    R5
1888
1889        CLRR    R0
1890        MVO     R0,     CURPC.C ; make sure current piece number is cleared.
1891
1892@@wellloop:
1893        MVII    #EDGEB, R3
1894        CMP     HEIGHT, R3      ; R3 has actual well height.
1895        BLE     @@return        ; Do nothing if well is empty.
1896
1897@@randloop:
1898        MVII    #5,     R2
1899        CALL    NEXTRANDX       ; Get a random number (advance by 5 bits)
1900        ANDI    #31,    R0
1901        ADD     HEIGHT, R0
1902        CMPR    R3,     R0      ; Make sure it's less than the well height.
1903        BGE     @@randloop
1904
1905        MOVR    R0,     R2
1906        MVII    #EDGEL+1, R1
1907        CALL    GETPIXELS
1908        CMPI    #7,     R0
1909        BEQ     @@nukeit
1910@@fillit:
1911
1912        CALL    NUKELINE
1913        B       @@wellloop
1914
1915@@nukeit:
1916        MVO     R2,     @@bot
1917
1918        CALL    WAIT            ; Wait a few ticks
1919        DECLE   2               ;
1920
1921        MVII    #@@wellloop, R5
1922        PSHR    R5
1923        B       DOSCORE.nukeit  ; Nuke the line, collapsing the well.
1924                                ; This will return to @@wellloop
1925
1926@@return:
1927        PULR    PC              ; Return.
1928        ENDP
1929
1930;;==========================================================================;;
1931;;  SCOREDROP                                                               ;;
1932;;  Loops over a four line window and marks rows for deletion.              ;;
1933;;  Returns line-clearing score in R0 and leaves lines to be cleared set    ;;
1934;;  to color #7.                                                            ;;
1935;;                                                                          ;;
1936;;  NUKELINE                                                                ;;
1937;;  Force-clears a line as if it were full.                                 ;;
1938;;                                                                          ;;
1939;;  INPUTS:                                                                 ;;
1940;;  R2 == Row number to start at.                                           ;;
1941;;                                                                          ;;
1942;;  OUTPUTS:                                                                ;;
1943;;  R0 = Number of lines cleared.                                           ;;
1944;;  R2 = original value plus 4                                              ;;
1945;;  R3 = Number of lines cleared.                                           ;;
1946;;  R1, R4, R5 trashed                                                      ;;
1947;;                                                                          ;;
1948;;  $1E0..$1E3 used as temp storage.                                        ;;
1949;;==========================================================================;;
1950SCOREDROP       PROC
1951@@trip  EQU     $1E2
1952@@full  EQU     $1E3
1953
1954        PSHR    R5                      ; Save return address
1955        CLRR    R3                      ; Clear count of lines to score
1956        MVO     R3,     @@full
1957        MVII    #3,     R4              ; Loop trip count
1958
1959        B       @@doscore
1960NUKELINE:
1961        PSHR    R5
1962        CLRR    R4
1963        MVO     R4,     @@full
1964        MVO     R4,     @@trip
1965        B       @@nuke
1966
1967@@doscore:
1968        MVO     R4,     @@trip
1969
1970        MVII    #EDGEL, R1              ; Iterate over columns 15..24
1971@@scoreloop:
1972        CALL    GETPIXELS               ; Read a pixel
1973        TSTR    R0                      ; Is it zero?
1974        BEQ     @@sloop                 ; If so, stop -- line's not full.
1975
1976        INCR    R1
1977        CMPI    #EDGER, R1              ; At the end of the well?
1978        BLE     @@scoreloop             ; If not, keep looping
1979
1980@@nuke:
1981        MVI     @@full, R3
1982        INCR    R3
1983        MVO     R3,     @@full
1984
1985        ;; Prepare to set entire row in well to color 7
1986        MVII    #EDGEL, R1              ; Start at left edge of well
1987        MVII    #7,     R0              ; Color = 7
1988@@floop:
1989        CALL    PUTPIXELS               ; Draw the pixel
1990        INCR    R1                      ; Move to next column
1991        CMPI    #EDGER, R1              ; Are we at the right edge?
1992        BLE     @@floop                 ; No: Keep looping.
1993
1994@@sloop:
1995        MVI     @@trip, R4
1996        INCR    R2                      ; Go to next line
1997        DECR    R4                      ; Decrement loop count
1998        BPL     @@doscore               ; Keep going as long as R4 >= 0
1999
2000@@sdone:                                ; Scoring complete
2001        MVI     @@full, R3
2002
2003        ;; If this was a 4-line clear, play the special sound effect
2004        CMPI    #4,     R3
2005        BNEQ    @@return
2006        MVII    #2,     R1              ; High priority sound effect
2007        CALL    PLAY.sfxp
2008        DECLE   FXBOMB4
2009@@return:
2010        MOVR    R3,     R0              ; Return number of cleared lines.
2011        PULR    PC                      ; Return to caller.
2012
2013        ENDP
2014
2015;;==========================================================================;;
2016;;  DOSCORE                                                                 ;;
2017;;  Calls SCOREDROP to score a dropped piece and mark lines for deletion.   ;;
2018;;                                                                          ;;
2019;;  Calculates the score based on number of downward moves the player       ;;
2020;;  made, as well as the number of lines cleared.                           ;;
2021;;                                                                          ;;
2022;;  Collapses lines that have been marked for deletion by the scoring       ;;
2023;;  routines.  Processes the four lines ABOVE R2.  (This works pretty well, ;;
2024;;  since the scoring routines leave R2 pointing to the line AFTER the four ;;
2025;;  lines that were scored.)  Lines are scanned in the leftmost column,     ;;
2026;;  and lines containing a pixel of color 7 in this column are cleared.     ;;
2027;;                                                                          ;;
2028;;  INPUTS:                                                                 ;;
2029;;  R2 == Line at top of 4-line window to score                             ;;
2030;;                                                                          ;;
2031;;  OUTPUTS:                                                                ;;
2032;;  R0, R1, R2, R4, R5 trashed?                                             ;;
2033;;                                                                          ;;
2034;;  Locations $1E0..$1E3, $1E6 used as temporary storage.                   ;;
2035;;==========================================================================;;
2036SCORETABLE      DECLE   500, 1500, 3000, 6000
2037DOSCORE PROC
2038@@bot   EQU     $1E2
2039@@top   EQU     $1E3
2040@@clr   EQU     $1E6
2041
2042        PSHR    R5
2043
2044
2045        MVII    #EDGEB-4, R3    ; Make sure we're not too close to
2046        CMPR    R3,     R2      ;  the bottom of the well.
2047        BLE     @@rowok
2048        MOVR    R3,     R2      ; If we are, move our window up some.
2049@@rowok:
2050        PSHR    R2
2051
2052        ; First, add in any "move points"
2053        MVI     MSCORE, R0
2054
2055
2056        MVI     PSCORL, R1      ; Add this to our score
2057        MVI     PSCORH, R4
2058        ADDR    R0,     R1      ; Add moves to lower half of 32-bit score
2059        ADCR    R4              ; Propogate carry from lower to upper half
2060        MVO     R1,     PSCORL
2061        MVO     R4,     PSCORH
2062
2063        CLRR    R2
2064        MVO     R2,     MSCORE  ; Clear the total.
2065
2066        ; Wait one tick to make sure any queued pixels have been drawn.
2067        ; This works because the ISR always drains the committed pixel
2068        ; queue completely, and WAIT busywaits on a counter maintained
2069        ; by the ISR.  Cool, eh?
2070        CALL    WAIT
2071        DECLE   1
2072
2073        PULR    R2
2074        ; Next, see if we need to clear any lines due to this drop
2075        CALL    SCOREDROP       ; Score the drop.
2076
2077        MOVR    R0,     R5      ; If we didn't score anything, return
2078        BEQ     @@done          ; (chain return through PICKPIECE)
2079@@hmm
2080        ADDI    #SCORETABLE-1, R5 ; R5 = &scoretable[lines]
2081
2082        MVI@    R5,     R0      ; Load base score for this # of lines
2083
2084        MVI     LEVEL,  R3
2085
2086        MVI     PSCORL, R1      ; Otherwise, add this to our score
2087        MVI     PSCORH, R4
2088@@scoreloop:
2089        ADDR    R0,     R1      ; (multiply by level thru repeated addition)
2090        ADCR    R4
2091
2092        DECR    R3
2093        BPL     @@scoreloop     ; Go around 'level + 1' times.
2094
2095        MVO     R1,     PSCORL
2096        MVO     R4,     PSCORH
2097
2098@@notnuke:
2099        SUBI    #4,     R2      ; Find top of window
2100        MVO     R2,     @@top   ; Remember top row
2101        ADDI    #3,     R2      ; Move to bottom of window
2102
2103        CALL    SPAWN           ; Fork the line clearing as new task.
2104        DECLE   13, 3           ; Period == 6 ticks (10Hz), task #3
2105
2106    ;;--------------------------------------------------------------------;;
2107    ;;  NOTE:  THIS IS A NEW TASK HERE!!!  IT IS CALLED FOR EACH LINE!!!  ;;
2108    ;;  We do this to avoid having tasks pile up in the queue while we    ;;
2109    ;;  do our work.  Things such as score updates (most common) tend to  ;;
2110    ;;  pile up in the queue otherwise.                                   ;;
2111    ;;--------------------------------------------------------------------;;
2112
2113@@scan:
2114        MVII    #EDGEL, R1      ; Scan along left edge
2115        MVO     R2,     @@bot   ; Remember current y position
2116        CALL    GETPIXEL        ; Grab a pixel.
2117        MVI     @@bot,  R2      ; Restore row position
2118        MVI     @@top,  R1      ; Restore top of window
2119        CMPI    #7,     R0      ; Is this pixel color 7?
2120        BEQ     @@collapseok    ; It is: go collapse this row.
2121
2122        DECR    R2              ; Move up a row on the screen
2123        CMPR    R1,     R2      ; Are we out of our window?
2124        BGE     @@scan          ; If not, keep scanning
2125
2126@@done:
2127        CALL    SLEEP
2128        DECLE   31,     3       ; Sleep for 1/4th second after drop.
2129        PULR    R5
2130        B       PICKPIECE       ; Chain return through PICKPIECE
2131
2132@@collapseok:
2133
2134        ;; When we collapse a line, the top of our scanning window
2135        ;; actually moves down a row, and our scan position does NOT move
2136        ;; up a row.  So, look at the scan row, and increment the top
2137        ;; row value.
2138        INCR    R1              ; Move top down one row
2139        MVO     R1,     @@top   ; Store new top of window
2140
2141        ;; Increment our "cleared lines" count and queue up the "show lines"
2142        ;; task.
2143        MVI     LINES,  R1
2144        INCR    R1
2145        MVO     R1,     LINES
2146
2147        MVII    #UPDATESTATS, R0
2148        JSRD    R5,     QTASK
2149
2150        ;; We spawned as a one-shot task.  We retrigger only after we're
2151        ;; sure we might need it, so that we don't get over-triggered.
2152        ;; (We will get triggered up to 5 times, with the fifth trigger
2153        ;; doing the call to PICKPIECE.)
2154        MVII    #3,     R3
2155        CALL    RETRIGGERTASK
2156
2157@@nukeit:
2158        MVII    #1,     R1
2159        CALL    PLAY.sfxp       ; Trigger the collapse soundeffect.
2160        DECLE   FXBOMB
2161
2162@@nosfx:
2163        MVII    #EDGEL, R1      ; Start at left edge.
2164
2165@@xloop:
2166        MVI     HEIGHT, R2      ; Start at top of screen
2167        MVO     R1,     XSAVE   ; Save our current column
2168        CLRR    R4              ; Propogate a black pixel from top
2169
2170@@yloop:
2171        MVO     R4,     @@clr   ; Remember prev row's color
2172        MVO     R2,     YSAVE   ; Save our current row
2173        CALL    PIXCALC         ; Calculate mask/addr for this pixel
2174        MOVR    R1,     R4      ; Remember mask
2175        CALL    GETPIXELR       ; Get color to copy to next row
2176        MOVR    R4,     R1      ; Restore mask
2177        MOVR    R0,     R4      ; Remember color for next row
2178        MVI     @@clr,  R0      ; Restore previous rows color
2179        CALL    PUTPIXELR       ; Draw prev rows color in this row
2180        MVI     YSAVE,  R2      ; Restore row position
2181        MVI     XSAVE,  R1      ; Restore column position
2182        INCR    R2              ; Move down one row
2183        CMP     @@bot,  R2      ; Are we at the last row?
2184        BLE     @@yloop         ; No?  Keep looping.
2185
2186        INCR    R1              ; Move to next column
2187        CMPI    #EDGER, R1      ; Are we at the right edge yet?
2188        BLE     @@xloop         ; No?  Keep looping.
2189
2190        ; Decrement one from our well height here, so that music speed
2191        ; changes after we collapse a row, and our scan window changes as
2192        ; well for speed.
2193        MVI     HEIGHT, R1      ; Decrement the well height now.
2194        INCR    R1              ; (Note:  'HEIGHT' is actually
2195        MVO     R1,     HEIGHT  ; EDGEB - height.  Hence the INCR.)
2196
2197        PULR    PC              ; Return... we will be retriggered
2198                                ; if there are more lines to scan.
2199        ENDP
2200
2201
2202;;==========================================================================;;
2203;;  UPDATESCORE                                                             ;;
2204;;  Shows current score, by incrementally updating a displayed score value. ;;
2205;;==========================================================================;;
2206UPDATESCORE     PROC
2207        MVII    #PSCORL,R4      ; Point to player/displayed score
2208        MVI@    R4,     R2      ; Get lo 16 of player's actual score
2209        MVI@    R4,     R3      ; Get hi 16 of player's actual score
2210        MVI@    R4,     R0      ; Get lo 16 of displayed score
2211        MVI@    R4,     R1      ; Get hi 16 of displayed score
2212        CMPR    R3,     R1      ; Compare upper halves.
2213        BEQ     @@next          ; Displayed = Actual:  Test lower halves
2214        BNC     @@doincr        ; Displayed < Actual:  Do the increment
2215        BNEQ    @@uhoh          ; Displayed > Actual?  UHOH!
2216@@next:
2217        CMPR    R2,     R0      ; Compare lower halves.
2218        BEQ     @@return        ; Displayed = Actual:  Do nothing.
2219        BNC     @@doincr        ; Displayed < Actual:  Do the increment
2220                                ; Displayed > Actual?  UHOH!
2221@@uhoh:
2222        ; If we get here, we accidentally overshot the player's actual
2223        ; score somehow.  Compensate by setting the displayed score to the
2224        ; actual score.  :-P  (Note:  We also use this code to force a
2225        ; redisplay of the score on purpose by setting the displayed
2226        ; score to 0xFFFFFFFF, which is always higher than the player's
2227        ; score (or is it?  How good are YOU?)).
2228        MOVR    R2,     R0      ; Set lo half of displayed to actual
2229        MOVR    R3,     R1      ; Set hi half of displayed to actual
2230        B       @@disp          ; Display the fixed score.
2231
2232@@return:
2233        JR      R5
2234
2235@@doincr:
2236        ; First, find the difference between the displayed and actual
2237        ; scores.  This is a 32-bit subtract.
2238        SUBR    R0,     R2      ; Subtract the lo halves.
2239        ADCR    R3              ; Subtract the borrow from the hi half
2240        DECR    R3              ;    as a "not borrow"
2241        SUBR    R1,     R3      ; Subtract the hi halves
2242
2243        ; Divide the difference by 8, rounding upwards.
2244        ; This is effectively "num = (num + 7) / 8".  It should
2245        ; produce a non-zero number if num starts out non-zero.
2246        ADDI    #7,     R2      ; Add rounding factor
2247        ADCR    R3              ; ... and carry into upper half
2248
2249        ; Unsigned right shift by 3
2250        CLRC
2251        RRC     R3,     1       ; Shift the first bit.  Can't SARC (sign bit)
2252        RRC     R2,     1       ;
2253        SARC    R3,     2       ; SARC is safe now: sign bit is for sure 0
2254        RRC     R2,     2       ;
2255
2256;       TSTR    R2
2257;       BNEQ    @@addit
2258;       TSTR    R3
2259;       BNEQ    @@addit
2260;       HLT                     ; THIS SHOULD NEVER HAPPEN
2261;       INCR    R2
2262@@addit:
2263        ADDR    R2,     R0      ; Add the new increment's lo half
2264        ADCR    R1              ; Propogate the carry
2265        ADDR    R3,     R1      ; Add the new increment's hi half
2266@@disp:
2267        MVO     R0,     DSCORL  ; Store the updated display value (lo)
2268        MVO     R1,     DSCORH  ; Store the updated display value (hi)
2269
2270        MVI     SCRDIG, R2
2271        MVI     SCRCOL, R3
2272        MVI     SCRPOS, R4
2273        B       DEC32B          ; Display with leading zeros.
2274                                ; (Chained return)
2275
2276        ENDP
2277
2278;;==========================================================================;;
2279;;  UPDATESTATS                                                             ;;
2280;;  Updates game information such as # of lines, level, and drop speed.     ;;
2281;;==========================================================================;;
2282UPDATESTATS    PROC
2283        PSHR    R5
2284        MVI     SCRCOL, R3      ; Get score color
2285
2286        MVI     LINES,  R0      ; Get current number of lines
2287
2288        MVI     LEVEL,  R1      ; Get current level
2289        TSTR    R1
2290        BEQ     @@initial       ; First time through, force an update.
2291
2292        ADDR    R1,     R1      ; R1 = 2 * (level)
2293        MOVR    R1,     R2
2294        SLL     R2,     2       ; R2 = 8 * (level)
2295        ADDR    R2,     R1      ; R1 = 10 * (level)
2296
2297        CMPR    R1,     R0      ; See if we've cleared enough lines to go up
2298                                ; a level.
2299        BLT     @@samelevel     ; If not new level, just update line counter.
2300
2301        MVII    #3,     R1      ; Sound effect priority == 3
2302        CALL    PLAY.sfxp       ;
2303        DECLE   FXDING3         ; Ding to signify "Next Level".
2304       ;B       @@nextlevel
2305
2306@@nextlevel:
2307        MVI     LEVEL,  R2      ; Get current level number
2308        INCR    R2              ; Update our level number
2309        MVO     R2,     LEVEL   ; Store the new level number
2310
2311        B       @@skipinitial
2312
2313@@initial:
2314        MVI     SLEVEL, R2      ; Get starting level
2315        MVO     R2,     LEVEL   ; Store it to our level number
2316
2317@@skipinitial:
2318
2319        MVII    #20,    R4      ; See if level > 20.  If so, clamp to that
2320        CMPR    R4,     R2      ; for purposes of setting speed.
2321        BLE     @@speedok
2322        MOVR    R4,     R2
2323@@speedok:
2324        ADDI    #SPDTBL+1, R2   ; Point R2 into Speed Table
2325        MVI@    R2,     R2
2326        MVO     R2,     SPEED   ; Set piece speed from speed table
2327
2328        MVII    #LVLLOC + 20, R4
2329        MVII    #2,     R2
2330        MVI     LEVEL,  R0      ; Increment the level number
2331        CALL    DEC16A          ; Display the updated level number
2332
2333        MVI     LINES,  R0
2334@@samelevel:
2335        MVII    #LINLOC + 20, R4
2336        MVII    #2,     R2
2337        PULR    R5
2338        B       DEC16A
2339        ENDP
2340
2341;;==========================================================================;;
2342;;  SPDTBL                                                                  ;;
2343;;  Speed table.  Contains rates at which pieces drop for all the levels.   ;;
2344;;  Rates are all in "Ticks * 2".                                           ;;
2345;;==========================================================================;;
2346SPDTBL  PROC
2347        DECLE   160 + 0         ; Level  1
2348        DECLE   120 + 0         ; Level  2
2349        DECLE   100 + 0         ; Level  3
2350        DECLE    80 + 0         ; Level  4
2351        DECLE    60 + 0         ; Level  5
2352        DECLE    50 + 0         ; Level  6
2353        DECLE    40 + 0         ; Level  7
2354        DECLE    34 + 0         ; Level  8
2355        DECLE    28 + 0         ; Level  9
2356        DECLE    24 + 0         ; Level 10
2357        DECLE    20 + 0         ; Level 11
2358        DECLE    18 + 0         ; Level 12
2359        DECLE    16 + 0         ; Level 13
2360        DECLE    14 + 0         ; Level 14
2361        DECLE    12 + 0         ; Level 15
2362        DECLE    10 + 0         ; Level 16
2363        DECLE     8 + 0         ; Level 17
2364        DECLE     6 + 0         ; Level 18
2365        DECLE     4 + 0         ; Level 19
2366        DECLE     2 + 0         ; Level 20 and beyond
2367        ENDP
2368
2369;;==========================================================================;;
2370;;  MARQUEE                                                                 ;;
2371;;  Cycles colors on a string of text.                                      ;;
2372;;                                                                          ;;
2373;;  INPUTS:                                                                 ;;
2374;;  R2 -- Task instance data:                                               ;;
2375;;        Low byte of instance data gives screen offset.                    ;;
2376;;        High byte of instance data gives length.                          ;;
2377;;==========================================================================;;
2378MARQUEE PROC
2379
2380        MOVR    R2,     R3
2381        ANDI    #$FF,   R3      ; R3 == screen offset.
2382        XORR    R3,     R2
2383        SWAP    R2              ; R2 == length.
2384        ADDI    #$200,  R3      ; Convert screen offset into actual address.
2385
2386        MVI@    R3,     R0      ; Get first display word from string.
2387        MVII    #$7,    R1      ; Color mask for three LSBs.
2388        ANDR    R1,     R0      ; Get color from leading character in string.
2389        COMR    R1              ; Invert our mask, so we can replace colors.
2390@@loop:
2391        DECR    R0              ; Cycle color by decrementing
2392        BNEQ    @@ok            ; Make sure it didn't go to zero!
2393        MVII    #$7,    R0      ; If it did, set it back to 7.
2394@@ok:
2395        MVI@    R3,     R4      ; Get a character from the display.
2396        ANDR    R1,     R4      ; Mask away its color bits.
2397        ADDR    R0,     R4      ; Replace them with our new color bits.
2398        MVO@    R4,     R3      ; Write the modified character to the display.
2399        INCR    R3              ; Move to next character.
2400        DECR    R2              ; Loop until done.
2401        BNEQ    @@loop
2402
2403        JR      R5              ; Return.
2404        ENDP
2405
2406;;==========================================================================;;
2407;;  ANIDROP                                                                 ;;
2408;;  Color cycles CS1 and CS3 so that collapse lines "flash", or sets a      ;;
2409;;  solid color while piece is active.                                      ;;
2410;;==========================================================================;;
2411ANIDROP PROC
2412        MVI     CURPC.C, R2     ; Is there a current piece active?
2413        TSTR    R2
2414        BNEQ    @@active
2415        MVI     CS1,    R2      ; No? Just flash colors then.
2416        INCR    R2
2417@@active:
2418        MVO     R2,     CS1     ; Set up colorstack #1 and #3
2419        MVO     R2,     CS3
2420        JR      R5              ; Return.
2421        ENDP
2422
2423;;==========================================================================;;
2424;;  MUTETOGGLE                                                              ;;
2425;;  Toggles MUTE state                                                      ;;
2426;;==========================================================================;;
2427MUTETOGGLE PROC
2428        DIS
2429        MVI     MUTE,   R0      ; Get current mute status
2430        TSTR    R0              ; If zero, we're muted.
2431        BEQ     @@unmute
2432        ; Mute the music by zeroing its volume registers.
2433        CLRR    R0
2434        MVI     SNDSTRM+3,R1    ; Get current stream mask
2435        SLL     R1,     2       ; Move volume-register bits to top
2436
2437        SLLC    R1              ; If channel C's vol control is enabled ....
2438        BNC     @@notc
2439        MVO     R0,     PSG0+13 ; ... clear it
2440@@notc:
2441        SLLC    R1              ; If channel B's vol control is enabled ....
2442        BNC     @@notb
2443        MVO     R0,     PSG0+12 ; ... clear it
2444
2445@@notb:
2446        SLLC    R1              ; If channel A's vol control is enabled ....
2447        BNC     @@nota
2448        MVO     R0,     PSG0+11 ; ... clear it
2449
2450@@nota:
2451        INCR    PC              ; skip INCR R0.
2452@@unmute:
2453        INCR    R0              ; Un-mute the audio
2454
2455        MVO     R0,     MUTE    ; Store the new mute status
2456        EIS
2457        JR      R5
2458        ENDP
2459
2460
2461;;==========================================================================;;
2462;;  ROTPIECE_CW                                                             ;;
2463;;  Rotates a piece clockwise by updating the two LSBs of its piece         ;;
2464;;  index #.                                                                ;;
2465;;                                                                          ;;
2466;;  ROTPIECE_CCW                                                            ;;
2467;;  Rotates a piece counter clockwise by updating the two LSBs of its       ;;
2468;;  piece index #.                                                          ;;
2469;;                                                                          ;;
2470;;  INPUTS:                                                                 ;;
2471;;  R3 -- Piece number/rotation                                             ;;
2472;;  R5 -- Return address                                                    ;;
2473;;                                                                          ;;
2474;;  OUTPUTS                                                                 ;;
2475;;  R3 -- New piece/rotation                                                ;;
2476;;  R0 -- Trashed                                                           ;;
2477;;==========================================================================;;
2478ROTPIECE_CW:    PROC
2479        SETC                    ; Flag that this is a clockwise rotate
2480        INCR    PC              ; (skip the CLRC)
2481ROTPIECE_CCW:
2482        CLRC                    ; Flag that this is a counter-clockwise rotate
2483        MOVR    R3,     R0      ; Copy piece number/rotation to R0
2484        ADCR    PC              ; If counter-clockwise, complement R0,
2485        COMR    R0              ; ... else don't.
2486        SETC
2487        RLC     R0,     1
2488        ANDI    #3,     R0
2489        XORR    R0,     R3      ; Magic!
2490        JR      R5
2491        ENDP
2492
2493;;==========================================================================;;
2494;; GRAVITY                                                                  ;;
2495;; Makes the pieces fall!                                                   ;;
2496;;                                                                          ;;
2497;; MVPIECE                                                                  ;;
2498;; Moves/Rotates a piece around on the screen.                              ;;
2499;;                                                                          ;;
2500;; INPUTS:                                                                  ;;
2501;; R2 -- Event number:                                                      ;;
2502;;       13: Disc Left       Move piece left                                ;;
2503;;       14: Disc Right      Move piece right                               ;;
2504;;       15: Disc Down       Move piece down                                ;;
2505;;       16: Action Top      Rotate CCW                                     ;;
2506;;       17: Action Left     Rotate CW                                      ;;
2507;;       18: Action Right    Rotate CW                                      ;;
2508;;                                                                          ;;
2509;; THIS CODE SUCKS.                                                         ;;
2510;; (Although, it sucks less than the original code that I had here.)        ;;
2511;;==========================================================================;;
2512GRAVITY PROC
2513        MVII    #15,    R2      ; Event #15 is 'down'
2514        CLRR    R0              ; Flag that this is NOT a hand-ctrl input
2515        INCR    PC              ; (skip 'MOVR')
2516MVPIECE:
2517        MOVR    R2,     R0      ; Flag that this IS a hand-ctrl input
2518        MVO     R0,     WASHAND ; Store 'WASHAND' flag.
2519
2520        PSHR    R5              ; Save return address
2521        PSHR    R2              ; Save keypad #
2522
2523        MVII    #CURPC.C, R4
2524        CALL    GETPIECEREC     ; Load current piece's record into R0..R3
2525
2526        PULR    R4              ; Get keypad # into R4
2527
2528        TSTR    R0              ; Is there an active piece?
2529        BEQ     @@return        ; Do nothing if no piece is active.
2530
2531        MVII    #@@check, R5    ; Set up "return address"
2532
2533        MVO     R4,     WASDOWN ; Default "WAS DOWN" to "NOT"
2534
2535        SUBI    #13,    R4      ; Scale range down to 0..6, check for R4==13
2536        BMI     @@return        ; Bogus input R4 < 13 (should never happen)
2537        BNEQ    @@not_left      ; If taken, R4 != 13
2538
2539        DECR    R1              ; Move left
2540        JR      R5              ; Jump to '@@check'
2541
2542@@not_left:
2543        SUBI    #2,     R4      ; Check for R4==14 or R4==15
2544        BMI     @@is_right      ; Bogus input R4 == 13 (should never happen)
2545        BNEQ    @@not_down      ; If taken, R4 != 14
2546
2547        MVO     R4,     WASDOWN ; Set 'WASDOWN' to 'Yes, it was DOWN'.
2548        INCR    R2              ; Move down one
2549        JR      R5              ; Jump to '@@check'
2550
2551@@is_right:
2552        INCR    R1              ; Move right
2553        JR      R5              ; Jump to '@@check'
2554
2555@@not_down:
2556        DECR    R4              ; Check for R4==16 or R4 > 16
2557        ; Note: Both of these branches chain return through ROTPIECE_xxx
2558        BEQ     ROTPIECE_CCW    ; If taken, R4==16 (top action button)
2559        B       ROTPIECE_CW     ; If taken, R4>=17 (bottom action buttons)
2560
2561@@check:
2562        ;; All movement paths lead to here.  At this point, R0..R3
2563        ;; contain the proposed piece state (rotation, X, Y)
2564
2565        CALL    TESTPIECE       ; Is the piece placeable here?
2566        TSTR    R0
2567        BNEQ    @@cantmove      ; If we couldn't move the piece, leave.
2568
2569
2570        ; Prepare to move the piece.
2571        CALL    GETPIECECOLOR   ; Get the piece's color.
2572        MVO     R0,     CURPC.C ; Set piece's color in its record.
2573        MVO     R0,     CS1     ; Force color-stack to piece's color
2574        MVO     R0,     CS3     ; Force color-stack to piece's color
2575
2576        MVII    #TMPPC.X, R4
2577        CALL    PUTPIECEREC.NC  ; Save updated piece in temp storage
2578        CALL    GETPIECEREC     ; Restore original piece info
2579        CLRR    R0              ; Force color to 0 for erase.
2580        CALL    DRAWPIECE       ; Erase previous piece
2581
2582        MVII    #TMPPC.X, R4
2583        CALL    GETPIECEREC.NC  ; Restore updated piece info
2584        INCR    R4
2585        MVII    #7,     R0      ; Always draw in color 7 here!
2586        CALL    PUTPIECEREC.NC  ; Store it to the current piece info
2587
2588        CALL    DRAWPIECE       ; Draw the updated piece on the screen
2589
2590        ; Bip and increment the per-move total
2591        MVI     WASHAND, R0     ; Was this a hand-controller input?
2592        TSTR    R0
2593        BEQ     @@nothand       ; No... don't bip, and don't add to MSCORE
2594
2595        CALL    PLAY.sfx        ; Yes... BIP!
2596        DECLE   FXBIP
2597
2598        MVI     WASDOWN, R0     ; If this was a hand movement that pushed
2599        TSTR    R0              ; down, increment the MSCORE.
2600        BNEQ    @@notdown       ; No... don't increment MSCORE
2601
2602        MVI     MSCORE, R0
2603        ADDI    #MOVEINC,R0     ; Yes... increment MSCORE!
2604        MVO     R0,     MSCORE
2605@@notdown:
2606@@nothand:
2607
2608        PULR    R5              ; Chain Return via COMMITPXQ
2609        B       COMMITPXQ       ; Commit the pixels to be drawn to screen.
2610
2611@@cantmove:
2612        MVI     WASDOWN, R0     ; Was this a downward move?
2613        TSTR    R0
2614        BNEQ    @@return        ; No?  Just return then.
2615        PULR    R5
2616        B       PLACEPIECE      ; Yes?  Place the piece (w/ chained return)
2617
2618@@return:
2619        PULR    PC              ; Return
2620        ENDP
2621
2622;;==========================================================================;;
2623;;  NEXTSHOW                                                                ;;
2624;;  Shows the currently displayed piece in the next-piece box.              ;;
2625;;                                                                          ;;
2626;;  NEXTCLEAR                                                               ;;
2627;;  Clears the currently displayed piece in the next-piece box.             ;;
2628;;                                                                          ;;
2629;;  INPUTS:                                                                 ;;
2630;;  Location 'NXTPC' holds current next piece.                              ;;
2631;;  R5 -- return address.                                                   ;;
2632;;                                                                          ;;
2633;;  OUTPUTS:                                                                ;;
2634;;  R0 -- saved                                                             ;;
2635;;  R1 -- NXTPCX                                                            ;;
2636;;  R2 -- NXTPCY                                                            ;;
2637;;  R3 -- Next piece's piece number/rotation                                ;;
2638;;  R4, R5 -- trashed                                                       ;;
2639;;==========================================================================;;
2640NEXTSHOW PROC
2641        MVO     PC,     DIDNXT  ; Set the "We showed next piece" flag.
2642                                ; (Note: PC guaranteed to be non-zero)
2643        SETC                    ; Set the "This is NEXTSHOW" flag.
2644        INCR    PC              ; Skip the CLRC.
2645NEXTCLEAR:
2646        CLRC                    ; Set the "This is NEXTCLEAR" flag.
2647        PSHR    R5              ; Save our return address.
2648        PSHR    R0              ; Save R0.
2649        MVI     NXTPC,  R3      ; Get the next piece's piece number/rotation
2650        BNC     @@clear         ; If carry cleared, set color to 0.
2651        CALL    GETPIECECOLOR   ; Otherwise, get the piece's color.
2652        INCR    PC              ; Skip the CLRR.
2653@@clear:
2654        CLRR    R0              ; Clear the piece's color.
2655        MVII    #NXTPCX, R1     ; Position ourself in the next-piece box.
2656        MVII    #NXTPCY, R2
2657        CALL    DRAWPIECE
2658
2659        PULR    R0              ; Restore R0.
2660        PULR    R5              ; Chain return via COMMITPXQ
2661        B       COMMITPXQ       ; Commit the pixels for display immediately.
2662        ENDP
2663
2664
2665;;==========================================================================;;
2666;;  NEXTTOGGLE                                                              ;;
2667;;  Toggles whether next-piece is shown.                                    ;;
2668;;==========================================================================;;
2669NEXTTOGGLE      PROC
2670        MVI     SHOWNXT, R1     ; Are we currently showing next piece?
2671        TSTR    R1
2672        BEQ     @@turnon        ; No... then start showing it.
2673@@turnoff:
2674        CLRR    R1              ; Yes... turn it off!
2675        MVO     R1,     SHOWNXT
2676        B       NEXTCLEAR       ; Chain return through NEXTCLEAR
2677@@turnon:
2678        INCR    R1
2679        MVO     R1,     SHOWNXT
2680        B       NEXTSHOW        ; Chain return through NEXTSET
2681        ENDP
2682
2683
2684;;==========================================================================;;
2685;;  PLACEPIECE                                                              ;;
2686;;  Make piece permanent where it is at.                                    ;;
2687;;==========================================================================;;
2688PLACEPIECE      PROC
2689        DIS
2690        PSHR    R5
2691        MVII    #CURPC.C, R4    ; Write to "current piece" record.
2692        CALL    GETPIECEREC     ; Make this the current piece.
2693        EIS
2694
2695        TSTR    R0              ; Make sure we actually had an active piece
2696        BNEQ    @@active
2697        PULR    PC              ; Not active?  Just return!
2698@@active:
2699
2700        CALL    DRAWPIECE       ; Draw it.
2701        CALL    COMMITPXQ       ; Commit the pixels.
2702        CALL    WAIT            ; Wait one tick, so pixels are committed.
2703        DECLE   1
2704
2705        CLRR    R5              ; Mark our current piece as inactive.
2706        MVO     R5,     CURPC.C ; It will get reactivated by DOSCORE.
2707                                ; We do this after the 'wait' so that we're
2708                                ; sure our queued pixels are drawn.
2709
2710        CMPI    #EDGEB, R4      ; Is this piece off-screen?
2711        BLE     @@notgameover   ; No... keep going
2712        CALL    NEXTCLEAR
2713        PULR    R5
2714        CLRR    R0
2715        MVO     R0,     HEIGHT
2716        B       SCHEDEXIT       ; Yes... game over!
2717
2718@@notgameover:
2719        CMP     HEIGHT, R4      ; Did this piece form the new top-of-well?
2720        BGE     @@nottop
2721        MVO     R4,     HEIGHT
2722
2723@@nottop:
2724        MVI     DIDNXT, R0      ; Was 'next piece' displayed during this
2725        TSTR    R0              ; piece?  If not, double MSCORE
2726        BNEQ    @@didnext
2727        MVI     MSCORE, R0
2728        ADDR    R0,     R0      ; double the MSCORE
2729        MVO     R0,     MSCORE
2730@@didnext:
2731
2732        PSHR    R4
2733        CALL    PLAY.sfx
2734        DECLE   FXBYUMP
2735
2736        PULR    R1              ; Make row # the task-instance data
2737        MVII    #DOSCORE, R0    ; Queue the scoring task.
2738        PULR    R5
2739        JD      QTASK           ; Queue and chain return
2740        ENDP
2741
2742
2743;;==========================================================================;;
2744;;  TITLESCREEN                                                             ;;
2745;;  Draws the title screen and waits for user input.                        ;;
2746;;==========================================================================;;
2747TITLESCREEN     PROC
2748        PSHR    R5
2749
2750        CLRR    R0
2751        MVO     R0,     CS0             ; Set color stack to BLACK
2752        MVO     R0,     CS1
2753        MVO     R0,     CS2
2754        MVO     R0,     CS3
2755        MVO     R0,     CB              ; Set display border to BLACK
2756
2757        MVII    #$F0,   R1              ; Set entire display to BLACK
2758        MVII    #$200,  R4
2759        CALL    FILLMEM
2760
2761        CALL    DRAWSTRING3
2762        DECLE   6, $200+0*20+3
2763        BYTE    "Joseph Zbiciak",0
2764
2765        CALL    DRAWSTRING4
2766        DECLE   $200+1*20+6
2767        BYTE    "presents",0
2768
2769        CALL    DRAWSTRING5
2770        DECLE   TITLE + 1               ; Point to the title
2771        DECLE   $807                    ; In GRAM font.
2772        DECLE   $200+5*20+10-3          ; Point R4 to location on screen
2773
2774        CALL    DRAWSTRING3
2775        DECLE   1, $200+10*20+2
2776        DECLE   "Copyright ", COPYR, " 2000", 0
2777        MVO     R1,     SCRCOL
2778
2779        ; Animate the '2000' in Copyright 2000.
2780        SUBI    #4,     R4              ; Back up to the '2000'
2781        MVO     R4,     SCRPOS
2782        MVII    #6,     R4              ; Suppress 6 digits
2783        MVO     R4,     SCRDIG
2784        MVII    #2000,  R0
2785        CLRR    R1
2786        MVII    #PSCORL,R4
2787        MVO@    R0,     R4              ; PSCORL = 2000
2788        MVO@    R1,     R4              ; PSCORH = 0 --> PSCOR = 2000
2789        SUBI    #25,    R0
2790        MVO@    R0,     R4              ; DSCORL = 1975
2791        MVO@    R1,     R4              ; DSCORH = 0 --> DSCOR = 1975
2792
2793        CALL    STARTTASK
2794        DECLE   2                       ; Task # 2
2795        DECLE   UPDATESCORE             ; Update displayed date
2796        DECLE   4,      4               ; 15Hz
2797
2798        ; Start a Marquee task on the 4-TRIS title string.
2799        MVII    #107 + 256*6, R2        ; Length 6, Offset 107
2800        CALL    STARTTASK
2801        DECLE   0                       ; Task #0
2802        DECLE   MARQUEE                 ; MARQUEE task
2803        DECLE   10,     10              ; Period: every 5 ticks
2804
2805        ; Make all three tasks active.
2806        MVII    #3,     R3
2807        MVO     R3,     TSKACT
2808
2809        ; Start off title screen music.
2810        CALL    PLAY.mus
2811        DECLE   M_TITLE
2812
2813        ; Set our keypad dispatch
2814        CLRR    R0
2815        MVO     R0,     HANDFN
2816
2817        PULR    PC
2818        ENDP
2819
2820;;==========================================================================;;
2821;;  GAMEHAND                                                                ;;
2822;;  Table of keypad dispatches for INGAME                                   ;;
2823;;==========================================================================;;
2824GAMEHAND        PROC
2825@@kp0:  DECLE   STUB            ;  0: KP0
2826@@kp1:  DECLE   STUB            ;  1: KP1
2827@@kp2:  DECLE   STUB            ;  2: KP2
2828@@kp3:  DECLE   STUB            ;  3: KP3
2829@@kp4:  DECLE   NEXTTOGGLE      ;  4: KP4
2830@@kp5:  DECLE   STUB            ;  5: KP5
2831@@kp6:  DECLE   NEXTTOGGLE      ;  6: KP6
2832@@kp7:  DECLE   MUTETOGGLE      ;  7: KP7
2833@@kp8:  DECLE   STUB            ;  8: KP8
2834@@kp9:  DECLE   MUTETOGGLE      ;  9: KP9
2835@@kpC:  DECLE   NEXTTOGGLE      ; 10: KPC
2836@@kpE:  DECLE   STUB            ; 11: KPE
2837@@dsU:  DECLE   STUB            ; 12: Disc Up
2838@@dsL:  DECLE   MVPIECE         ; 13: Disc Left
2839@@dsR:  DECLE   MVPIECE         ; 14: Disc Right
2840@@dsD:  DECLE   MVPIECE         ; 15: Disc Down
2841@@acT:  DECLE   MVPIECE         ; 16: Action Top
2842@@acL:  DECLE   MVPIECE         ; 17: Action Left
2843@@acR:  DECLE   MVPIECE         ; 18: Action Right
2844        ENDP
2845
2846;;==========================================================================;;
2847;;  INGAME                                                                  ;;
2848;;  Sets up the display for the game by clearing the screen to "colored     ;;
2849;;  squares" mode, etc.  Also sets up some initial tasks for starting the   ;;
2850;;  game.                                                                   ;;
2851;;==========================================================================;;
2852INGAME: PROC
2853        PSHR    R5
2854        ; Clear screen to colored squares mode
2855        MVII    #$10,   R0
2856        SWAP    R0              ; $1000 corresponds to four black sq
2857        MVII    #$200,  R4      ; Fill memory starting at $200
2858        MVII    #$F0,   R1      ; ... through $2EF (all BACKTAB)
2859        CALL    FILLMEM
2860
2861        ; Set up color stack advance bits to left and right of the "well".
2862        ; Also, draw well edges in yellow.
2863        MVII    #$206, R4
2864        MVII    #$22F8, R0      ; Black w/ "advance" bit, no colsq
2865        MVII    #11,    R1      ; 11 rows
2866        MVII    #$1186, R2      ; Yellow in blocks 0, 2
2867        MVII    #$3430, R3      ; Yellow in blocks 1, 3
2868@@csloop:
2869        MVO@    R0,     R4
2870        MVO@    R2,     R4
2871        ADDI    #4,     R4
2872        MVO@    R3,     R4
2873        MVO@    R0,     R4
2874        ADDI    #12,    R4
2875        DECR    R1
2876        BNEQ    @@csloop
2877
2878        ; Draw bottom of the "well"
2879        MVII    #EDGEL, R1
2880        MVII    #EDGEB, R2
2881        MVII    #6,     R0
2882@@botloop:
2883        CALL    PUTPIXELS
2884        INCR    R1
2885        CMPI    #EDGER, R1
2886        BLE     @@botloop
2887
2888
2889        ; Draw the 'next piece' box
2890        MVII    #NXTPCX-3, R1
2891        MVII    #NXTPCY-3, R2
2892        MVII    #6,     R0
2893@@npxloop:
2894        CALL    PUTPIXELS
2895        ADDI    #7,     R2
2896        CALL    PUTPIXELS
2897        SUBI    #7,     R2
2898        INCR    R1
2899        CMPI    #NXTPCX+3, R1
2900        BLE     @@npxloop
2901
2902        INCR    R2
2903        DECR    R1
2904@@npyloop:
2905        CALL    PUTPIXELS
2906        SUBI    #6,     R1
2907        CALL    PUTPIXELS
2908        ADDI    #6,     R1
2909        INCR    R2
2910        CMPI    #NXTPCY+4, R2
2911        BLE     @@npyloop
2912
2913        ; draw 'Lines', 'Level' and 'Next' onscreen in cyan
2914        MVII    #$1809, R1
2915        MVII    #LVLLOC, R4
2916        MVII    #8,     R2
2917        CALL    TLA             ; Draw 'Level'
2918        ADDI    #LINLOC - LVLLOC - 3, R4
2919        CALL    TLA             ; Draw 'Lines'
2920        SUBI    #3 + LINLOC - NXTLOC, R4
2921        CALL    TLA             ; Draw 'Next'
2922
2923        ; Spool up the random number generator a bit
2924        MVII    #$FF,   R2
2925        CALL    NEXTRANDX
2926
2927        ; Pick a starting piece and a starting 'next piece'
2928        CLRR    R0
2929        MVO     R0,     NXTPC
2930        CALL    PICKPIECE       ; Pick a random piece (starting piece)
2931        MVII    #$FF,   R2
2932        CALL    NEXTRANDX       ; Spin the random number generator some more.
2933        CALL    PICKPIECE       ; Pick a random piece (next piece)
2934
2935        ; Set PREVBL routine to animate CS1 and CS3
2936        MVII    #ANIDROP, R0
2937        MVO     R0,     PREVBL
2938
2939        ; Task 0:  Step blocks down the screen / score drops.
2940        ; Task 1:  Key/disc/pad repeat manager.
2941        ; Task 2:  Score display update.
2942        ; Task 3:  Sleep task.
2943
2944        ; Set up piece drop task
2945        CALL    STARTTASK
2946        DECLE   0
2947        DECLE   GRAVITY
2948        DECLE   120,    120     ; 1Hz
2949
2950        ; Set up the score update.
2951        CLRR    R2              ; Do not suppress any digits.
2952        MVII    #$1802, R3      ; Display in orange, using GRAM font
2953        MVII    #20*11 + 5, R4  ; Put at bottom of screen in middle
2954        MVO     R2,     SCRDIG
2955        MVO     R3,     SCRCOL
2956        MVO     R4,     SCRPOS
2957        CALL    STARTTASK
2958        DECLE   2               ; Task # 2
2959        DECLE   UPDATESCORE     ; Update displayed score
2960        DECLE   4,      4       ; 30Hz
2961
2962
2963        ; Allow up to four active tasks.  (Task 3 is used for SLEEP).
2964        MVII    #4,     R3
2965        MVO     R3,     TSKACT
2966
2967        ; Set up color stack.  Entries 0 and 2 are black, 1 and 3 are white.
2968        ; Entries 1 and 3 will be "cycled" during a drop animation sequence.
2969        MVII    #CS0,   R4
2970        CLRR    R0
2971        MVO@    R0,     R4
2972        MVO@    R0,     R4
2973        MVO@    R0,     R4
2974        MVO@    R0,     R4
2975        MVII    #PSCORL,R4      ; (interruptible, for STIC)
2976
2977        ; Clear our total lines count and level number.  (UPDATESTATS
2978        ; will move the level number to 1 for us.)
2979        MVO     R0,     LINES   ; Lines cleared so far
2980        MVO     R0,     LEVEL   ; Clear level number to force an update
2981
2982
2983        ; Clear our starting score
2984        MVO@    R0,     R4      ; PSCORL
2985        MVO@    R0,     R4      ; PSCORH
2986        DECR    R0              ; Set to -1 to force a redisplay.
2987        MVO@    R0,     R4      ; DSCORL
2988        MVO@    R0,     R4      ; DSCORH
2989
2990        ; Clear the well height.
2991        MVII    #EDGEB, R0
2992        MVO     R0,     HEIGHT
2993
2994        ; Set our keypad dispatch
2995        MVII    #GAMEHAND, R0
2996        MVO     R0,     HANDFN
2997
2998        ; Play some background music
2999        CALL    PLAY.mus
3000        DECLE   M_GAME
3001
3002        ; Chain through "UPDATESTATS"
3003        PULR    R5
3004        B       UPDATESTATS
3005        ENDP
3006
3007;;==========================================================================;;
3008;;  TLA                                                                     ;;
3009;;  'Three Letter Acronym'                                                  ;;
3010;;  Used to draw the labels over 'Level', 'Lines' and 'Next' on the screen. ;;
3011;;                                                                          ;;
3012;;  INPUTS:                                                                 ;;
3013;;  R1 -- Screen format word, including starting GRAM/GROM card #.          ;;
3014;;  R2 -- Index value for stepping between GRAM/GROM card #'s.              ;;
3015;;  R4 -- Display pointer.                                                  ;;
3016;;  R5 -- Return address.                                                   ;;
3017;;                                                                          ;;
3018;;  OUTPUTS:                                                                ;;
3019;;  R1 -- Screen format word, with GRAM/GROM card # advanced by 3.          ;;
3020;;  R4 -- Display pointer, advanced by 3                                    ;;
3021;;  R2, R5 untouched.                                                       ;;
3022;;==========================================================================;;
3023TLA     PROC
3024        MVO@    R1,     R4
3025        ADDR    R2,     R1
3026        MVO@    R1,     R4
3027        ADDR    R2,     R1
3028        MVO@    R1,     R4
3029        ADDR    R2,     R1
3030        JR      R5
3031        ENDP
3032
3033
3034;;==========================================================================;;
3035;;  TESTHAND                                                                ;;
3036;;  Table of keypad dispatches for SOUNDTEST                                ;;
3037;;==========================================================================;;
3038TESTHAND        PROC
3039@@kp0:  DECLE   MUTETOGGLE      ; KP0
3040@@kp1:  DECLE   SOUNDTEST.sfx   ; KP1
3041@@kp2:  DECLE   SOUNDTEST.sfx   ; KP2
3042@@kp3:  DECLE   SOUNDTEST.sfx   ; KP3
3043@@kp4:  DECLE   SOUNDTEST.sfx   ; KP4
3044@@kp5:  DECLE   SOUNDTEST.sfx   ; KP5
3045@@kp6:  DECLE   SOUNDTEST.sfx   ; KP6
3046@@kp7:  DECLE   SOUNDTEST.mus   ; KP7
3047@@kp8:  DECLE   SOUNDTEST.mus   ; KP8
3048@@kp9:  DECLE   SOUNDTEST.mus   ; KP9
3049@@kpC:  DECLE   SOUNDTEST.soff  ; KPC
3050@@kpE:  DECLE   SOUNDTEST.moff  ; KPE
3051@@dsU:  DECLE   SCHEDEXIT       ; Disc Up
3052@@dsL:  DECLE   SCHEDEXIT       ; Disc Left
3053@@dsR:  DECLE   SCHEDEXIT       ; Disc Right
3054@@dsD:  DECLE   SCHEDEXIT       ; Disc Down
3055@@acT:  DECLE   SOUNDTEST.spd   ; Action Top
3056@@acL:  DECLE   SOUNDTEST.spd   ; Action Left
3057@@acR:  DECLE   SOUNDTEST.spd   ; Action Right
3058        ENDP
3059
3060
3061
3062;;==========================================================================;;
3063;;  SOUNDTEST                                                               ;;
3064;;  Extremely simple Sound Test routine                                     ;;
3065;;==========================================================================;;
3066SOUNDTEST       PROC
3067        PSHR    R5
3068
3069        ; Set our keypad dispatch
3070        MVII    #TESTHAND, R0
3071        MVO     R0,     HANDFN
3072
3073        ; Force music and sound effects off
3074        CALL    @@soff                  ; Force sound effects off.
3075        CALL    @@moff                  ; Force music off.
3076
3077        ; Display instructions
3078        MVII    #$F0,   R1              ; Set entire display to BLACK
3079        MVII    #$200,  R4
3080        CALL    FILLZERO
3081
3082        MVII    #$1807, R1              ; Pastel Purple, GRAM font
3083        MVII    #$200 + 5, R4
3084
3085        CALL    DRAWSTRING2
3086        BYTE    "SOUND TEST",0
3087
3088        CALL    DRAWSTRING3
3089        DECLE   6, $200 + 40            ; Yellow, top of screen
3090        ;        01234567890123456789
3091        BYTE    "1 - 6:  Sound Effect"
3092        BYTE    "7,8,9:  Bkgnd Music "
3093        BYTE    "0:      Toggle Mute "
3094        BYTE    "Clear:  Stop SFX    "
3095        BYTE    "Enter:  Stop Music  "
3096        BYTE    "Action: Music speed "
3097        BYTE    "Disc:   Exit", 0
3098
3099        CALL    DRAWSTRING3
3100        DECLE   1, $2F0 - 40            ; Blue, bottom of screen.
3101
3102        ;        01234567890123456789
3103        BYTE    "intvnut AT gmail.com"
3104        BYTE    "       v1.12",0
3105
3106        ; Start a Marquee task on the Sound Test string.
3107        MVII    #5 + 256*10, R2         ; Length 10, Offset 5
3108        CALL    STARTTASK
3109        DECLE   0                       ; Task #0
3110        DECLE   MARQUEE                 ; MARQUEE task
3111        DECLE   14,     14              ; Period: every 7 ticks
3112
3113        ; Make maquee task active.
3114        MVII    #1,     R3
3115        MVO     R3,     TSKACT
3116
3117        PULR    PC                      ; Return
3118
3119@@soff:
3120        MVII    #$FF,   R1
3121        MVII    #FX_OFF, R4
3122        B       PLAY.sfxn               ; Force sound effects off
3123
3124@@moff:
3125        MVII    #FX_OFF, R4
3126        B       PLAY.musn               ; Force music off
3127
3128@@sfx:  ; Play a sound effect
3129        DECR    R2
3130        ADDR    R2,     R2
3131        MOVR    R2,     R4
3132        B       PLAY.sfxn
3133
3134@@mus:  ; Play new music
3135        DECR    R2
3136        ADDR    R2,     R2
3137        MOVR    R2,     R4
3138        B       PLAY.musn
3139
3140@@spd:  ; Toggle music speed
3141        MVII    #EDGEB, R0
3142        XOR     HEIGHT, R0
3143        MVO     R0,     HEIGHT
3144        JR      R5
3145
3146        ENDP
3147
3148;;==========================================================================;;
3149;;  GAMEOVER                                                                ;;
3150;;  Guess what?  It's game over time!!                                      ;;
3151;;==========================================================================;;
3152GAMEOVER        PROC
3153        PSHR    R5
3154
3155        ; Set our keypad dispatch
3156        CLRR    R0
3157        MVO     R0,     HANDFN
3158
3159        ; Stop the current game music & sfx, and play a loud BOOM!
3160        MVO     R0,     SNDSTRM+7   ; Make SFX enable mask go to 0
3161        DECR    R0
3162        MVO     R0,     MUTE        ; Force music to be un-muted
3163        MVO     R0,     DSCORL      ; set 'displayed score' to 0xFFFFFFFF
3164        MVO     R0,     DSCORH      ; (moved from below to save some code)
3165        CALL    PLAY.mus            ; Play loud BOOM on music channel
3166        DECLE   FXBOOM2
3167
3168        ; Force the displayed score to be fully updated.
3169        CALL    UPDATESCORE
3170
3171        ; Wait for "BOOM2" to finish
3172        MVII    #2,     R0
3173@@spin:
3174        CMP     SNDSTRM, R0
3175        BLE     @@spin
3176
3177        ; Clear the well!
3178        CALL    CLEARWELL
3179        CALL    WAIT
3180        DECLE   20                      ; Wait 1/3rd second afterwards
3181
3182        ; Put up the GAME OVER string.
3183        CALL    DRAWSTRING3
3184        DECLE   $7
3185        DECLE   $200 + 20*5 + 5
3186        BYTE    "Game Over!",0
3187
3188        ; Start a Marquee task on the Game Over string.
3189        MVII    #105 + 256*10, R2       ; Length 10, Offset 105
3190        CALL    STARTTASK
3191        DECLE   0                       ; Task #0
3192        DECLE   MARQUEE                 ; MARQUEE task
3193        DECLE   10,     10              ; Period: every 5 ticks
3194
3195        MVII    #1,     R0
3196        MVO     R0,     TSKACT
3197
3198        ; Play the Game Over music
3199        CALL    PLAY.mus
3200        DECLE   M_OVER
3201
3202        PULR    PC
3203
3204        ENDP
3205
3206;;==========================================================================;;
3207;;  SETLEVEL                                                                ;;
3208;;  Receives a keypad number and sets up our starting score and level       ;;
3209;;  number appropriately.                                                   ;;
3210;;                                                                          ;;
3211;;  INPUT:                                                                  ;;
3212;;  R2 -- Key/disc/action button number (0..18)                             ;;
3213;;                                                                          ;;
3214;;  OUTPUT:                                                                 ;;
3215;;  SLEVEL set up appropriately.                                            ;;
3216;;==========================================================================;;
3217SETLEVEL        PROC
3218        TSTR    R2              ; See if it's zero
3219        BEQ     @@got_level     ; Yes... Set it anyway.  Triggers 'sound test'
3220        CMPI    #10,    R2      ; See if it's > 10
3221        BLE     @@got_level     ; No, use this as the level number
3222
3223@@default_lev:
3224        MVII    #4,     R2      ; Default is to start at level #4
3225
3226@@got_level:
3227        MVO     R2,     SLEVEL  ; Store starting level number
3228
3229        B       SCHEDEXIT       ; Return via SCHEDEXIT
3230        ENDP
3231
3232;;==========================================================================;;
3233;;  HANDTASK                                                                ;;
3234;;  Handles a keypad press.                                                 ;;
3235;;                                                                          ;;
3236;;  INPUT:                                                                  ;;
3237;;  R2 -- Keypad key number (0..11), disc dir (12..15), or action           ;;
3238;;        button (16..18)                                                   ;;
3239;;  R5 -- Return address                                                    ;;
3240;;                                                                          ;;
3241;;  OUTPUT:                                                                 ;;
3242;;  Jumps to appropriate function.  R2 is still set to the keypad number.   ;;
3243;;==========================================================================;;
3244HANDTASK         PROC
3245        MVI     HANDFN, R4
3246        TSTR    R4
3247        BEQ     SETLEVEL
3248        ADDR    R2,     R4
3249        MVI@    R4,     PC
3250        ENDP
3251
3252;;==========================================================================;;
3253;;  KEYREPEAT                                                               ;;
3254;;  Key repeat task, scheduled on task #1.  De-schedules self if nothing    ;;
3255;;  is currently pressed.  Works by clearing LHAND/LHKPD.                   ;;
3256;;==========================================================================;;
3257KEYREPEAT       PROC
3258        MVII    #1,     R3      ; Prepare to disable task if needed
3259        MVI     LHAND,  R0      ; See if something was pressed
3260        MVO     R0,     REPEAT  ; Update "repeating" flag.
3261        TSTR    R0              ;
3262        BEQ     STOPTASK        ; No? Disable task and chain the return
3263        CLRR    R0              ; Yes? Clear last-pressed info.
3264        MVO     R0,     LHAND
3265        MVO     R0,     LHKPD
3266        JR      R5              ; Return.
3267        ENDP
3268
3269;;==========================================================================;;
3270;;  SCANHAND                                                                ;;
3271;;  Scan the hand controllers and schedule tasks based on the input         ;;
3272;;  triggers we see.  Hand controller debouncing is done here too, which    ;;
3273;;  makes scanning controllers somewhat time-consuming.  (I need to         ;;
3274;;  eventually experiment with debounce parameters on real hardware if I    ;;
3275;;  plan to make this a cartridge.)                                         ;;
3276;;                                                                          ;;
3277;;  Hand controller docs:                                                   ;;
3278;;   Bit 0:  Down,   Row 0 keys --------- 1    2    3                       ;;
3279;;   Bit 1:  Right,  Row 1 keys --------- 4    5    6                       ;;
3280;;   Bit 2:  Up,     Row 2 keys --------- 7    8    9                       ;;
3281;;   Bit 3:  Left,   Row 3 keys --------- C    0    E                       ;;
3282;;   Bit 4:  Corner                       |    |    |                       ;;
3283;;   Bit 5:  T/L     Col 2 keys --------- | -- | ---+                       ;;
3284;;   Bit 6:  L/R     Col 1 keys --------- | ---+                            ;;
3285;;   Bit 7:  T/R     Col 0 keys ----------+                                 ;;
3286;;==========================================================================;;
3287        ;        E    C    9    8    7    6    5    4    3    2    1    0
3288PADTBL: BYTE    $28, $88, $24, $44, $84, $22, $42, $82, $21, $41, $81, $48
3289        ;       rgt  lft  top
3290ACTTBL: BYTE    $C0, $60, $A0
3291        ;       bot  rgt  lft  top
3292DSCTBL: BYTE    $01, $02, $08, $04
3293
3294SCANHAND        PROC
3295DBOUT   EQU     5               ; Debounce outer loop
3296DBIN    EQU     120             ; Debounce inner loop
3297DB80    EQU     (80*DBIN)/100   ; 80% of inner loop iters
3298
3299@@startover:
3300        CLRR    R2
3301        MVO     R2,     PAUSED  ; Clear 'we paused' flag
3302
3303        MVII    #DBIN,  R2
3304@@dbnothing:
3305        MVI     CTRL0,  R0
3306        AND     CTRL1,  R0
3307        XORI    #$FF,   R0
3308
3309        ; See if it is the same as our last hand-controller input.
3310        ; If so, just ignore it.
3311        CMP     LHAND,  R0
3312        BEQ     @@nothing
3313
3314        MOVR    R0,     R1      ; Lets see if it's anything at all
3315        BNEQ    @@input         ; If it comes up non-empty, process it.
3316
3317        DECR    R2              ; If it comes up empty, debounce before
3318        BNEQ    @@dbnothing     ; saying it's just nothing.
3319        B       @@noinput
3320@@input:
3321
3322        ; If last input was keypad, ignore this if they share any bits.
3323        MVI     LHKPD,  R0
3324        ANDR    R1,     R0
3325        BNEQ    @@nothing
3326
3327        ; Debounce the input.  If we read the same value >80% of the
3328        ; iterations of the debounce inner loop, we say it's good.
3329        ; Otherwise we iterate the outer loop.
3330@@debounce:
3331        MVII    #DBOUT, R3      ; Outer loop max count
3332@@oloop:
3333        CLRR    R2              ; Match count
3334        MVII    #DBIN,  R0      ; Inner loop count
3335
3336        MVI     CTRL0,  R1
3337        AND     CTRL1,  R1
3338@@iloop:
3339        MVI     CTRL0,  R4
3340        AND     CTRL1,  R4
3341        CMPR    R4,     R1
3342        BNEQ    @@notsame       ; Yes... don't count it
3343        INCR    R2              ; No... do count it.
3344@@notsame:
3345        DECR    R0              ; Count down inner loop
3346        BNEQ    @@iloop
3347        XORI    #$FF,   R1
3348
3349        CMPI    #DB80,  R2      ; Was it same >80% of inner loop?
3350        BGE     @@ok            ; Yes, keep it.
3351
3352        DECR    R3              ; No, iterate outer loop
3353        BNEQ    @@oloop
3354        ; If we fall through here, just parse whatever we read last.
3355        ; Somebody really needs to clean their controllers!
3356@@ok:
3357        MVI     PAUSED, R3      ; If we paused during debounce, start this
3358        TSTR    R3              ; whole thing over.
3359        BNEQ    @@startover
3360
3361        ; Parse controller info in R1.
3362        ; If bit 4 is set, assume this is NOT a keypad press.
3363        MOVR    R1,     R0
3364        ANDI    #$10,   R0
3365        BNEQ    @@nopad
3366
3367        ; See if it EXACTLY matches a keypad key
3368        MVII    #PADTBL,R4
3369        MVII    #11,    R0
3370@@kloop:
3371        CMP@    R4,     R1
3372        BEQ     @@gotpad
3373        DECR    R0
3374        BGE     @@kloop
3375        B       @@nopad
3376
3377@@gotpad:
3378        MVO     R1,     LHAND   ; Set last-hand value
3379
3380        ; If we _think_ we got a keypad input, make DAMN SURE by debouncing
3381        ; AGAIN with 3x the debounce inner loop.
3382        MVII    #3*DBIN, R3    ; Inner loop count
3383@@kpdloop:
3384        MVI     CTRL0,  R4
3385        AND     CTRL1,  R4
3386        XORI    #$FF,   R4
3387        CMPR    R4,     R1
3388        BNEQ    @@debounce      ; Miscompare?  Hell with this!
3389@@kpnotsame:
3390        DECR    R3              ; Count down inner loop
3391        BNEQ    @@kpdloop
3392
3393        MVO     R1,     LHKPD   ; Record that last input was keypad.
3394
3395        MOVR    R0,     R1
3396        B       @@dispatch      ; Keypad keys don't repeat.
3397
3398@@notrepeating:
3399        PSHR    R5
3400        CALL    STARTTASK       ; Make the keypress repeat
3401        DECLE   1               ; Task #1
3402        DECLE   KEYREPEAT       ; KEYREPEAT task
3403        DECLE   30,     8       ; First repeat 250ms, rest @ 15Hz
3404        PULR    R5
3405
3406@@repeating:
3407        PULR    R1
3408@@dispatch:
3409        MVII    #HANDTASK, R0   ; Queue the HANDTASK to process key
3410
3411        JD      QTASK
3412
3413@@nopad:
3414        CLRR    R0
3415        MVO     R0,     LHKPD
3416
3417        MOVR    R1,     R0      ; Copy input to R0 to decode action buttons.
3418        ANDI    #$E0,   R0
3419        BEQ     @@noaction      ; Don't decode action btns if there are none
3420
3421        ; Action buttons shouldn't repeat like the disc, so avoid doing
3422        ; action buttons if these bits match bits in REPEAT.
3423@@hmm
3424        MVI     REPEAT, R2
3425        ANDI    #$E0,   R2
3426        CMPR    R2,     R0
3427        BEQ     @@noaction
3428
3429        MVI     LHAND,  R2
3430        ANDI    #$E0,   R2
3431        CMPR    R2,     R0
3432        BEQ     @@noaction
3433
3434        PSHR    R1
3435        ; Ok, decode the action buttons.
3436        MVII    #ACTTBL,R4      ; R4 points to the "action table"
3437        MVII    #3,     R1
3438@@actloop:
3439        CMP@    R4,     R0
3440        BEQ     @@gotaction
3441        DECR    R1
3442        BNEQ    @@actloop
3443        B       @@notaction
3444@@gotaction:
3445        ; Queue an action-key event
3446        ADDI    #15,    R1
3447        PSHR    R5
3448        MVII    #HANDTASK, R0   ; Queue the HANDTASK to process key
3449        JSRD    R5,     QTASK
3450        PULR    R5
3451@@notaction:
3452        PULR    R1
3453
3454@@noaction:
3455        ; Now process disc events.
3456        MVI     LHAND,  R2      ; Get our previous controller input
3457        MVO     R1,     LHAND   ; Save current input
3458        MOVR    R1,     R3
3459
3460        MVII    #DSCTBL,R4      ; R4 points to the "disc table"
3461        MVII    #4,     R1
3462@@discloop:
3463        MOVR    R3,     R0      ; Restore word.
3464        AND@    R4,     R0      ; Compare against a direction bit.
3465        BNEQ    @@gotdisc       ; Is this bit set?
3466        DECR    R1
3467        BNEQ    @@discloop
3468        B       @@nothing
3469@@gotdisc:
3470        ADDI    #11,    R1
3471        PSHR    R1
3472        MOVR    R0,     R3
3473        AND     REPEAT, R0      ; See if we're repeating
3474        BNEQ    @@repeating
3475        ANDR    R3,     R2      ; See if this is just our prev keypress
3476        BEQ     @@notrepeating  ; Yup, this is new.
3477        PULR    R1
3478        B       @@nothing       ; ... otherwise ignore it.
3479
3480@@noinput:
3481        CLRR    R1
3482        MVO     R1,     LHAND   ; Clear last-hand
3483        MVO     R1,     LHKPD   ; Clear last-was-keypad
3484        MVO     R1,     REPEAT  ; Clear repeating key
3485@@nothing:
3486        JR      R5              ; Return.
3487
3488        ENDP
3489
3490;;==========================================================================;;
3491;;  NEXTRAND                                                                ;;
3492;;  Simple task which updates the random number generator.  It advances     ;;
3493;;  the random number generator's state by one bit.  This is a good one     ;;
3494;;  to call from the background task to keep things random.                 ;;
3495;;                                                                          ;;
3496;;  NEXTRANDX                                                               ;;
3497;;  Same as NEXTRAND, only it iterates for R2 iterations, generating R2     ;;
3498;;  new random bits.  (default is only one new random bit)                  ;;
3499;;                                                                          ;;
3500;;  INPUTS:                                                                 ;;
3501;;  R2 -- Number of iterations (NEXTRANDX only)                             ;;
3502;;  R5 -- Return address                                                    ;;
3503;;  Random state in RANDLO, RANDHI                                          ;;
3504;;                                                                          ;;
3505;;  OUTPUTS:                                                                ;;
3506;;  R0, R1 -- Contains current rand # state.                                ;;
3507;;  R2 -- Zeroed                                                            ;;
3508;;  R4 -- Trashed                                                           ;;
3509;;==========================================================================;;
3510NEXTRAND        PROC
3511        MVII    #1,     R2
3512NEXTRANDX
3513        MVII    #RANDLO, R4
3514        MVI@    R4,     R0
3515        MVI@    R4,     R1
3516
3517@@loop:
3518        SLLC    R0,     1
3519        RLC     R1,     1
3520        BNC     @@nocarry
3521;       XORI    #$5,    R0      ; period==(2**31 - 1) polynomial
3522        XORI    #$04C1, R1      ; period==(2**32 - 1) polynomial (CRC-32 poly)
3523        XORI    #$1DB7, R0
3524@@nocarry:
3525        DECR    R2
3526        BNEQ    @@loop
3527
3528        SUBI    #2,     R4
3529        MVO@    R0,     R4
3530        MVO@    R1,     R4
3531        JR      R5
3532
3533        ENDP
3534
3535;;==========================================================================;;
3536;; POW10                                                                    ;;
3537;; Look-up table with powers of 10 as 32-bit numbers (little endian)        ;;
3538;;                                                                          ;;
3539;; NPW10                                                                    ;;
3540;; Same as POW10, only -10**x instead of 10**x                              ;;
3541;;==========================================================================;;
3542
3543POW10
3544POW10_9 DECLE  1000000000 AND $FFFF, 1000000000 SHR 16  ; 10**9
3545POW10_8 DECLE  100000000  AND $FFFF, 100000000  SHR 16  ; 10**8
3546POW10_7 DECLE  10000000   AND $FFFF, 10000000   SHR 16  ; 10**7
3547POW10_6 DECLE  1000000    AND $FFFF, 1000000    SHR 16  ; 10**6
3548POW10_5 DECLE  100000     AND $FFFF, 100000     SHR 16  ; 10**5
3549POW10_4 DECLE  10000      AND $FFFF, 10000      SHR 16  ; 10**4
3550POW10_3 DECLE  1000       AND $FFFF, 1000       SHR 16  ; 10**3
3551POW10_2 DECLE  100        AND $FFFF, 100        SHR 16  ; 10**2
3552POW10_1 DECLE  10         AND $FFFF, 10         SHR 16  ; 10**1
3553POW10_0 DECLE  1          AND $FFFF, 1          SHR 16  ; 10**0
3554
3555NPW10
3556NPW10_9 DECLE -1000000000 AND $FFFF,(-1000000000 SHR 16) AND $FFFF  ;-10**9
3557NPW10_8 DECLE -100000000  AND $FFFF,(-100000000  SHR 16) AND $FFFF  ;-10**8
3558NPW10_7 DECLE -10000000   AND $FFFF,(-10000000   SHR 16) AND $FFFF  ;-10**7
3559NPW10_6 DECLE -1000000    AND $FFFF,(-1000000    SHR 16) AND $FFFF  ;-10**6
3560NPW10_5 DECLE -100000     AND $FFFF,(-100000     SHR 16) AND $FFFF  ;-10**5
3561NPW10_4 DECLE -10000      AND $FFFF,(-10000      SHR 16) AND $FFFF  ;-10**4
3562NPW10_3 DECLE -1000       AND $FFFF,(-1000       SHR 16) AND $FFFF  ;-10**3
3563NPW10_2 DECLE -100        AND $FFFF,(-100        SHR 16) AND $FFFF  ;-10**2
3564NPW10_1 DECLE -10         AND $FFFF,(-10         SHR 16) AND $FFFF  ;-10**1
3565NPW10_0 DECLE -1          AND $FFFF,(-1          SHR 16) AND $FFFF  ;-10**0
3566
3567;;==========================================================================;;
3568;;  DEC16                                                                   ;;
3569;;  Displays a 16-bit decimal number on the screen with leading blanks      ;;
3570;;  in a field up to 5 characters wide.                                     ;;
3571;;                                                                          ;;
3572;;  DEC16A                                                                  ;;
3573;;  Same as DEC16, only displays leading zeroes.                            ;;
3574;;                                                                          ;;
3575;;  DEC16B                                                                  ;;
3576;;  Same as DEC16, only leading zeros are controlled by bit 15 of R3.       ;;
3577;;  (If set, suppress leading zeros.  If clear, show leading zeros.)        ;;
3578;;                                                                          ;;
3579;;  DEC16C                                                                  ;;
3580;;  Same as DEC16B, except R1 contains an amount to add to the first digit  ;;
3581;;                                                                          ;;
3582;;  INPUTS:                                                                 ;;
3583;;  R0 -- Decimal number                                                    ;;
3584;;  R1 -- (DEC16C only): Amount to add to initial digit.                    ;;
3585;;  R2 -- Number of digits to suppress  (If R2<=5, it is 5-field_width)     ;;
3586;;  R3 -- Color mask                                                        ;;
3587;;  R4 -- Screen offset (8-bit)                                             ;;
3588;;                                                                          ;;
3589;;  OUTPUTS:                                                                ;;
3590;;  R0 -- Zeroed                                                            ;;
3591;;  R1 -- Trashed                                                           ;;
3592;;  R2 -- Remaining digits to suppress (0 if initially <= 5.)               ;;
3593;;  R3 -- Color mask, with bit 15 set if no digits displayed.               ;;
3594;;  R4 -- Pointer to character just right of string                         ;;
3595;;  R5 -- Trashed                                                           ;;
3596;;                                                                          ;;
3597;;  Routine uses $110, $111 for temporary storage.                          ;;
3598;;==========================================================================;;
3599DEC16   PROC
3600@@so    EQU     $110
3601@@fw    EQU     $111
3602        SETC                    ; Prepare to set bit 15 of color mask
3603        INCR    PC              ; Skip the CLRC
3604DEC16A
3605        CLRC                    ; Prepare to clear bit 15 of clrmask
3606        SLL     R3,     1
3607        RRC     R3,     1       ; Set/clear bit 15 of color mask
3608DEC16B
3609        CLRR    R1
3610DEC16C
3611        PSHR    R5              ; Save return address
3612        MVII    #POW10_4, R5    ; Point to '10000' entry in POW10
3613        MVO     R4,     @@so    ; Save screen offset
3614        MVO     R2,     @@fw    ; Save field width
3615        MVII    #5,     R4      ; Iterate 5 (16-bit goes to 65536)
3616        MOVR    R1,     R2
3617        INCR    PC
3618@@digitlp:
3619
3620        CLRR    R2              ; Start with division result == 0
3621        MVI@    R5,     R1      ; Load power of 10
3622        INCR    R5              ; Point to next smaller power of 10
3623@@divloop:
3624        INCR    R2
3625        SUBR    R1,     R0      ; Divide by repeated subtraction
3626        BC      @@divloop
3627        ADDR    R1,     R0      ; Loop iterates 1 extra time: Fix it.
3628        DECR    R2              ; Fix extra iter.  Also test if 0
3629        BNEQ    @@disp          ; If digit != 0, display it.
3630        TSTR    R3              ; If digit == 0 and no lead 0, skip
3631        BMI     @@blank
3632@@disp:
3633        SLL     R3,     1       ; Clear "no leading 0" flag
3634        SLR     R3,     1       ;
3635        MVI     @@fw,   R1      ; Get field width
3636        DECR    R1              ; Are we in active field yet?
3637        BMI     @@ok            ; Yes: Go ahead and display
3638        MVO     R1,     @@fw    ; No: Save our count-down till field
3639        B       @@iter          ;     and don't display the digit.
3640@@blank:
3641        MOVR    R3,     R2      ; Blank character _just_ gets format
3642        MVI     @@fw,   R1      ; Get field width
3643        DECR    R1              ; Are we in active field yet?
3644        BMI     @@drawit        ; Yes: Go ahead and display
3645        MVO     R1,     @@fw    ; No: Save our count-down till field
3646        B       @@iter          ;     and don't display the digit.
3647@@ok:
3648        ADDI    #$10,   R2      ; Pseudo-ASCII digits start at 0x10
3649        SLL     R2,     2       ; Put pseudo-ASCII char in position
3650        ADDR    R2,     R2      ; ... by shifting left 3
3651        XORR    R3,     R2      ; Merge with display format
3652@@drawit:
3653        MVI     @@so,   R1      ; Get screen offset
3654        XORI    #$200,  R1      ; Move to screen
3655        MVO@    R2,     R1      ; Put character on screen
3656        INCR    R1              ; Move the pointer
3657        MVO     R1,     @@so    ; Save the new offset
3658@@iter:
3659        DECR    R4              ; Count down our digit count
3660        BNEQ    @@digitlp       ; Keep iterating
3661
3662        MVI     @@so,   R4      ; Restore offset
3663        MVI     @@fw,   R2      ; Restore digit suppress ocunt
3664
3665        PULR    PC              ; Whew!  Done!
3666        ENDP
3667
3668;;==========================================================================;;
3669;;  DEC32                                                                   ;;
3670;;  Displays a 32 bit number without leading zeros.  It performs this feat  ;;
3671;;  by calling DEC16 twice.                                                 ;;
3672;;                                                                          ;;
3673;;  DEC32A                                                                  ;;
3674;;  Same as DEC32, except leading zeros are displayed.                      ;;
3675;;                                                                          ;;
3676;;  DEC32B                                                                  ;;
3677;;  Same as DEC32, except leading zeros are controlled by bit 15 of R3      ;;
3678;;  (If set, suppress leading zeros.  If clear, show leading zeros.)        ;;
3679;;                                                                          ;;
3680;;  INPUTS:                                                                 ;;
3681;;  R0 -- Low half of 32-bit number                                         ;;
3682;;  R1 -- High half of 32-bit number                                        ;;
3683;;  R2 -- Number of leading digits to suppress (10 - field width)           ;;
3684;;  R3 -- Screen format word                                                ;;
3685;;  R4 -- Screen offset (8-bit)                                             ;;
3686;;                                                                          ;;
3687;;  $110..$113 used for temporary storage                                   ;;
3688;;==========================================================================;;
3689DEC32   PROC
3690@@so    EQU     $110
3691@@fw    EQU     $111
3692@@fmt   EQU     $35D
3693
3694        SETC                    ; Prepare to set bit 15 of color mask
3695        INCR    PC              ; Skip the CLRC
3696DEC32A
3697        CLRC                    ; Prepare to clear bit 15 of clrmask
3698        SLL     R3,     1
3699        RRC     R3,     1       ; Set/clear bit 15 of color mask
3700        NOP                     ; (interruptible for STIC)
3701DEC32B
3702        PSHR    R5              ; Save return address
3703        MVO     R2,     @@fw    ; Save field width
3704        MVO     R4,     @@so    ; Save screen offset
3705        MVO     R3,     @@fmt
3706
3707        CLRR    R3
3708        PSHR    R3              ; Push accumulator (init'd to 0)
3709
3710        ; Use division by repeated subtraction to generate a 16-bit
3711        ; value which represents the first 5 digits of the 10 digit number.
3712        MVII    #NPW10_9, R5    ; Point to -10**9
3713
3714@@digitlp:
3715        CLRR    R3
3716        MVI@    R5,     R2      ; Load low half of 32-bit -10**x
3717        MVI@    R5,     R4      ; Load high half of 32-bit -10**x
3718@@divlp:
3719        SLR     R3,     1
3720@@divlpb:
3721        INCR    R3
3722        ADDR    R2,     R0      ; Add the low half
3723        ADCR    R1              ; Add carry from low half
3724        RLC     R3,     1       ; See if adding the carry carried
3725        ADDR    R4,     R1      ; Add high half
3726        BC      @@divlp         ; Loop if we had a carry from either
3727        SARC    R3,     1       ;   upper half ADD.  (We can't get
3728        BC      @@divlpb        ;   a carry from both, though.)
3729
3730        ; Subtract off the extra iteration
3731        SUBR    R2,     R0      ; Subtract the low half.
3732        ADCR    R1              ; Add in the "not-borrow"
3733        DECR    R1              ; Turn "not-borrow" into "borrow"
3734        SUBR    R4,     R1      ; Subtract the high half.
3735
3736        DECR    R3
3737        BEQ     @@nxtdigit
3738
3739        ; Take our count and multiply it by the appropriate power of 10.
3740        MOVR    R5,     R2
3741        MOVR    R3,     R4
3742        SUBI    #NPW10_4, R2    ; Translate 10**x to 10**(x-5)
3743        BEQ     @@donemult
3744@@mult:
3745        ADDR    R4,     R4      ; To mult by 10, do (x<<1)+(x<<3)
3746        MOVR    R4,     R3
3747        SLL     R3,     2
3748        ADDR    R3,     R4
3749        ADDI    #$4,    R2
3750        BLT     @@mult
3751@@donemult:
3752        ADD@    SP,     R4      ; Add this to our 16-bit accum.
3753        PSHR    R4              ; that we keep on top-of-stack
3754@@nxtdigit:
3755        CMPI    #NPW10_4, R5
3756        BLT     @@digitlp
3757
3758        MVI     @@fw,   R2      ; Restore field width
3759        MVI     @@so,   R4      ; Restore screen offset
3760        MVI     @@fmt,  R3      ; ...
3761        MVO     R0,     @@fmt   ; Save low half momentarily.
3762        PULR    R0              ; Get accumulated word for display
3763        PSHR    R1              ; Save upper bit
3764        CALL    DEC16B          ; Display first five digits
3765
3766        ; Now, our 32-bit number should be less than 100000.  That
3767        ; means R1 should be 0 or 1.  We display the last five digits
3768        ; as a single 16-bit number by handling that bit separately.
3769
3770        MVI     @@fmt,  R0      ; Restore lower 16 bits
3771
3772        PULR    R1              ; Get upper bit
3773        TSTR    R1              ; Was it zero?
3774        BEQ     @@noextra       ; Yes:  Nothing special to do
3775        MVII    #6,     R1      ; No: Add 6 to the leading digit
3776        ADDI    #5536,  R0      ; ... and "5536" to remaining digits
3777@@noextra:
3778        PULR    R5              ; Chain the return.
3779        B       DEC16C          ; Display remaining digits.  WHEW!
3780        ENDP
3781
3782;;==========================================================================;;
3783;;  DEC32Z                                                                  ;;
3784;;  Same as DEC32, except a zero is displayed in the final position if      ;;
3785;;  the whole number's value is zero.                                       ;;
3786;;                                                                          ;;
3787;;  DEC16Z                                                                  ;;
3788;;  Same as DEC16, except a zero is displayed in the final position if      ;;
3789;;  the whole number's value is zero.                                       ;;
3790;;==========================================================================;;
3791DEC32Z  PROC
3792        TSTR    R0              ; Is lower half non-zero?
3793        BNEQ    DEC32           ; Yes:  Go display the number as per usual.
3794        TSTR    R1              ; Is upper half non-zero?
3795        BNEQ    DEC32           ; Yes:  Go display the number as per usual.
3796        MVII    #10,    R1      ; Otherwise, display a 0 in a field that is
3797        B       @@dozero        ; ... up to 10 spaces wide.
3798DEC16Z:
3799        TSTR    R0              ; Is the number non-zero?
3800        BNEQ    DEC16           ; Yes:  Go display the number as per usual.
3801        MVII    #5,     R1      ; Otherwise, display a 0 in a field that is
3802                                ; ... up to 5 spaces wide.
3803@@dozero:
3804        SUBR    R2,     R1      ; Account for suppressed digits.
3805        BLE     @@nodisp        ; Display nothing if # of suppressed digits
3806                                ; is greater than or equal to our field width.
3807
3808        ADDI    #$200,  R4      ; Point into display memory.
3809        INCR    PC              ; Skip the first MVO@ in loop below.
3810@@loop:
3811        MVO@    R3,     R4      ; Output a blank space (leading blanks)
3812        DECR    R1              ; Decrement our count of leading blanks.
3813        BNEQ    @@loop          ; Keep looping until we've filled the blank
3814                                ; ... portion of the displayed number.
3815
3816        XORI    #$80,   R3      ; Now, display the digit 0 in last position.
3817        MVO@    R3,     R4
3818        XORI    #$80,   R3      ; Finally, restore our display format word.
3819
3820@@nodisp:
3821        JR      R5              ; Return to the caller.
3822        ENDP
3823
3824;;==========================================================================;;
3825;;  DRAWSTRING1                                                             ;;
3826;;  Puts an ASCIIZ string pointed to by R0 onscreen.                        ;;
3827;;                                                                          ;;
3828;;  DRAWSTRING2                                                             ;;
3829;;  Puts an ASCIIZ string after a JSR R5 onscreen.                          ;;
3830;;                                                                          ;;
3831;;  DRAWSTRING3                                                             ;;
3832;;  Reads R1, R4 from @ R5, then does DRAWSTRING2.                          ;;
3833;;                                                                          ;;
3834;;  DRAWSTRING4                                                             ;;
3835;;  Reads R4 from @ R5, then does DRAWSTRING2.                              ;;
3836;;                                                                          ;;
3837;;  DRAWSTRING5                                                             ;;
3838;;  Reads R0, R1, R4 from @ R5, then does DRAWSTRING1.                      ;;
3839;;                                                                          ;;
3840;;  INPUTS:                                                                 ;;
3841;;  R0 -- String pointer (if DRAWSTRING)                                    ;;
3842;;  R1 -- Screen format word                                                ;;
3843;;  R4 -- Output pointer                                                    ;;
3844;;  R5 -- Return address (also, string if DRAWSTRING2).                     ;;
3845;;                                                                          ;;
3846;;  OUTPUTS:                                                                ;;
3847;;  R0 -- Zero if DRAWSTRING2, one if DRAWSTRING                            ;;
3848;;  R1 -- Untouched EXCEPT bit 15 is cleared.                               ;;
3849;;  R4 -- Points just after displayed string.                               ;;
3850;;  R5 -- Points just past end of string.                                   ;;
3851;;  R2 and R3 are not modified.                                             ;;
3852;;==========================================================================;;
3853DRAWSTRING      PROC
3854
3855DRAWSTRING5
3856        MVI@    R5,     R0
3857        MVI@    R5,     R1
3858        MVI@    R5,     R4
3859DRAWSTRING1
3860        PSHR    R5
3861        MOVR    R0,     R5
3862        SETC
3863        INCR    PC
3864DRAWSTRING2:
3865        CLRC
3866        SLL     R1,     1
3867        RRC     R1,     1
3868        MVI@    R5,     R0      ; Get first char of string
3869@@tloop:
3870        SUBI    #32,    R0      ; Shift ASCII range to charset
3871        SLL     R0,     2       ; Move it to position for BTAB word
3872        SLL     R0,     1
3873        XORR    R1,     R0      ; Merge with color info
3874        MVO@    R0,     R4      ; Write to display
3875        MVI@    R5,     R0      ; Get next character
3876        TSTR    R0              ; Is it NUL?
3877        BNEQ    @@tloop         ; --> No, keep copying then
3878
3879        SLLC    R1,     1
3880        ADCR    R0
3881        SLR     R1,     1
3882        ADDR    R0,     PC
3883        JR      R5
3884        PULR    PC
3885DRAWSTRING3:
3886        MVI@    R5,     R1
3887DRAWSTRING4:
3888        MVI@    R5,     R4
3889        B       DRAWSTRING2
3890        ENDP
3891
3892;;==========================================================================;;
3893;;  FILLZERO                                                                ;;
3894;;  Fills memory with zeros                                                 ;;
3895;;                                                                          ;;
3896;;  FILLMEM                                                                 ;;
3897;;  Fills memory with a constant                                            ;;
3898;;                                                                          ;;
3899;;  INPUTS:                                                                 ;;
3900;;  R0 -- Fill value (FILLMEM only)                                         ;;
3901;;  R1 -- Number of words to fill                                           ;;
3902;;  R4 -- Start of fill area                                                ;;
3903;;  R5 -- Return address                                                    ;;
3904;;                                                                          ;;
3905;;  OUTPUTS:                                                                ;;
3906;;  R0 -- Zeroed (if FILLZERO)                                              ;;
3907;;  R1 -- Zeroed                                                            ;;
3908;;  R4 -- Points to word after fill area                                    ;;
3909;;==========================================================================;;
3910FILLZERO        PROC
3911        CLRR    R0
3912FILLMEM
3913        MVO@    R0,     R4
3914        DECR    R1
3915        BNEQ    FILLMEM
3916        JR      R5
3917        ENDP
3918
3919;;==========================================================================;;
3920;;  SNDPRIO                                                                 ;;
3921;;                                                                          ;;
3922;;  Task which prioritizes sound streams by giving stream #0                ;;
3923;;  all of the channel bits that aren't being used by stream #1.  This is   ;;
3924;;  called by the ISR during sound-stream updates.                          ;;
3925;;                                                                          ;;
3926;;  This task also implements the "Mute" functionality.                     ;;
3927;;==========================================================================;;
3928SNDPRIO         PROC
3929        MVI     SNDSTRM+4,R0            ; load current hold count for strm 2
3930        CLRR    R1
3931        CMPI    #2,     R0              ; is it less than 2?
3932        BLT     @@inactive              ; yes: stream is inactive
3933        MVI     SNDSTRM+7,R1
3934@@inactive:
3935        XORI    #$3FFF, R1
3936
3937        MVI     MUTE,   R0
3938        TSTR    R0
3939        BNEQ    @@notmute
3940        ANDI    #$7FF,  R1              ; Disallow access to volume.
3941@@notmute:
3942
3943        MVO     R1,     SNDSTRM+3
3944
3945        JR      R5
3946        ENDP
3947
3948;;==========================================================================;;
3949;;  DOSNDSTREAM  -- This is the sound engine!                               ;;
3950;;                                                                          ;;
3951;;  PSG memory map                                                          ;;
3952;;                                                                          ;;
3953;;        $1F4:$1F0       Channel A period (12 bits)                        ;;
3954;;        $1F5:$1F1       Channel B period (12 bits)                        ;;
3955;;        $1F6:$1F2       Channel C period (12 bits)                        ;;
3956;;        $1F7:$1F3       Envelope period (16 bits)                         ;;
3957;;                                                                          ;;
3958;;        $1F8            Channel enables                                   ;;
3959;;        $1F9            Noise period (5 bits)                             ;;
3960;;        $1FA            Envelope mode                                     ;;
3961;;                                                                          ;;
3962;;        $1FB            Channel A volume                                  ;;
3963;;        $1FC            Channel B volume                                  ;;
3964;;        $1FD            Channel C volume                                  ;;
3965;;                                                                          ;;
3966;;        $1FE, $1FF      Hand controllers (not used for sound.)            ;;
3967;;                                                                          ;;
3968;;  Sound is microprogrammed with a series of records of the following      ;;
3969;;  format:                                                                 ;;
3970;;                                                                          ;;
3971;;    INSTRUCTION WORD (two formats):                                       ;;
3972;;                                                                          ;;
3973;;    Format 1 (BREF = 0)                                                   ;;
3974;;    +---+---+---+---+---+---+---+---+---+---+---+---+----+----+----+----+ ;;
3975;;    |          DURATION TO HOLD THIS FRAME          |QUIT|ZERO|PCTL|BREF| ;;
3976;;    +---+---+---+---+---+---+---+---+---+---+---+---+----+----+----+----+ ;;
3977;;                                                                          ;;
3978;;    Format 2 (BREF = 1)                                                   ;;
3979;;    +---+---+---+---+---+---+---+---+---+---+---+---+----+----+----+----+ ;;
3980;;    | BREF OFFSET RELATIVE TO $ - 2 | DUR. TO HOLD  |QUIT|ZERO|PCTL|BREF| ;;
3981;;    +---+---+---+---+---+---+---+---+---+---+---+---+----+----+----+----+ ;;
3982;;                                                                          ;;
3983;;    CONTROL WORD                                                          ;;
3984;;    +---+----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+    ;;
3985;;    | 0 |LINK|snD|snC|snB|snA|sn9|sn8|sn7|sn6|sn5|sn4|sn3|sn2|sn1|sn0|    ;;
3986;;    +---+----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+    ;;
3987;;                                                                          ;;
3988;;    DATA BYTES, 1 word for each two snX bits set.                         ;;
3989;;    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+     ;;
3990;;    |    SECOND BYTE TO WRITE       |     FIRST BYTE TO WRITE       |     ;;
3991;;    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+     ;;
3992;;                                                                          ;;
3993;;    LINK ADDRESS (if LINK set), 1 word, as-is.                            ;;
3994;;    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+     ;;
3995;;    |          ADDRESS TO CONTINUE SOUND PROCESSING AT.             |     ;;
3996;;    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+     ;;
3997;;                                                                          ;;
3998;;                                                                          ;;
3999;;  Instruction Word details:                                               ;;
4000;;                                                                          ;;
4001;;  The DUR field in the Instruction Word specifies how long a set of       ;;
4002;;  values is held in the PSG before it processes the next word.  The       ;;
4003;;  duration is specified in ticks, where 1 tick == 1/60th second.  A       ;;
4004;;  duration of 0 means "immediately"--the sound processor will start       ;;
4005;;  processing the next sound record as soon as it completes this one.      ;;
4006;;  This is useful for combining operations, such as setting ZERO &         ;;
4007;;  PCTL to zero the previous notes, and then sending new ones (if they     ;;
4008;;  are on different channels).                                             ;;
4009;;                                                                          ;;
4010;;  The QUIT, ZERO, PCTL, BREF and LINK flags control the following         ;;
4011;;  behavior:                                                               ;;
4012;;                                                                          ;;
4013;;    QUIT   When set, this terminates the sound given sound microprogram   ;;
4014;;           after the duration specified.                                  ;;
4015;;                                                                          ;;
4016;;    ZERO   When set, this tells the sound processor that no bytes are     ;;
4017;;           coded for this frame since all were zeros.  This causes the    ;;
4018;;           processor to write a pattern of zeros guided by the control    ;;
4019;;           word.                                                          ;;
4020;;                                                                          ;;
4021;;    PCTL   When set, this tells the sound processor to use the previous   ;;
4022;;           control word.  The previous control word is not defined when   ;;
4023;;           the stream is started, but it is defined across a link.        ;;
4024;;                                                                          ;;
4025;;    BREF   When set, this frame is coded only as a back-reference.  All   ;;
4026;;           control decles and data bytes come from the back reference.    ;;
4027;;           Note:  When using a back-reference, the QUIT, ZERO, PCTL, and  ;;
4028;;           DUR settings are used from the Instruction Word for the        ;;
4029;;           _current_ frame!  Also, the duration is limited to four bits   ;;
4030;;           when BREF=1, since the upper byte gives the back-reference     ;;
4031;;           relative offset.                                               ;;
4032;;                                                                          ;;
4033;;    LINK   When set, this tells the sound processor that this frame       ;;
4034;;           is followed by the address for the next frame.  Otherwise,     ;;
4035;;           the sound processor assumes that the next frame is after this  ;;
4036;;           one.  LINK is effectively a "jump after this" instruction.     ;;
4037;;           NOTE:  LINK is actually in the Control Word, and is remembered ;;
4038;;           when PCTL=1 in the next record.                                ;;
4039;;                                                                          ;;
4040;;  Control Word details:                                                   ;;
4041;;                                                                          ;;
4042;;  The bits sn0 ... snD report whether bytes are coded for each of the     ;;
4043;;  fourteen sound registers on the PSG.  The PCTL bit in the Instruction   ;;
4044;;  word specifies whether the previous control word is reused for the      ;;
4045;;  current frame or is read from the stream.                               ;;
4046;;                                                                          ;;
4047;;  Sound Engine details:                                                   ;;
4048;;                                                                          ;;
4049;;  Up to two sound programs may be running at once.  It is up to the       ;;
4050;;  programmer to ensure that they do not try to control the same channels  ;;
4051;;  at once.  The intended use is to have music playing on one stream       ;;
4052;;  and sound effects on a second stream.                                   ;;
4053;;                                                                          ;;
4054;;  The following state is kept for each active sound stream in 16-bit      ;;
4055;;  memory:                                                                 ;;
4056;;                                                                          ;;
4057;;      HOLDCNT    -- Number of ticks left for current record times 2       ;;
4058;;                    If the LSB of HOLDCNT is set, stream processing stops ;;
4059;;                    after this record.                                    ;;
4060;;                                                                          ;;
4061;;      PREVCTL    -- Previous control word.                                ;;
4062;;                                                                          ;;
4063;;      NEXTREC    -- Pointer to the next stream record.                    ;;
4064;;                                                                          ;;
4065;;      STRMMSK    -- Stream channel mask.  This is used to prevent a       ;;
4066;;                    stream from updating certain channel parameters and   ;;
4067;;                    is most useful when, say, a sound-effect needs to     ;;
4068;;                    borrow a channel briefly.                             ;;
4069;;                                                                          ;;
4070;;  NOTE: Bit 15 of control word (eg. bit 7 of second word) MUST BE 0.      ;;
4071;;        Otherwise, copy loop will loop forever.  Also, bit 14 of stream   ;;
4072;;        channel mask MUST BE 0, otherwise the copy loop will write part   ;;
4073;;        of the link address to one of the hand controller addresses.  :-) ;;
4074;;                                                                          ;;
4075;;==========================================================================;;
4076DOSNDSTREAM:    PROC
4077        MVI@    R5,     R4
4078        PSHR    R5
4079
4080        MVI@    R4,     R1              ; Load tick/hold count (REC[0])
4081        SUBI    #2,     R1              ; Count down the hold count
4082        BMI     @@inactive              ; If count goes negative, channel is
4083                                        ;    inactive.
4084        BEQ     @@nextrec               ; If count goes to zero, load next
4085                                        ;    sound record into the channel
4086
4087        DECR    R4                      ; Otherwise, store the updated count.
4088        MVO@    R1,     R4              ; count ==> REC[0]
4089
4090@@inactive:
4091        PULR    PC                      ; Return.
4092
4093@@backup:
4094        DECR    R4
4095@@nextrec:
4096        MVI@    R4,     R5              ; Load the stream record ptr (REC[1])
4097        MVI@    R5,     R2              ; Load hold tick-count from stream
4098        MOVR    R2,     R3
4099        SARC    R2,     1               ; See if this is a back-reference
4100        BNC     @@notbref
4101        ANDI    #$7F,   R2              ; Clear BREF offset out of upper bits.
4102        SWAP    R3
4103        ANDI    #$FF,   R3              ; Extract BREF offset from upper 8 bits
4104        ADDI    #2,     R3              ; Make BREF relative to just before rec
4105        DECR    R4
4106        MVO@    R5,     R4              ; Store updated record ptr (REC[1])
4107        SUBR    R3,     R5              ; Subtract relative offset
4108@@notbref:
4109        SARC    R2,     1               ; See if we have new control word.
4110        BC      @@prevctl               ; If PCTL = 1, use prev control word.
4111        MVI@    R5,     R1              ; Load new control-word from stream
4112        MVO@    R1,     R4              ; Store updated ctrl-word to str info
4113                                        ;   (REC[2])
4114        INCR    PC                      ; Skip the following MVI@.
4115@@prevctl:
4116        MVI@    R4,     R1              ; Load previous ctrl-word from str info
4117                                        ;   (REC[2])
4118
4119        MVI@    R4,     R0              ; Load the stream channel mask (REC[3])
4120        SUBI    #4,     R4              ; Point back at hold tick-count
4121        SARC    R2,     1               ; See if we are writing a zero-record
4122        BNEQ    @@notzero
4123        ; If this was a zero hold count, set up so that we will process
4124        ; next record.  This is almost like tail-recursion.
4125        MVII    #@@backup, R3
4126        PSHR    R3
4127@@notzero:
4128        MVO@    R2,     R4              ; Store new tick count (REC[0])
4129
4130        ; We generate a write mask by ANDing the stream channel mask
4131        ; with the control word.  The result is a bit-mask describing
4132        ; which PSG registers we'll actually write to.
4133        ANDR    R1,     R0              ; Generate channel write mask
4134        MVII    #$1F0,  R2              ; Point to the PSG for writing.
4135
4136        BC      @@zerorec               ; If we're writing a zero record,
4137                                        ; don't read any bytes from stream.
4138
4139        PSHR    R4
4140        MVII    #1,     R4
4141        COMR    R0
4142        SARC    R1,     1       ; 6     ; See if we have a coded word.
4143@@loop:
4144        BNC     @@noread        ; 9 7   ; If not, skip the read.
4145@@read:
4146        SWAP    R3,     1
4147        XORI    #1,     R4
4148        ADDR    R4,     PC
4149        MVI@    R5,     R3      ; 8     ; ... otherwise read word from stream
4150        SARC    R0,     1       ; 6     ; See if we are to write this word.
4151        ADCR    PC              ; 6
4152        MVO@    R3,     R2      ; 8     ; ... otherwise write to the PSG
4153        INCR    R2              ; 6     ; Always update the PSG pointer.
4154
4155        SARC    R1,     1       ; 6     ; See if we have a coded word.
4156        BNEQ    @@loop          ; 9 7   ; Yes: Keep looping!
4157        BC      @@read          ;       ; affects last bit ONLY.
4158
4159@@finish:
4160        PULR    R4
4161
4162        CMPI    #$1FF,  R2              ; Did we have a link pointer in this
4163                                        ; record?
4164        BLT     @@nolink                ; If not, then don't grab one here.
4165        MOVR    R3,     R5              ; Move link pointer to record pointer
4166        B       @@waslink               ; Skip the back-ref test.
4167@@nolink:
4168        CMP@    R4,     R5              ; Detect if this was a back-ref
4169        BLE     @@return                ; If it was, our pointer didn't advance
4170        DECR    R4                      ; Else, prepare to store new rec ptr
4171@@waslink:
4172        MVO@    R5,     R4              ; Store record ptr to stream info
4173@@return:
4174        PULR    PC                      ; Return
4175
4176@@noread:
4177        INCR    R2              ; 6
4178        SARC    R0,     1       ; 6
4179        SARC    R1,     1       ; 6
4180        BNEQ    @@loop          ; 9 7
4181        BC      @@read          ;      ; affects last bit ONLY.
4182        B       @@finish
4183
4184                                ; 56  coded and written
4185                                ; 48  coded, not written
4186                                ; 36  not coded, not written
4187@@zerorec:
4188        CLRR    R3
4189        SARC    R0,     1       ; 6     ; See if we are to write this word.
4190@@zloop:
4191        BNC     @@nozero        ; 9 7   ; Skip write if not in write mask
4192        MVO@    R3,     R2      ; 8     ; ... otherwise write to the PSG
4193@@nozero:
4194        INCR    R2              ; 6     ; Always update the PSG0pointer.
4195        SARC    R0,     1       ; 6     ; See if we are to write this word.
4196        BNEQ    @@zloop         ; 9 7   ; Yes: Keep looping!
4197        BNC     @@zlink
4198        MVO@    R3,     R2
4199
4200        ; We need to handle link records specially here.
4201@@zlink:
4202        SLLC    R1,     2               ; Check bit 14.
4203        BNOV    @@nolink                ; If set, we have link, else, we don't
4204        MVI@    R5,     R3              ; Load the link address
4205        MVO@    R3,     R4              ; Store record ptr to stream info
4206
4207        PULR    PC
4208
4209        ENDP
4210
4211
4212;;==========================================================================;;
4213;;  PLAY.sfx                                                                ;;
4214;;  Plays a sound effect on stream #1.                                      ;;
4215;;                                                                          ;;
4216;;  PLAY.sfxp                                                               ;;
4217;;  Plays a sound effect with raised priority on stream #1.                 ;;
4218;;                                                                          ;;
4219;;  PLAY.mus                                                                ;;
4220;;  Plays a music stream on stream #0.                                      ;;
4221;;                                                                          ;;
4222;;  PLAY.sfxn                                                               ;;
4223;;  Same as PLAY.sfxp, only sound effect number is passed in R4             ;;
4224;;  instead of in the invocation record.                                    ;;
4225;;                                                                          ;;
4226;;  PLAY.musn                                                               ;;
4227;;  Same as PLAY.mus, only sound effect number is passed in R4              ;;
4228;;  instead of in the invocation record.                                    ;;
4229;;                                                                          ;;
4230;;  INPUTS:                                                                 ;;
4231;;  R1 -- Priority (if PLAY.sfxp or PLAY.sfxn)                              ;;
4232;;  R5 -- Invocation record (if PLAY.sfx, PLAY.sfxp, PLAY.mus)              ;;
4233;;        DECLE   Index into SNDTBL.                                        ;;
4234;;                                                                          ;;
4235;;  OUTPUTS:                                                                ;;
4236;;  R1, R4 is clobbered                                                     ;;
4237;;  Sound is triggered on requested channel.                                ;;
4238;;  Returns at R5, or just after if invocation record is used.              ;;
4239;;==========================================================================;;
4240PLAY            PROC
4241
4242@@musn: CLRC                            ; Flag that this is music
4243        INCR    PC                      ; (skip the SETC)
4244@@sfxn: SETC                            ; Flag that this is a sound effect
4245        B       @@go2                   ; Start the music/sound effect
4246
4247@@sfx:  CLRR    R1                      ; If PLAY.sfx, set prio == 0.
4248@@sfxp: SETC                            ; Flag that this is a sound effect
4249        INCR    PC                      ; (skip the CLRC)
4250@@mus:  CLRC                            ; Flag that this is music.
4251
4252
4253@@go:   MVI@    R5,     R4              ; Read invocation record.
4254@@go2:  PSHR    R5                      ; Save return address
4255        PSHR    R0                      ; Save R0 (minimize clobbering)
4256
4257        MVII    #SNDSTRM+4, R5          ; point to sound effect stream
4258        BC      @@notmusic
4259        SUBI    #4,     R5              ; If carry was set, point to sfx stream
4260        CLRR    R1
4261        DECR    R1
4262@@notmusic:
4263
4264        ; Note: The following is non-interruptible, and that's necessary
4265        ; to avoid a race w/ the sound code that runs in the ISR.
4266        DIS
4267        ; First see if something is already playing on this stream
4268        MVII    #2,     R0
4269        CMP@    R5,     R0              ; Compare hold delay vs. 2.
4270        BGT     @@notbusy               ; If delay > 2, stream is busy
4271
4272        ; If the channel is busy, check our sound priority.  This test is
4273        ; performed only for sound effects.  (INCR R1/BEQ is always taken
4274        ; for music).
4275        INCR    R1                      ; If this is music, don't do prio.
4276        BEQ     @@music                 ;
4277
4278        CMP     SFXPRIO, R1             ; Now see if this sound effect is
4279                                        ; high enough priority to play.
4280        BLT     @@noplay                ; No... don't play it.
4281
4282@@play:
4283        MVO     R1,     SFXPRIO         ; Update the sound effect priority
4284
4285@@music:
4286        ADDI    #2,     R5              ; Point to stream mask
4287        MVI@    R5,     R0              ; Get the current stream mask
4288        SUBI    #3,     R5              ; Move the pointer back
4289        SWAP    R0,     1               ; We're interested in vol bits
4290                                        ; and channel enables.
4291
4292        SLR     R0,     2               ; Shift away bits we don't want
4293
4294        SARC    R0,     2               ; ChanEn->C  VolA->OV
4295        BNC     @@no_chan_en            ; ChanEn set?
4296        MVII    #$38,   R1              ;Yes...
4297        MVO     R1,     PSG0+10         ;Set channel enables to "Tone Only"
4298@@no_chan_en:
4299        CLRR    R1                      ; Prepare to write zeros.
4300        BNOV    @@no_vol_a              ; VolA set?
4301        MVO     R1,     PSG0+11         ;Yes... Clear Volume for channel A
4302        MVO     R1,     PSG0+0          ;Yes... Clear PeriodLo for channel A
4303        MVO     R1,     PSG0+4          ;Yes... Clear PeriodHi for channel A
4304@@no_vol_a:
4305        SARC    R0,     2               ; VolB->C  VolC->OV
4306        BNC     @@no_vol_b              ; VolB set?
4307        MVO     R1,     PSG0+12         ;Yes... Clear Volume for channel B
4308        MVO     R1,     PSG0+1          ;Yes... Clear PeriodLo for channel B
4309        MVO     R1,     PSG0+5          ;Yes... Clear PeriodHi for channel B
4310@@no_vol_b:
4311        BNOV    @@no_vol_c              ; VolC set?
4312        MVO     R1,     PSG0+13         ;Yes... Clear Volume for channel C
4313        MVO     R1,     PSG0+2          ;Yes... Clear PeriodLo for channel C
4314        MVO     R1,     PSG0+6          ;Yes... Clear PeriodHi for channel C
4315@@no_vol_c:
4316        B       @@dorecord
4317
4318@@notbusy:
4319        INCR    R1
4320        BEQ     @@dorecord
4321        MVO     R1,     SFXPRIO         ; Update the sound effect priority
4322@@dorecord:
4323
4324        ; At this point R4 is an index into the sound table
4325        ; and R5 points to the second word of the stream record.
4326
4327        ADDI    #SNDTBL,R4              ; Point R5 into sound table
4328
4329        DECR    R5
4330        MVII    #2,     R1              ; Start off with hold count = 2
4331        MVO@    R1,     R5              ; Store hold count == 2
4332        MVI@    R4,     R1
4333        MVO@    R1,     R5              ; Store record pointer
4334        INCR    R5
4335        MVI@    R4,     R1
4336        MVO@    R1,     R5              ; Store channel mask
4337
4338@@noplay:
4339        EIS
4340        PULR    R0
4341        PULR    PC
4342        ENDP
4343
4344;;==========================================================================;;
4345;;  SOUND EFFECTS                                                           ;;
4346;;  BOMB    -- Plays a descending pitch following by an explosion.          ;;
4347;;  DING3   -- Dings 3 times (signals "next level")                         ;;
4348;;  BOOM    -- Just the explosion                                           ;;
4349;;  SILENCE -- The calm after the storm                                     ;;
4350;;==========================================================================;;
4351BOMB
4352        DECLE   16                      ; 1/60th second
4353        DECLE   $2144                   ; Channel C period/vol, Channel Enable
4354        DECLE   $0010, $0C38            ; Period: $010, ChEnable: $38, Vol: $0C
4355
4356        DECLE   16                      ; 1/60th second
4357        DECLE   $04                     ; Rapid descening pitch on channel C
4358        DECLE        $18, $12, $20, $12, $30, $12, $40
4359        DECLE   $12, $50, $12, $60, $12, $70, $12, $80
4360BOOM
4361        DECLE   800                     ; Long delay (5/6ths second)
4362        DECLE   $2788                   ; Env & noise period, Ch.Enable, C Vol
4363        DECLE   $28FF                   ; Env Period == $28FF
4364        DECLE   $1F1C                   ; Noise on channel C only., period 1F
4365        DECLE   $3F00                   ; Envelope type = 0000
4366SILENCE
4367        DECLE   0
4368        DECLE   $0100
4369        DECLE   $38                     ; No noise enabled
4370
4371        DECLE   12
4372        DECLE   $3AFF                   ; All except envelope trigger
4373
4374BOOM2
4375        DECLE   4                       ; No delay
4376        DECLE   $3AFF                   ; Clear nearly everything
4377
4378        DECLE   $3F0                    ; Long delay
4379        DECLE   $3F88                   ; Env & noise period, Ch.Enable, Vols
4380        DECLE   $1FFF
4381        DECLE   $1F07                   ; Noise on all channels, period 1F
4382        DECLE   $3F00                   ; Envelope type = 0000
4383        DECLE   $3F3F                   ; Volume = 15 + Envelope
4384
4385        DECLE   $0F0                    ; Long delay
4386        DECLE   $4000
4387        DECLE   SILENCE
4388
4389BIP
4390        DECLE   $20                     ; 1/30th second
4391        DECLE   $2144                   ; Chan C
4392        DECLE   $00FF, $0938            ; Period: $0FF, ChEnable: $38, Vol: $09
4393
4394        DECLE   $0A                     ; 0/60th second, PCTL, QUIT
4395        DECLE   $0000, $0038            ; Zero all but channel enables.
4396
4397BYUMP
4398        DECLE   16                      ; 1/60th second
4399        DECLE   $2144                   ; Channel C period/vol, Channel Enable
4400        DECLE   $0410, $0C38            ; Period: $510, ChEnable: $38, Vol: $0D
4401        DECLE   16                      ; 1/60th second
4402        DECLE   $04                     ; Rapid descening pitch on channel C
4403        DECLE   $50, $12, $90
4404        DECLE   $10                     ; Last frame, chain to SILENCE
4405        DECLE   $4044
4406        DECLE   $04E0
4407        DECLE   SILENCE
4408
4409DING3
4410        DECLE   15*16                   ; 1/4th second
4411        DECLE   $35EE                   ; Channel C
4412        DECLE   $6160, $00FF
4413        DECLE   $1800                   ; B = $060 C = $061, Env Per = $18FF
4414        DECLE   $0038                   ; Channel enables, Envelope type 0
4415        DECLE   $3F3F                   ; Volume = 15 + envelope
4416
4417        DECLE   15*16 + 4               ; 1/4th second + zero
4418        DECLE   $0400                   ; Hit envelope register w/ Zero
4419
4420        DECLE   15*16 + 4               ; 1/4th second, + zero
4421        DECLE   $4400                   ; Hit envelope register w/ Zero & Link
4422        DECLE   SILENCE                 ; Silence afterwards.
4423
4424BOMB4
4425        DECLE   $14                     ; One tick
4426        DECLE   $3FFF                   ; Clear everything.
4427                                        ; (makes the 'gong' more consistent).
4428
4429        DECLE   $10                     ;
4430        DECLE   $1DBB                   ;
4431        DECLE   $1419, $07FF,  $3807
4432        DECLE   $0038, $3F3F
4433
4434        DECLE   16                      ; 1/60th second
4435        DECLE   $2044                   ; Channel C period/vol, Channel Enable
4436        DECLE   $0010, $000C            ; Period: $010, Vol: $0C
4437
4438        DECLE   16                      ; 1/60th second
4439        DECLE   $04                     ; Rapid descening pitch on channel C
4440        DECLE               $18,$12,$20,$12,$28,$12,$30,$12,$38,$12,$40
4441        DECLE   $12,$10,$12,$18,$12,$20,$12,$28,$12,$30,$12,$38,$12,$40
4442        DECLE   $12,$10,$12,$18,$12,$20,$12,$28,$12,$30,$12,$38,$12,$40
4443        DECLE   0
4444        DECLE   $4000, BOMB
4445
4446;;==========================================================================;;
4447;; Background music data                                                    ;;
4448;;==========================================================================;;
4449        INCLUDE "nut1mrch.asm"
4450        INCLUDE "chindnce.asm"
4451        INCLUDE "behappy.asm"
4452
4453;;==========================================================================;;
4454;;  FONT                                                                    ;;
4455;;==========================================================================;;
4456        INCLUDE "font.asm"
4457
4458
4459        DECLE   "http://spatula-city.org/~im14u2c/intv/", 0
4460        DECLE   "Copyright 2000 Joseph Zbiciak",0
4461        DECLE   "JZ"
4462        DECLE   0
4463