; VRAM addresses
SPRATR equ $1B00
; BASIC routines
PLAY equ $73E5
; System variables addresses
CLIKSW equ $F3DB ; Keyboard click sound
FORCLR equ $F3E9 ; Foreground colour
RG1SAV equ $F3E0 ; V9938 register copy
; Game constants
JOYUP equ 1
JOYDOWN equ 5
AUTO_OBJECT equ 3
BALL_OBJECT equ 4
UPPER_BOUND equ 8
LOWER_BOUND equ 150
RGOAL_BOUND equ 250
LGOAL_BOUND equ 4
PAD1_X1 equ 24
PAD1_X2 equ 20
PAD2_X1 equ 228
PAD2_X2 equ 232
PADCENTER equ 17
PAUSE equ 4 ; pause in seconds after a win
CPUYCENTER equ 96
XREACTION equ 180
SUBREACTION equ 10
AUTO_THRESHOLD equ 4 ; internal value needed for CPU AI
INITLEVEL equ 1 ; 1 slowest - 3 fastest, 4 two balls
MAXLEVEL equ 4
HITSLEVEL equ 6
HITSWIN equ 10
MAGIC equ 1
;----------------------------------------------------------
; Main program
;----------------------------------------------------------
.BIOS
.ORG $C500
xor a
ld [CLIKSW],a ; Disables keyclick
ld hl,$020c
ld [FORCLR+1],hl ; Background dark green & border medium green
call INIGRP ; initializes screen 2
ld a,[RG1SAV]
or 3
ld b,a
ld c,1
call WRTVDP ; 16x16 magnified sprites 16x16
MAIN_LOOP: call SHOW_MENU
GAME_LOOP: call DRAW_OBJECTS
call MOVE_OBJECTS
call CHECK_WINNER
jr nz,GAME_LOOP
jr MAIN_LOOP
;------------------------------------------------------
;
; Description: Initializes some data & shows selection screen
; In:
; Out:
; Modifies:
;
;------------------------------------------------------
SHOW_MENU: call CLRSCR
ld hl,0
ld [SCORE1P],hl ; reset both scores
ld hl,$1300 ; text position
ld de,MENUTXT
call PRNTTEXT
ld hl,INITDATA+20
ld [hl],2 ; set initially a 2P game
@@WAITKEY: xor a
call SNSMAT ; reads keyboard matrix
bit 2,a
jr z,@@SET2P
bit 1,a
jr nz,@@WAITKEY
inc [hl]
@@SET2P: call INITOBJECTS
call CLRSCR
ld hl,SPRITES
ld de,$3800
ld bc,64
call LDIRVM ; loads sprite patterns
ld hl,$0000 ; text position
ld de,COURT
call PRNTTEXT ; draw court & score panels
call UPDATESCORE1
jp UPDATESCORE2
;------------------------------------------------------
;
; Description: Clears pattern table and fills color table with a fixed attribute
; In:
; Out:
; Modifies:
;
;------------------------------------------------------
CLRSCR: ld hl,0
ld bc,$1800
ld a,0
call FILVRM
ld hl,$2000
ld bc,$1800
ld a,$FC
jp FILVRM
;------------------------------------------------------
;
; Description: Check if any player is the winner
; In:
; Out:
; Modifies:
;
;------------------------------------------------------
CHECK_WINNER: ld hl,$3C3B ; sets "WIN" string in text buffer
ld [TXTBUFF],hl
ld a,HITSWIN
ld hl,SCORE1P
cp [hl] ; has player 1 won?
jr nz,@@WIN2P
call UPDATESCORE1
jr @@DELAY
@@WIN2P: inc hl
cp [hl] ; has player 2 won?
ret nz
call UPDATESCORE2
@@DELAY: call CLRSPR
ld hl,SND_PING
call PLAY
ld hl,SND_PONG
call PLAY
ld b,PAUSE*50 ; waits some seconds (not in MSX2+ or TR!)
@@LOOP: halt
djnz @@LOOP
ret
;------------------------------------------------------
;
; Description:
; In:
; Out:
; Modifies:
;
;------------------------------------------------------
DRAW_OBJECTS: halt
ld hl,SPRATR
call SETWRT
ld hl,SPRTAB
ld a,[OBJECTS]
ld b,a ; Number of objects to draw
@@NXTSPRITE: push bc
ld bc,$0498
otir ; dumps first 4 bytes to VRAM
ld de,12
add hl,de
pop bc
djnz @@NXTSPRITE
ret
;------------------------------------------------------
;
; Description:
; In:
; Out:
; Modifies:
;
;------------------------------------------------------
MOVE_OBJECTS: ld ix,SPRTAB ; sprite objects table
ld a,[OBJECTS]
ld b,a ; Number of objects to move
@@OBJLOOP: push bc
ld a,[ix+4] ; load type of object
cp BALL_OBJECT
jr nz,@@NOBALL
call MOVE_BALL ; is a ball
jr @@NXTOBJ
@@NOBALL: cp AUTO_OBJECT
jr nz,@@NOAUTO
call MOVE_AUTO ; is a CPU player
jr @@NXTOBJ
@@NOAUTO: call MOVE_MAIN ; is a human player
@@NXTOBJ: ld de,16
add ix,de
pop bc
djnz @@OBJLOOP
; increase level if minimum hits to change level have been reached
ld hl,HITS
ld a,HITSLEVEL
cp [hl]
ret nz
ld [hl],0
inc hl
ld a,[hl]
cp MAXLEVEL
ret z
inc a
jp SETLEVEL
;------------------------------------------------------
;
; Description: Moves 1P(Human) paddle
; In:
; Out:
; Modifies:
;
;------------------------------------------------------
MOVE_MAIN: cp 2
jr z,@@PLY2 ; test only stick 2
call GTSTCK ; test sticks 1 & 0
or a
jr nz,@@MOVE
@@PLY2: call GTSTCK
or a
ret z
@@MOVE: push ix
pop hl
cp JOYUP
jr nz,@@NOUP
ld a,[hl]
cp UPPER_BOUND
ret c
ld c,a
ld a,[SPEED]
neg
jr @@UPDATEY
@@NOUP: cp JOYDOWN
ret nz
ld a,[hl]
cp LOWER_BOUND
ret nc
ld c,a
ld a,[SPEED]
@@UPDATEY: add c ; Y = Y (+/-) SPEED
ld [hl],a
ret
;------------------------------------------------------
;
; Description: Moves CPU paddle
; In:
; Out:
; Modifies:
;
;------------------------------------------------------
MOVE_AUTO: ld c,CPUYCENTER ; loads default CPU Y center
ld iy,[BALLADDR] ; loads address of the ball with greatest X
ld hl,CENTERCPU
ld a,[hl]
or a
ld [hl],MAGIC
jr nz,@@GETPOS ; jump if ball is not going towards paddle
ld a,[MINX]
cp [iy+1] ; test if ball's X is greater than the minimum x required to move CPU
jr nc,@@GETPOS ; if it's not greater move padding using default Y as reference
ld c,[iy] ; C = ball's Y
@@GETPOS: push ix
pop hl
ld a,[hl]
add a,PADCENTER ; center of the paddle
sub c
jr c,@@GODOWN
cp AUTO_THRESHOLD
ret c
ld a,[hl]
cp UPPER_BOUND
ret c
ld c,a
ld a,[SPEED]
neg
jr @@UPDATEY
@@GODOWN: neg
cp AUTO_THRESHOLD
ret c
ld a,[hl]
cp LOWER_BOUND
ret nc
ld c,a
ld a,[SPEED]
@@UPDATEY: add c
ld [hl],a
ret
;------------------------------------------------------
;
; Description: Check if ball has hit a paddle
; In: A=Ball X coordinate, HL = GREATESTX address
; Out:
; Modifies:
;
;------------------------------------------------------
CHECK_HIT: cp PAD2_X1
jr c,@@CHECKPAD1
cp PAD2_X2
ret nc
ld [hl],0 ; HL still holds GREATESTX address
ld hl,SND_PONG ; the sound is PONG
ld a,[SPRTAB+16] ; we must compare with paddle 2 Y
jr @@PADBOUNDS
@@CHECKPAD1: cp PAD1_X1
ret nc
cp PAD1_X2
ret c
ld hl,SND_PING ; the sound is PING
ld a,[SPRTAB] ; we must compare with paddle 1 Y
@@PADBOUNDS: ld c,a
; test if ball's Y is between paddle edges
ld a,[ix]
add 2 ; align with ball's center
sub c ; substracts ball's Y coordinate
cp 32
ret nc
push af
and $FC
rra ; (POSY/4)*2
ld de,DIRTABLE ; WARNING!!, low byte can't be greater than F0
add a,e
ld e,a
ld a,[de]
ld [ix+7],a ; X speed counter
inc de
ld a,[de]
ld [ix+8],a ; Y speed counter
call PLAY
pop af
ld hl,HITS
inc [hl]
cp PADCENTER ; is over/under paddle's center?
ld a,[ix+6]
jr nc,@@ALWPOSITIVE
bit 7,a
jr nz,@@INVX
jr @@NEG
@@ALWPOSITIVE: bit 7,a
jr z,@@INVX
@@NEG: neg
ld [ix+6],a ; inverts Y increment
@@INVX: ld a,[ix+5]
neg
ld [ix+5],a ; inverts X increment
ld [ix+9],1 ; finish X temporal speed counter now
ret
;------------------------------------------------------
;
; Description:
; In:
; Out:
; Modifies:
;
;------------------------------------------------------
MOVE_BALL: ; Y(+0),X(+1),PATTERN(+2),COLOR(+3),
; TYPE(+4),BALLINCX(+5),BALLINCY(+6),
; SPDCOUNTX(+7),SPDCOUNTY(+8),SPDCTEMPX(+9),SPDCTEMPY(+10),
; 0(+11),0(+12),0(+13),0(+14),0(+15)
bit 7,[ix+5]
jr nz,@@NOCENTERCPU ; test if ball is going towards CPU paddle (positive increment)
ld hl,CENTERCPU
ld [hl],0
@@NOCENTERCPU: dec [ix+9] ; decrement X movement counter
jr nz,@@MOVEY
ld a,[ix+7]
ld [ix+9],a ; restores X movement counter
ld a,[SPEED]
bit 7,[ix+5]
jr nz,@@GOINGLEFT ; test if ball is going towards CPU paddle (positive increment)
ld hl,GREATESTX
add [ix+1] ; add coordinate X
cp [hl]
jr c,@@CHKGOAL ; is the CPU's approaching nearest ball?
ld [hl],a ; if so, saves his X coordinate
ld [BALLADDR],ix ; and his attribute address
jr @@CHKGOAL
@@GOINGLEFT: neg
add [ix+1] ; add coordinate X
@@CHKGOAL: cp RGOAL_BOUND ; is beyond right goal line?
jr nc,@@SCORE1
cp LGOAL_BOUND ; is beyond left goal line?
jr c,@@SCORE2
ld [ix+1],a ; saves X coordinate
call CHECK_HIT
@@MOVEY: dec [ix+10] ; decrement Y movement counter
ret nz
ld a,[ix+8]
ld [ix+10],a ; restores Y movement counter
ld a,[SPEED]
bit 7,[ix+6] ; test if Y increment is negative
jr z,@@NONEGY
neg ; so speed is negative
@@NONEGY: add [ix] ; add coordinate Y
ld [ix],a ; saves updated Y coordinate
cp UPPER_BOUND ; is beyond upper bound?
jr c,@@BOUNDY
cp LOWER_BOUND+33 ; is beyond lower bound?
ret c
@@BOUNDY: ld a,[ix+6]
neg ; inverts Y increment
ld [ix+6],a
ret
@@SCORE1: ld hl,SCORE1P
inc [hl] ; 1P scores 1 point
call NUM2TXT
call UPDATESCORE1
jr INITOBJECTS
@@SCORE2: ld hl,SCORE2P
inc [hl] ; 2P scores 1 point
call NUM2TXT
call UPDATESCORE2
;--- DON'T MOVE THE FOLLOWING SUBROUTINE FROM HERE!!---
;
; Description:
; In:
; Out:
; Modifies:
;
;-------------------------------------------------------
INITOBJECTS: ld a,208 ; always hide the fourth ball
ld hl,SPRATR+12
call WRTVRM
ld hl,INITDATA
ld de,SPRTAB
ld bc,(4*16)+7
ldir
ld a,INITLEVEL
;--- DON'T MOVE THE FOLLOWING SUBROUTINE FROM HERE!!---
;
; Description: Sets level parameters
; In: A = level
; Out:
; Modifies:
;
;------------------------------------------------------
SETLEVEL: bit 2,a ; level 4?
jr z,@@NOADDBALL
ld a,1
ld hl,OBJECTS
ld [hl],4 ; fix a maximum of 4 objects to move/draw
@@NOADDBALL: ld [LEVEL],a
ld b,a
inc a
ld [SPEED],a ; SPEED is always LEVEL+1 (2-4)
ld a,XREACTION
@@SUBX: sub SUBREACTION
djnz @@SUBX
ld [MINX],a
ret
;------------------------------------------------------
;
; Description: Translates a number between 0-19 to ascii
; In: HL = score address
; Out:
; Modifies:
;
;------------------------------------------------------
NUM2TXT: ld de,$3031
ld a,[hl]
sub 10
jr nc,@@GT10
add 10
dec e
@@GT10: add a,d
ld d,a
ld [TXTBUFF],de
ld hl,SND_POINT
call PLAY
ret
;------------------------------------------------------
; Routines to print scores
;------------------------------------------------------
; 1P routine
UPDATESCORE1: ld hl,$0138 ; erase position
ld de,$030F ; text position
jr UPDATESCORE
; 2P routine
UPDATESCORE2: ld hl,$0188 ; erase position
ld de,$0323 ; text position
UPDATESCORE: xor a
ld bc,64
call FILVRM ; erases score panel
inc h
ld bc,64
call FILVRM
inc h
ld bc,64
call FILVRM
inc h
ld bc,64
call FILVRM
ex de,hl
ld de,TXTBUFF
PRNTTEXT: ld a,[de]
or a
ret z
cp 32 ; is a space?
jr z,@@SPACE
cp 13 ; is a newline?
jr nz,@@ISCHAR
ld a,h
add a,6 ; next Y row
ld h,a
ld l,0
jr @@NXTCHR
@@ISCHAR: push hl
push de
call PRNTBIGCHAR
pop de
pop hl
@@SPACE: ld a,l
add a,7 ; next X column
ld l,a
@@NXTCHR: inc de
jr PRNTTEXT
;------------------------------------------------------
;
; Description:
; In: H=Y(0-47),L=X(0-63)
; Out:
; Modifies:
;
;------------------------------------------------------
PRNTBIGCHAR: push hl
ld de,FONTCHR-48*6
ld h,0
ld l,a
push hl
add hl,hl
add hl,hl ; *4
add hl,de
ex de,hl
pop hl
add hl,hl ; *2
add hl,de
ex de,hl ; DE = address of char pattern
pop hl
ld b,6 ; every char has an height of 6 lines
@@NXTLINE: push hl
ld a,[de] ; load char byte pattern
rlca
call c,SETPIX ; if bit is on, draws pixel
inc l ; increases X coordinate
rlca
call c,SETPIX
inc l
rlca
call c,SETPIX
inc l
rlca
call c,SETPIX
inc l
rlca
call c,SETPIX
inc l
rlca
call c,SETPIX
inc l
rlca
call c,SETPIX
inc l
rlca
call c,SETPIX
pop hl
inc de
inc h ; increases Y coordinate
djnz @@NXTLINE
ret