;;==========================================================================;; ;; Joe Zbiciak's PSYCHO BALLS ;; ;; Copyright 2002, Joe Zbiciak, im14u2c AT primenet DOT com. ;; ;; http://www.primenet.com/~im14u2c/intv/ ;; ;;==========================================================================;; ;* ======================================================================== *; ;* TO BUILD IN BIN+CFG FORMAT: *; ;* as1600 -o balls1.bin -l balls1.lst balls1.asm *; ;* *; ;* TO BUILD IN ROM FORMAT: *; ;* as1600 -o balls1.rom -l balls1.lst balls1.asm *; ;* ======================================================================== *; ;* ======================================================================== *; ;* This program is free software; you can redistribute it and/or modify *; ;* it under the terms of the GNU General Public License as published by *; ;* the Free Software Foundation; either version 2 of the License, or *; ;* (at your option) any later version. *; ;* *; ;* This program is distributed in the hope that it will be useful, *; ;* but WITHOUT ANY WARRANTY; without even the implied warranty of *; ;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *; ;* General Public License for more details. *; ;* *; ;* You should have received a copy of the GNU General Public License *; ;* along with this program; if not, write to the Free Software *; ;* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *; ;* ======================================================================== *; ;* Copyright (c) 2002, Joseph Zbiciak *; ;* ======================================================================== *; ROMW 16 ; Use 16-bit ROM width ;------------------------------------------------------------------------------ ; Include system information ;------------------------------------------------------------------------------ INCLUDE "../library/gimini.asm" ;------------------------------------------------------------------------------ ; Allocate some variables in System RAM and Scratch RAM ;------------------------------------------------------------------------------ SCRATCH ORG $100, $100, "-RWBN" ISRVEC RMB 2 ; Always at $100 / $101 _SCRATCH EQU $ ; end of scratch area SYSTEM ORG $2F0, $2F0, "-RWBN" STACK RMB 32 ; Reserve 32 words for the stack STICSH RMB 32 ; STIC Shadow XPOS RMB 8 ; X positions YPOS RMB 8 ; Y positions XVEL RMB 8 ; X velocities YVEL RMB 8 ; Y velocities RNDLO RMB 1 ;\__ Random number generator RNDHI RMB 1 ;/ WTIMER RMB 1 ; WAIT timer SLPTR RMB 1 SNDLIST RMB 3 ; Random sounds LASTSND EQU $ SWPOS RMB 1 ; Sweeper position SWDIR RMB 1 ; Sweeper direction SWTIMER RMB 1 ; Sweeper timer _SYSTEM EQU $ ; end of system area SWCHAR EQU 95*8 + X_RED SWPER EQU 60 * 5 ; sweep every 5 seconds (6 on PAL) ;------------------------------------------------------------------------------ ; Support for polling ECScable ;------------------------------------------------------------------------------ EC_LOC EQU $CF00 EC_MAG EQU $69 EC_POLL EQU $CF01 ORG $5000 ; Use default memory map ;------------------------------------------------------------------------------ ; EXEC-friendly ROM header. ;------------------------------------------------------------------------------ ROMHDR: BIDECLE ZERO ; MOB picture base (points to NULL list) BIDECLE ZERO ; Process table (points to NULL list) BIDECLE MAIN ; Program start address BIDECLE ZERO ; Bkgnd picture base (points to NULL list) BIDECLE ONES ; GRAM pictures (points to NULL list) BIDECLE TITLE ; Cartridge title/date DECLE $03C0 ; No ECS title, run code after title, ; ... no clicks ZERO: DECLE $0000 ; Screen border control DECLE $0000 ; 0 = color stack, 1 = f/b mode ONES: DECLE C_BLU, C_BLU ; Initial color stack 0 and 1: Blue DECLE C_BLU, C_BLU ; Initial color stack 2 and 3: Blue DECLE C_BLU ; Initial border color: Blue ;------------------------------------------------------------------------------ ;; ======================================================================== ;; ;; TITLE -- Display our modified title screen & copyright date. ;; ;; ======================================================================== ;; TITLE: PROC BYTE 102, 'Psycho Balls #1', 0 BEGIN ; Patch the title string to say '=JRMZ=' instead of Mattel. CALL PRINT.FLS ; Write string (ptr in R5) DECLE C_WHT, $23D ; White, Point to 'Mattel' in top-left STRING '=JRMZ=' ; Guess who? :-) STRING ' Productions' BYTE 0 CALL PRINT.FLS ; Write string (ptr in R1) DECLE C_WHT, $2D0 ; White, Point to 'Mattel' in lower-right STRING '2002 =JRMZ=' ; Guess who? :-) BYTE 0 ; Done. RETURN ; Return to EXEC for title screen display ENDP ;; ======================================================================== ;; ;; MAIN: Here's our main program code. ;; ;; ======================================================================== ;; MAIN: PROC DIS ; ------------------------------------------------------------- ; ; Reset the stack pointer. ; ; ------------------------------------------------------------- ; MVII #STACK, R6 ; ------------------------------------------------------------- ; ; Initialize random number generator from contents of RAM. ; ; ------------------------------------------------------------- ; MVII #$100, R4 MVII #$130, R3 @@rnd: ADD@ R4, R0 ADD@ R4, R1 DECR R3 BNEQ @@rnd TSTR R0 BNEQ @@r0_ok MOVR PC, R0 @@r0_ok: TSTR R1 BNEQ @@r1_ok MOVR PC, R1 @@r1_ok: MVO R0, RNDLO MVO R1, RNDHI ; ------------------------------------------------------------- ; ; Set up ISR. This feeds the STIC. ; ; ------------------------------------------------------------- ; MVII #BALLISR, R0 MVO R0, ISRVEC SWAP R0 MVO R0, ISRVEC+1 ; ------------------------------------------------------------- ; ; Clear the screen and display a message. ; ; ------------------------------------------------------------- ; CALL CLRSCR ; Clear the screen CALL PRINT.FLS ; Display our message. DECLE C_YEL ; Yellow DECLE $200 + 5*20 + 4 ; Row #5, colukmn #4 on screen STRING 'Psycho Balls!' BYTE 0 ; ------------------------------------------------------------- ; ; Set up the initial status of the balls in the STIC Shadow ; ; ------------------------------------------------------------- ; CALL MEMCPY DECLE STICSH, BALLINIT, 24 ; ------------------------------------------------------------- ; ; Randomly generate initial positions, velocities. ; ; ------------------------------------------------------------- ; CALL RAND_ARRAY DECLE XPOS, 8, 15 ; Array, length, bits/elem CALL RAND_ARRAY DECLE YPOS, 8, 15 ; Array, length, bits/elem CALL RAND_ARRAY DECLE XVEL, 8, 10 ; Array, length, bits/elem CALL RAND_ARRAY DECLE YVEL, 8, 10 ; Array, length, bits/elem ; ------------------------------------------------------------- ; ; Make the XVEL/YVEL values signed. Relies on XVEL, YVEL ; ; being adjacent. ; ; ------------------------------------------------------------- ; MVII #XVEL, R4 MOVR R4, R5 MVII #16, R1 @@fix_vel: MVI@ R4, R0 SUBI #512, R0 MVO@ R0, R5 DECR R1 BNEQ @@fix_vel ; ------------------------------------------------------------- ; ; Set up the 'sweeper'. He keeps the balls cleaned off the ; ; edges (or tries to). ; ; ------------------------------------------------------------- ; MVII #$200, R1 MVO R1, SWPOS ; upper left corner MVII #1, R1 MVO R1, SWDIR ; going to the right. MVII #SWPER, R1 MVO R1, SWTIMER ; Sweeper starts out sleeping ; ------------------------------------------------------------- ; ; Everything is set up. Enable interrupts. ; ; ------------------------------------------------------------- ; EIS @@main_lp: MVI EC_LOC, R0 CMPI #EC_MAG, R0 BNEQ @@no_poll CALL EC_POLL @@no_poll ; ------------------------------------------------------------- ; ; Update XPOS by XVEL and merge w/ X reg in STIC shadow. ; ; ------------------------------------------------------------- ; MVII #XPOS, R4 MVII #XVEL, R5 MVII #STICSH+0, R3 MVII #8, R2 @@update_x: MVI@ R4, R0 ; Get X position DECR R4 ADD@ R5, R0 ; Add X velocity CMPI #$9F00, R0 ;\ BNC @@xok1 ; | MVII #$9F00, R0 ; |__ Try to keep them onscreen @@xok1: CMPI #$0800, R0 ; | (note unsigned compares!) BC @@xok2 ; | MVII #$0800, R0 ;/ @@xok2: MVO@ R0, R4 ; Store new X position SWAP R0 ;\__ Retain integer portion of ANDI #$00FF, R0 ;/ X position MVI@ R3, R1 ;\__ Get X register and clear old ANDI #$FF00, R1 ;/ X position XORR R0, R1 ; Merge new X position MVO@ R1, R3 ; Store new X register INCR R3 DECR R2 BNEQ @@update_x ; ------------------------------------------------------------- ; ; Update YPOS by YVEL and merge w/ Y reg in STIC shadow. ; ; ------------------------------------------------------------- ; MVII #YPOS, R4 MVII #YVEL, R5 MVII #STICSH+8, R3 MVII #8, R2 @@update_y: MVI@ R4, R0 ; Get X position DECR R4 ADD@ R5, R0 ; Add X velocity CMPI #$5F00, R0 ;\ BLT @@yok1 ; | MVII #$5F00, R0 ; |__ Try to keep them onscreen. @@yok1: CMPI #$0800, R0 ; | BGT @@yok2 ; | MVII #$0800, R0 ;/ @@yok2: MVO@ R0, R4 ; Store new X position SWAP R0 ;\__ Retain integer portion of ANDI #$007F, R0 ;/ Y position MVI@ R3, R1 ;\__ Get Y register and clear old ANDI #$FF80, R1 ;/ Y position XORR R0, R1 ; Merge new Y position MVO@ R1, R3 ; Store new Y register INCR R3 DECR R2 BNEQ @@update_y ; ------------------------------------------------------------- ; ; Check for any collisions. Randomize XVEL/YVEL on collision. ; ; Record up to three random numbers from collisions to play ; ; on the PSG. ; ; ------------------------------------------------------------- ; MVII #STICSH+$18, R4 MVII #SNDLIST, R0 MVO R0, SLPTR CLRR R1 @@check_coll: MVI@ R4, R2 ; Get next collision word ANDI #$3FF, R2 ; Any bits set? BEQ @@no_coll ; No: No collision MVII #11, R0 ;\ CALL RAND ; |-- Number = -512 to 511 SUBI #1024, R0 ;/ MVII #XVEL, R3 ;\__ Point to X velocity ADDR R1, R3 ;/ MVO@ R0, R3 ; Store new XVEL MVII #11, R0 ;\ CALL RAND ; |-- Number = -512 to 511 SUBI #1024, R0 ;/ ADDI #YVEL-XVEL, R3 ; Point to Y velocity MVO@ R0, R3 ; Store new YVEL MVI SLPTR, R3 CMPI #LASTSND,R3 BEQ @@no_snd MVII #10, R0 CALL RAND ADDI #$40, R0 MVO@ R0, R3 INCR R3 MVO R3, SLPTR @@no_snd: @@no_coll: INCR R1 CMPI #8, R1 BNEQ @@check_coll ; ------------------------------------------------------------- ; ; Fill out the silly sounds w/ silence if some are unused. ; ; ------------------------------------------------------------- ; MVI SLPTR, R5 CLRR R0 ; fill unused sounds w/ 0s. INCR PC ; skip MVO@ on first iter. @@pl: MVO@ R0, R5 CMPI #LASTSND, R5 BNEQ @@pl ; ------------------------------------------------------------- ; ; Play up to three silly sounds based on collisions. ; ; ------------------------------------------------------------- ; MVII #$38, R0 MVO R0, PSG0.chan_enable MVII #PSG0.chn_a_lo, R3 ; Period registers MVII #PSG0.chn_a_vol, R4 ; Volume registers MVII #SNDLIST, R5 ; Silly sounds MVII #3, R2 @@play: MVI@ R5, R0 ; get silly tone MVO@ R0, R3 ; \ SWAP R0 ; |__ Write out low and high ADDI #4, R3 ; | halves MVO@ R0, R3 ; / SUBI #3, R3 ; Point at next period register TSTR R0 ; Was it actually a tone? BEQ @@quiet ; $0000 means quiet MVII #$F, R0 ; non-zero: Make volume max. @@quiet: MVO@ R0, R4 ; Write the volume register DECR R2 BNEQ @@play ; ------------------------------------------------------------- ; ; Update the sweeper. ; ; ------------------------------------------------------------- ; MVI SWTIMER, R3 ; Get sweeper sleep timer DECR R3 ; Count it down BPL @@no_sweep ; Is it still going? No sweep CLRR R3 ; Make sweeper active MVI SWPOS, R1 ; Get sweeper position MVI SWDIR, R2 ;\__ Move sweeper ADDR R2, R1 ;/ MVII #SWCHAR,R0 ;\ XOR@ R1, R0 ; |-- toggle displayed/undisplayed MVO@ R0, R1 ;/ MVO R1, SWPOS ; Save updated position ; check for corners and update direction CMPI #$200 + 19, R1 BNEQ @@not_ur MVII #20, R2 ; Move down at upper-right corner B @@sw_done @@not_ur: CMPI #$200 + 11*20 + 19, R1 BNEQ @@not_lr MVII #$FFFF, R2 ; Move left at lower-right corner B @@sw_done @@not_lr: CMPI #$200 + 11*20, R1 BNEQ @@not_ll MVII #$FFEC, R2 ; Move up at lower-left corner B @@sw_done @@not_ll: CMPI #$200, R1 BNEQ @@sw_done MVII #SWPER, R3 ; Reset sweeper sleep at upper left MVII #1, R2 ; Move right at upper-left corner @@sw_done MVO R2, SWDIR @@no_sweep: MVO R3, SWTIMER ; store updated sweeper timer ; ------------------------------------------------------------- ; ; Wait for a frame update. ; ; ------------------------------------------------------------- ; CALL WAIT DECLE 1 B @@main_lp ENDP ;; ======================================================================== ;; ;; RAND_ARRAY -- Fill an array with random values. ;; ;; ======================================================================== ;; RAND_ARRAY PROC MVI@ R5, R4 ; Output pointer MVI@ R5, R1 ; Length MVI@ R5, R0 ; bits/number PSHR R5 @@loop: PSHR R0 CALL RAND MVO@ R0, R4 PULR R0 DECR R1 BNEQ @@loop PULR PC ENDP ;; ======================================================================== ;; ;; BALLINIT -- Initial STIC register state for our balls. ;; ;; ======================================================================== ;; BALLINIT PROC @@x0 DECLE STIC.mobx_visb + STIC.mobx_intr @@x1 DECLE STIC.mobx_visb + STIC.mobx_intr @@x2 DECLE STIC.mobx_visb + STIC.mobx_intr @@x3 DECLE STIC.mobx_visb + STIC.mobx_intr @@x4 DECLE STIC.mobx_visb + STIC.mobx_intr @@x5 DECLE STIC.mobx_visb + STIC.mobx_intr @@x6 DECLE STIC.mobx_visb + STIC.mobx_intr @@x7 DECLE STIC.mobx_visb + STIC.mobx_intr @@y0 DECLE STIC.moby_ysize2 @@y1 DECLE STIC.moby_ysize2 @@y2 DECLE STIC.moby_ysize2 @@y3 DECLE STIC.moby_ysize2 @@y4 DECLE STIC.moby_ysize2 @@y5 DECLE STIC.moby_ysize2 @@y6 DECLE STIC.moby_ysize2 @@y7 DECLE STIC.moby_ysize2 @@a0 DECLE STIC.moba_fg8 + STIC.moba_gram @@a1 DECLE STIC.moba_fg9 + STIC.moba_gram @@a2 DECLE STIC.moba_fgA + STIC.moba_gram @@a3 DECLE STIC.moba_fgB + STIC.moba_gram @@a4 DECLE STIC.moba_fgC + STIC.moba_gram @@a5 DECLE STIC.moba_fgD + STIC.moba_gram @@a6 DECLE STIC.moba_fgE + STIC.moba_gram @@a7 DECLE STIC.moba_fgF + STIC.moba_gram ENDP ;; ======================================================================== ;; ;; BALL -- Picture of our ball. ;; ;; ======================================================================== ;; BALL PROC DECLE %00111100 DECLE %01111110 DECLE %11011011 DECLE %11111111 DECLE %10111101 DECLE %11011011 DECLE %01100110 DECLE %00111100 ENDP ;; ======================================================================== ;; ;; BALLISR -- Main ISR. This feeds the STIC ;; ;; ======================================================================== ;; BALLISR PROC PSHR R5 CALL MEMCPY DECLE $0000, STICSH, 24 ; Copy 24 words to MOB registers CALL MEMCPY DECLE STICSH+$18, $18, 8 ; Copy 8 words from collision regs CLRR R0 MVII #$18, R4 ;\ MVO@ R0, R4 ; | MVO@ R0, R4 ; | MVO@ R0, R4 ; | MVO@ R0, R4 ; |-- Clear the collision registers MVO@ R0, R4 ; | in the STIC MVO@ R0, R4 ; | MVO@ R0, R4 ; | MVO@ R0, R4 ;/ MVII #C_BLU, R0 ;\__ Make screen blue MVO R0, $28 ;/ MVII #C_BLK, R0 ;\__ Make border black MVO R0, $2C ;/ MVO R0, $20 ; Keep the screen on MVI $21, R0 ; Keep the screen color-stack CALL MEMCPY ; Copy 8 words to GRAM card #0 DECLE $3800, BALL, 8 MVI WTIMER, R0 ;\ DECR R0 ; |__ Count down the wait timer BMI @@wt_zero ; | MVO R0, WTIMER ;/ @@wt_zero: PULR PC ENDP ;; ======================================================================== ;; ;; WAIT -- Busy-wait for the specified number of ticks. ;; ;; ======================================================================== ;; WAIT PROC MVI@ R5, R0 MVO R0, WTIMER CLRR R0 @@spin CMP WTIMER, R0 BNEQ @@spin JR R5 ENDP ;; ======================================================================== ;; ;; LIBRARY INCLUDES ;; ;; ======================================================================== ;; INCLUDE "../library/print.asm" ; PRINT.xxx routines INCLUDE "../library/rand.asm" ; RAND routine INCLUDE "../library/fillmem.asm" ; CLRSCR/FILLZERO/FILLMEM INCLUDE "../library/memcpy.asm" ; MEMCPY