Files
Avery_Breakout/averybreakout.asm
T
2024-12-10 21:34:52 +01:00

1874 lines
41 KiB
NASM

;Acid Breakout - a break from the daily acid
;build 006, 2010-05-12
;CLEAR!
;あめでと
;---------------------------------------------------
.IFNDEF TARGET
.def TARGET = 800 ; 5200
.ENDIF
;---------------------------------------------------
OPT r+ ; saves 10 bytes, and probably works :) https://github.com/tebe6502/Mad-Assembler/issues/10
;---------------------------------------------------
.macro build
dta d"1.00" ; number of this build (4 bytes)
.endm
.macro RMTSong
lda #:1
jsr RMTSongSelect
.endm
;---------------------------------------------------
icl 'lib/ATARISYS.ASM'
icl 'lib/MACRO.ASM'
display = $a000
screenWidth = 80 ;in pixels
screenBytes = screenWidth/2 ; in bytes
maxLines = 55 ; number of lines on the screen (must be odd)
spawnProbability = (256*1/5)
margin = 2 ; top and bottom screen safety margin
racquetPosMin = $2 ; min position of the paddle moved by the user
racquetPosMax = screenWidth-8 ; max position of the paddle moved by the user
racquetSize = 10
maxSpeed = 2 ; maximum speed of a ball. must be power of 2 ('and #' used)
maxBalls = 40 ; maximum number of moving balls, <$80 (bpl used!)
maxMemory = 7 ; number of saved pixel positions
; Beware! For easier calc somewhere it uses "modulo maxMemory"
; calculations and therefore this value must be a power of 2 -1 (?)!
maxBrickLines = 14 ; maximum number of lines of bricks to be eradicated
.zpvar xpos ypos .word = $80 ; position of the ball
.zpvar deXpos deYpos .byte ;position for deletion
.zpvar dX dY .word ;main loop shortcuts to the table values of delta
;xpos, dx - "static point precision" - [dx+1].[dx] (big endian!)
;this static point precision is emulated with .word calcs, just a result is the high byte
.zpvar color .byte ; color of the pixel to plot
.zpvar currBall collisionCheck racquetPos MyClok eXistenZstackPtr .byte
.zpvar xMemAddr yMemAddr .word ; address where to store memories of the current ball
.zpvar temp .word
.zpvar displayposition .word
.zpvar inlevel .word
.zpvar clearCount clearBallNr .byte
.zpvar DLI_A DLI_X dliCount .byte
.zpvar RMT_blocked noSfx SFX_EFFECT .byte
.zpvar AutoPlay .byte ; Auto Play flag ($80 - auto)
RMT_zpvars = AutoPlay+1 ; POZOR!!! RMT vars go here
;---------------------------------------------------
org $2000
MODUL
ins 'art/muzyka_stripped.rmt',+5 ; my RMT 1.28 on WINE is apparently broken. I lost some hair here (5, not 6)
.align $100
icl 'art/rmtplayr.a65'
;---------------------------------------------------
.align $400
font
ins 'art/Mild West.fnt' ; https://damieng.com/typography/zx-origins/mild-west/
dl
.by SKIP3
dta MODE2+LMS,a(statusBuffer)
;.by $80+$50 # fancy shmancy vscroll square pixels
;dta $4f+$20,a(display) ;VSCROLL
;:((maxlines-1)/2) dta a($2f8f)
.by SKIP1+DLII
.rept (maxlines-1), #
:3 dta MODEF+LMS, a(display+screenBytes*:1)
dta MODEF+LMS+DLII, a(display+screenBytes*:1)
.endr
;----
.by MODE2+LMS+SCH ;Hscroll
DLracquetAddr0
.wo racquetDisp
.by JVB
.wo dl
;---------------------------------------------------
dl_level
:8 .by SKIP8
dta 6+LMS,a(LevelText)
dta SKIP8
dta 6,SKIP4,6
.by JVB
.wo dl_level
;---------------------------------------------------
dl_start
:6 .by SKIP3
dta 6+LMS,a(StartText)
dta 6,SKIP4
dta 6,SKIP8,SKIP8
dta 6
.by SKIP1+DLII
.rept 20, #
:3 dta MODEF+LMS, a(display+screenBytes*:1)
dta MODEF+LMS+DLII, a(display+screenBytes*:1)
.endr
;----
.by JVB
.wo dl_start
;---------------------------------------------------
dl_over
:8 .by SKIP8
dta 6+LMS,a(OverText)
.by SKIP8
dta 6
.by JVB
.wo dl_over
;---------------------------------------------------
racquetDisp
:42 .byte $0
.byte $80, $80, $80, $80, $80
:36 .byte $0
;--------------------------------------------------
statusBuffer
dta d" Lives: 5 HS: 000000 Score: 000000 "
score = statusBuffer+33
HiScore = statusBuffer+17
Lives = statusBuffer+8
LevelText
dta d" entering level 000 "
dta d" HIT OUT OF "
dta d" BLOCKS "
OverText
dta d" GAME OVER "
dta d" YOUR SCORE: 000000 "
StartText
dta d" ,"
dta d"GAME by PIRX & PECUS"
dta d" MUSIC by ALEX "
dta d"press start to START"
BlocksToHit=LevelText+26
AllBlocks=LevelText+44
;--------------------------------------------------
icl 'lib/fileio.asm'
;--------------------------------------------------
;--------------------------------------------------
.proc vint
;--------------------------------------------------
;------------JOY-------------
;happy happy joy joy
;check for joystick now
ldy PORTA
tya
and #$04 ;left
bne jNotLeft
ldx racquetPos
cpx #racquetPosMin+1
bcc jNotLeft
dex
stx racquetPos
jNotLeft
tya
and #$08 ;right
bne jNotRight
ldx racquetPos
cpx #racquetPosMax
bcs jNotRight
inx
stx racquetPos
jNotRight
/*
;fire
lda TRIG0
bne JNotFire
...
JNotFire
*/
;lda racquetPos
sec
lda #screenWidth-1
sbc racquetPos
lsr
clc
adc #<racquetDisp
sta dlracquetAddr0
lda #>racquetDisp
adc #0
sta dlracquetAddr0+1
lda racquetPos
lsr
and #$01
;sta HSCROL
/* ;pos print
lda racquetPos
:4 lsr
clc
adc #'0'
sta hexDump
lda racquetPos
and #$0F
clc
adc #'0'
sta hexDump+1
*/
mva #0 dliCount
; mva #13 VSCROL ; FOX gfx mode only
/*
bit RMT_blocked
bmi SkipRMTVBL
; ------- RMT -------
lda sfx_effect
bmi lab2
asl @ ; * 2
tay ;Y = 2,4,..,16 instrument number * 2 (0,2,4,..,126)
ldx #0 ;X = 0 channel (0..3 or 0..7 for stereo module)
lda #0 ;A = 0 note (0..60)
bit noSfx
smi:jsr RASTERMUSICTRACKER+15 ;RMT_SFX start tone (It works only if FEAT_SFX is enabled !!!)
lda #$ff
sta sfx_effect ;reinit value
lab2
jsr RASTERMUSICTRACKER+3 ;1 play
; ------- RMT -------
SkipRMTVBL
*/
;sfx
lda sfx_effect
bmi lab2
asl ; * 2
tay ;Y = 2,4,..,16 instrument number * 2 (0,2,4,..,126)
ldx #3 ;X = 3 channel (0..3 or 0..7 for stereo module)
lda #12 ;A = 12 note (0..60)
jsr RASTERMUSICTRACKER+15 ;RMT_SFX start tone (It works only if FEAT_SFX is enabled !!!)
;
lda #$ff
sta sfx_effect ;reinit value
;
lab2
jsr RASTERMUSICTRACKER+3
skipSoundFrame
jmp XITVBV
.endp
;--------------------------------------------------
.proc DLI
;--------------------------------------------------
/* # fancy shmancy vscroll screen shenanigangs to get the square pixels
sta DLI_A
stx DLI_X
mva #$80 PRIOR
ldx dliCount
sta WSYNC
lda #13
sta VSCROL
lda #3
sta VSCROL
txa
asl
asl
;lda brickcolorTab,x
sta COLBAK
inx
stx dliCount
ldx DLI_X
lda DLI_A
rti
*/
sta DLI_A
mva #$80 PRIOR
lda VCOUNT
asl
asl
sta WSYNC
sta COLBAK
lda DLI_A
rti
.endp
;--------------------------------------------------
main
;--------------------------------------------------
jsr wait_for_depress
jsr MakeDarkScreen
jsr initialize
RMTsong song_main_menu
jsr StartScreen
jsr MakeDarkScreen
mva #$0 AutoPlay
jsr ScoreClear
mva #"5" Lives
jsr clearscreen
mva #$0 LevelType
jsr LoadLevelData.level000 ; set visible number to 000
jsr BuildLevelFromBuffer
jsr LevelScreen
RMTSong song_ingame
gameloop
jsr initialize.ClearTables
jsr MainScreen
jsr PlayLevel
bit EndLevelFlag ; reason for end level
bmi EndOfLife ; end of life :)
; end of level (level up)
jsr MakeDarkScreen
jsr NextLevel
jsr LevelScreen
; RMTSong song_ingame
jsr AudioInit ; after I/O
jmp gameloop
EndOfLife
dec Lives ; decrease Lives
lda Lives
cmp #"0"
beq gameOver ; if no lives - game over
jsr NextLife
jmp gameloop
gameOver
;game over
RMTSong song_game_over
jsr HiScoreCheckWrite
jsr GameOverScreen
@ lda RANDOM
and #%00001110
sta COLPF0
lda CONSOL
and #@consol(start) ; START
beq main
lda TRIG0 ; fire
jeq main
jmp @-
;--------------------------------------------------
.proc StartScreen
;--------------------------------------------------
jsr MakeDarkScreen
mva #$ff AutoPlay
sta LevelType ; Title
mva #"9" Lives
jsr clearscreen
jsr BuildLevelFromBuffer
mwa #dl_start dlptrs
lda #$0 ;+GTIACTLBITS
sta GPRIOR
sta COLBAKS
lda #@dmactl(standard|dma) ; normal screen width, DL on, P/M off
sta dmactls
pause 1
StartLoop
jsr PlayLevel
bit EndLevelFlag ; reason for end level
bmi EndOfStartScreen
; end of level (level up)
jsr NextLevel
jmp StartLoop
EndOfStartScreen
rts
.endp
;--------------------------------------------------
.proc NextLife
;--------------------------------------------------
ldy #maxBalls
sty eXistenZstackPtr
;OK, one ball starts!
lda eXistenZstack,Y
dey
sty eXistenZstackPtr
tax
jsr randomStart ;just one random pixxxel
;previously the whole band of ballz
rts
.endp
;--------------------------------------------------
.proc NextLevel
;--------------------------------------------------
lda LevelType
beq level000
bmi levelTitle
; load level from disk
loadNext
jsr FileUp
jsr LoadLevelData
levelTitle
jsr clearscreen
jsr BuildLevelFromBuffer
jsr initialize.ClearTables
rts ; start level
level000
mva #1 LevelType ; switch to files
; reset file number to 000
ldx #2
@ lda StartLevelNumber,x
sta LevelNumber,x
dex
bpl @-
jmp loadNext
.endp
;--------------------------------------------------
.proc LevelScreen
;--------------------------------------------------
jsr MakeDarkScreen
ldx #2
@ lda LevelNumber,x
sec
sbc #$20
sta LevelText+16,x
dex
bpl @-
mwa #dl_level dlptrs
lda #@dmactl(standard|dma) ; normal screen width, DL on, P/M off
sta dmactls
pause 100
rts
.endp
;--------------------------------------------------
.proc GameOverScreen
;--------------------------------------------------
jsr MakeDarkScreen
ldx #5
@ lda score,x
sta OverText+33,x
dex
bpl @-
mwa #dl_over dlptrs
lda #%00110010 ; normal screen width, DL on, P/M off
sta dmactls
pause 20
rts
.endp
;--------------------------------------------------
.proc MainScreen
;--------------------------------------------------
jsr MakeDarkScreen
mwa #dl dlptrs
lda #$0 ;+GTIACTLBITS
sta GPRIOR
sta COLBAKS
lda #@dmactl(standard|dma) ; normal screen width, DL on, P/M off
sta dmactls
pause 1
rts
.endp
;--------------------------------------------------
.proc MakeDarkScreen
;--------------------------------------------------
mva #0 dmactls ; dark screen
; and wait one frame :)
pause 1
rts
.endp
;--------------------------------------------------
.proc PlayLevel
;--------------------------------------------------
loop
mva #maxBalls-1 currBall
jsr cyclecolors
flight
ldx currBall
lda BalleXistenZ,x
jeq ballDoesNotexist
lda xposTableL,x
sta xpos
lda xposTableH,x
sta xpos+1
lda yposTableL,x
sta ypos
lda yposTableH,x
sta ypos+1
lda dxTableL,x
sta dX
lda dxTableH,x
sta dX+1
lda dyTableL,x
sta dY
lda dYTableH,x
sta dY+1
; now, delete the oldest pixel
;
lda memCycleTable,x
clc
adc #1 ;next position in the table
cmp #maxMemory
bne notMaxMem
lda #0
notMaxMem
sta memCycleTable,x ; memCycleTable saved
tax
lda xposMemTableAdrL,x
sta xMemAddr
lda xposMemTableAdrH,x
sta xMemAddr+1
lda yposMemTableAdrL,x
sta yMemAddr
lda yposMemTableAdrH,x
sta yMemAddr+1
;now on zero page I've got the addressess to store the old xPos and yPos
ldy currBall
lda (yMemAddr),y
tax
lda (xMemAddr),y
sta dexpos
; and erase the last point in the "snake"
;jsr deplot
;--------------------------------------------------
;deplot
; moved here for the speeeeeed
; deyxpos, deypos (.byte) - pixel position
;--------------------------------------------------
; let's calculate coordinates from xpos and ypos
;lda dexpos
lsr
tay
;---
;ldx deypos
lda lineAdrL,x
sta temp
lda lineAdrH,x
sta temp+1
lda dexpos
and #$01
tax
lda (temp),y
and debittable,x
sta (temp),y
;move the ball!!!
adw xpos dX xpos
adw ypos dY ypos
;top bounce
; if ypos<margin then bounce
lda ypos+1
cmp #margin
bcs noTop
; assuming that here a plot can get only from below, so it is enough to switch dy sign
; sbw #$ffff dy dy ;this does not compile :(
negw dY
mva #margin+1 ypos+1
noTop
;bottom bounce
; if ypos>maxLines+margin then bounce
lda ypos+1
cmp #maxLines+margin
bcc noBottom
; check if the ball hits the racquette
bit AutoPlay
bmi GoAuto
lda CONSOL
and #@consol(option) ; OPTION
bne bounceNormally
GoAuto
jmp bottomBounce ; turns off the ball kill
bounceNormally
lda ypos+1
cmp #maxLines+margin*2+maxSpeed*2 ; that makes the ball below the racquet
bcs flyDown2 ;kinda lame optimisation as A carries ypos+1
lda racquetPos
sec
sbc #racquetPosMin+1
bpl racquettePlus
lda #0
racquettePlus
cmp xpos+1
jcs flyDown
clc
adc #racquetSize-1
cmp xpos+1
jcs bottomBounce
flyDown
lda ypos+1
flyDown2
cmp #maxLines+margin*6+maxSpeed*2 ;maximum depth
bcc noBottom
ballOut
lda currBall
eXistenZdEstroy
;destroys ball number A
;pushes one free slot to the eXistenZstack
;ends with !number of balls in X (maxBalls == end of the game)
;txa
ldy eXistenZstackPtr
iny
sta eXistenZstack,y
sty eXistenZstackPtr
;ldx currBall
tax
lda #0
sta balleXistenZ,x
jmp flightLoopEnd
bottomBounce
; assuming that here a plot can get only from below,
; so it is enough to switch dy sign
; sbw #$ffff dy dy ;this does not compile :(
negw dY
mva #maxLines+margin-2 ypos+1
bit LevelType
bmi noPingSFX ; no SFX on title screen
mva #sfx_ping sfx_effect
noPingSFX
noBottom
;left bounce
lda xpos+1
cmp #margin
bcs noLeft
negw dX
mva #margin+1 xpos+1
noLeft
;right border bounce
lda xpos+1
cmp #screenWidth-margin
bcc noRight
negw dX
mva #screenWidth-margin-1 xpos+1
noRight
;jsr plot
; the full plot copied here to get few cycles and collision
; high byte is the integer position
; low byte is the "fractional" part
; let's calculate coordinates from xpos and ypos
lda xpos+1
lsr
tay
;---
ldx ypos+1
lda lineAdrL,x
sta temp
lda lineAdrH,x
sta temp+1
ldx color
lda xpos+1
and #$01
bne pRightNibble
pLeftNibble
lda (temp),y
sta collisionCheck
and #$0F
ora LNColtable,x
sta (temp),y
lda collisionCheck
and #$F0
cmp #%10000000
jne noCollision
jmp plotEnd ;unconditional branch
pRightNibble
lda (temp),y
sta collisionCheck
and #$F0
ora RNColtable,x
sta (temp),y
lda collisionCheck
and #$0F
cmp #%00001000
jne noCollision
plotEnd
lda ypos+1
cmp #maxLines+margin-2-1
jcs noCollision ;ball is outside the screen!
;switch direction, Charles
; an idea for assuming which direction to switch - dX or dY?
; on a diagram below in the middle there is an approached brick
/*
\ /
\-dY /
\ /
-dX [] -dX
/ \
/-dY \
/ \
*/
; it means:
; if |dX|>|dY| then dX == -dX
; else dY == -dY
; get absolute values
lda dX+1
bpl dXpositive
;dX is negative here
lda dY+1
bpl dXneg_dYpos
;dX and dY are negative here
cmp dX+1
bcc dX_gr_dY__dX_dYneg
; |dY| >= |dX| ; hour 5
negw dY
jmp bounceDone
dX_gr_dY__dX_dYneg
; hour 4
negw dX
jmp bounceDone
dXneg_dYpos
; dY in A
clc
adc dX+1
bpl dY_gr_dX__dXneg_dYpos
; |dX| > |dy|; hour 2
negw dX
jmp bounceDone
dY_gr_dX__dXneg_dYpos
; hour 1
negw dY
jmp bounceDone
dXpositive
lda dY+1
bpl dX_dYpositive
;dX positive, dY negative
clc
adc dX+1
bpl dX_gr_dY__dXpos_dYneg
; hour 7
negw dY
jmp bounceDone
dX_gr_dY__dXpos_dYneg
; hour 8
negw dX
jmp bounceDone
dX_dYpositive
;(dY+1)* is in A
cmp dX+1
bcc dX_gr_dY__dX_dYpos
; dY > dX ; hour 11
negw dY
jmp bounceDone
dX_gr_dY__dX_dYpos
; dY < dX ; hour 10
negw dX
bounceDone
; if Auto Play or OPTION key presset - no score
bit AutoPlay
bmi NoScoreUp
lda CONSOL
and #%00000100 ; OPTION
beq NoScoreUp
jsr ScoreUp
NoScoreUp
dew BricksInLevel
lda BricksInLevel
ora BricksInLevel+1
bne NoLevelEnd
; all bricks gone - level ended!
jmp GoNextLevel
NoLevelEnd
;spawn the new bally
; if there is still an empty slot for a new ball somewhere...
;lda RANDOM
;cmp #spawnProbability
;bcs noCollision
lda color
cmp #1
bne noCollision
eXistenZcReate
;creates a new ball
;removes one free slot from the eXistenZstack
;ends with ball number in X
;ends with zero when there is no free slot for a ball
ldy eXistenZstackPtr
beq noMoreSlots
lda eXistenZstack,Y
dey
sty eXistenZstackPtr
tax
;OK, in X there is an empty slot for a ball
;spawn it
lda #1
sta balleXistenZ,x
lda xPos
sta xPosTableL,x
lda xPos+1
sta xPosTableH,x
lda yPos
sta yPosTableL,x
lda yPos+1
sta yPosTableH,x
; random initial speed and direction
lda random
bpl dXplus
lda #-1
sta dxTableH,x
bne dXlower
dXplus
lda #1
sta dxTableH,x
dXlower
lda random
sta dxTableL,x
;randomize 1 maxSpeed-1 ;dy can not be too small or the game would take forever
lda #1
sta dyTableH,x
lda random
sta dyTableL,x
;pong
bit LevelType
bmi noPongSFX ; no SFX on title screen
mva #sfx_pong sfx_effect
noPongSFX
noCollision
noMoreSlots
flightLoopEnd
;end of the cycle for one ball
;save the changes now
ldx currBall
; let's save the position for the future erase
; old position of ball(currBall) is saved here
; in table nr memCycleTable(currBall)
ldy currBall
lda xpos+1 ;high byte is the integer position
sta (xMemAddr),y
lda ypos+1
sta (yMemAddr),y
; saved
lda xpos
sta xposTableL,x
lda xpos+1
sta xposTableH,x
lda ypos
sta yposTableL,x
lda ypos+1
sta yposTableH,x
lda dX
sta dxTableL,x
lda dX+1
sta dxTableH,x
lda dY
sta dyTableL,x
lda dY+1
sta dYTableH,x
endOfBallzLoop
dec currBall
jpl flight
pause 1 ;all balls
bit AutoPlay
bpl NoAuto
pause 1 ;additional pause if auto play mode (slower)
lda CONSOL
and #@consol(start) ; START
beq LevelOver ; Start pressed in Auto Play - exit
lda TRIG0
beq LevelOver
NoAuto
lda eXistenZstackPtr
cmp #maxBalls
jne loop
LevelOver
; level over
mva #$ff EndLevelFlag
jsr wait_for_depress
rts
;-------------------
ballDoesNotexist
;a delay loop for a ball that does not really exist (yet)
ldx #70
delayLoop
dex
bne delayLoop
jmp endOfBallzLoop
GoNextLevel
mva #0 EndLevelFlag ; level ended!
rts
.endp
;--------------------------------------------------
.proc fatplot
; xpos, ypos (.byte) - pixel position
; xpos<80
; pixel color in "color"
;--------------------------------------------------
; let's calculate coordinates from xpos and ypos
lda xpos
lsr
tay
;---
ldx ypos
lda lineAdrL,x
sta temp
lda lineAdrH,x
sta temp+1
ldx color
lda xpos
and #$01
bne fpRightNibble
fpLeftNibble
lda (temp),y
and #$0F
ora LNColtable,x
sta (temp),y
rts
fpRightNibble
lda (temp),y
and #$F0
ora RNColtable,x
sta (temp),y
rts
.endp
;--------------------------------------------------
.proc fatdeplot
; deyxpos, deypos (.byte) - pixel position
;--------------------------------------------------
; let's calculate coordinates from xpos and ypos
lda dexpos
lsr
tay
;---
ldx deypos
lda lineAdrL,x
sta temp
lda lineAdrH,x
sta temp+1
lda dexpos
and #$01
tax
lda (temp),y
and debittable,x
sta (temp),y
rts
.endp
;--------------------------------------------------
.proc clearDeadBall
;--------------------------------------------------
;dead ball in clearBallNr
ldx #maxMemory-1
stx clearCount
clearDeadLoop
ldx clearCount
lda xposMemTableAdrL,x
sta xMemAddr
lda xposMemTableAdrH,x
sta xMemAddr+1
lda yposMemTableAdrL,x
sta yMemAddr
lda yposMemTableAdrH,x
sta yMemAddr+1
;now on zero page I've got the addressess to store the old xPos and yPos
ldy clearBallNr
lda (xMemAddr),y
sta dexpos
lda (yMemAddr),y
sta deypos
jsr fatdeplot
dec clearCount
bpl clearDeadLoop
rts
.endp
;--------------------------------------------------
.proc ScoreUp
;--------------------------------------------------
inc score+5
lda score+5
cmp #"9"+1 ; 9+1 character code
bne ScoreReady
lda #"0" ; 0 character code
sta score+5
inc score+4
lda score+4
cmp #"9"+1 ; 9+1 character code
bne ScoreReady
lda #"0" ; 0 character code
sta score+4
inc score+3
lda score+3
cmp #"9"+1 ; 9+1 character code
bne ScoreReady
lda #"0" ; 0 character code
sta score+3
inc score+2
; bonus !!! :)
lda Lives
cmp #"9"
beq noLivesUP
inc Lives
mva #05 sfx_effect
mva #$ff COLBAKS
pause 2 ; sorry
inc COLBAKS
noLivesUP
;----------
lda score+2
cmp #"9"+1 ; 9+1 character code
bne ScoreReady
lda #"0" ; 0 character code
sta score+2
inc score+1
lda score+1
cmp #"9"+1 ; 9+1 character code
bne ScoreReady
lda #"0" ; 0 character code
sta score+1
inc score
ScoreReady
rts
.endp
;--------------------------------------------------
.proc ScoreClear
;--------------------------------------------------
lda #"0"
ldx #4
@ sta score,x
dex
bpl @-
rts
.endp
;--------------------------------------------------
.proc HiScoreCheckWrite
; It checks if the score is greater than hiscore.
; If yes - rewrites the score to hiscore.
;--------------------------------------------------
lda HiScore
cmp score
bcc higher1
bne lower
lda HiScore+1
cmp score+1
bcc higher2
bne lower
lda HiScore+2
cmp score+2
bcc higher3
bne lower
lda HiScore+3
cmp score+3
bcc higher4
bne lower
lda HiScore+4
cmp score+4
bcc higher5
bne lower
lda HiScore+5
cmp score+5
bcc higher6
lower
rts
higher1
lda score
sta HiScore
higher2
lda score+1
sta HiScore+1
higher3
lda score+2
sta HiScore+2
higher4
lda score+3
sta HiScore+3
higher5
lda score+4
sta HiScore+4
higher6
lda score+5
sta HiScore+5
rts
.endp
;--------------------------------------------------
.proc clearScreen
;--------------------------------------------------
lda #0
tax
@
:(maxLines*40/256+1) sta display+$100*#,x
inx
bne @-
rts
.endp
;--------------------------------------------------
.proc cyclecolorsReset
;--------------------------------------------------
ldy #6
cycleRloop
lda colorCycleTabReset,y
sta colorCycleTab,y
dey
bpl cycleRloop
mva #0 color
.endp
;--------------------------------------------------
.proc cyclecolors
;--------------------------------------------------
inc color
lda color
cmp #8
bne nocolorReset
mva #1 color
nocolorReset
ldy #6
cycleCloop
lda colorCycleTab,y
;sta COLPM1,y
sta PCOLR1,y
dey
bpl cycleCloop
;shift colors
/*
2
2 4
2 4 6
2 4 6 8
2 4 6 8 10
2 4 6 8 10 12
2 4 6 8 10 12 14
4 6 8 10 12 14 2
6 8 10 12 14 2 4
8 10 12 14 2 4 6
10 12 14 2 4 6 8
12 14 2 4 6 8 10
14 2 4 6 8 10 12
2 4 6 8 10 12 14
4 6 8 10 12 14
6 8 10 12 14
8 10 12 14
10 12 14
12 14
14
261 262 263 264 265 266 267
*/
cct = colorCycleTab
ldx cct+6
mva cct+5 cct+6
mva cct+4 cct+5
mva cct+3 cct+4
mva cct+2 cct+3
mva cct+1 cct+2
mva cct+0 cct+1
stx cct+0
rts
.endp
;--------------------------------------------------
colorCycleTab
.by 14,2,4,6,8,10,12
colorCycleTabReset
.by 14,2,4,6,8,10,12
brickcolorTab
.by 0
;--------------------------------------------------
.proc AudioInit
;--------------------------------------------------
; pokeys init
lda #3
sta skctl ; put Pokey into Init
sta skctl+$10
ldx #8
lda #0
@
sta $D200,x ; clear all voices, set AUDCTL to 00
sta $D210,x ; clear all voices, set AUDCTL to 00
dex
bpl @-
rts
.endp
;--------------------------------------------------
.proc initialize
;--------------------------------------------------
mva #>font CHBAS
mva #$00 PCOLR0 ; = $02C0 ;- - rejestr-cień COLPM0
mva #$7C COLBAKS
mva #screenWidth/2-racquetSize/4 racquetPos
mva #0 dliCount
sta RMT_blocked
lda #$ff
sta sfx_effect
JSR AudioInit
;RMT INIT
ldx #<MODUL ;low byte of RMT module to X reg
ldy #>MODUL ;hi byte of RMT module to Y reg
lda #0 ;starting song line 0-255 to A reg
jsr RASTERMUSICTRACKER ;Init
lda #@dmactl(standard|dma)
sta dmactls
mwa #dl dlptrs
vdli DLI
ClearTables
jsr cyclecolorsReset
mwa #clear_vars_start temp
ldy #0
@
tya
sta (temp),y
inw temp
cpw temp #clear_vars_end
bne @-
; prepare mem address tables (for "snake" routine)
;first address initialized
mva #<xposMemTable xposMemTableAdrL
mva #>xposMemTable xposMemTableAdrH
mva #<yposMemTable yposMemTableAdrL
mva #>yposMemTable yposMemTableAdrH
;now add maxBalls to the following addresses
;just take the previous one and add "maxBalls"
ldx #0
initLoop1
clc
lda xposMemTableAdrL,x
adc #<maxBalls ; maxBalls <$80
sta xposMemTableAdrL+1,x
lda xposMemTableAdrH,x
adc #>maxBalls ; maxBalls <$80, so it is == 0
sta xposMemTableAdrH+1,x
clc
lda yposMemTableAdrL,x
adc #<maxBalls ; maxBalls <$80
sta yposMemTableAdrL+1,x
lda yposMemTableAdrH,x
adc #>maxBalls ; maxBalls <$80, so it is == 0
sta yposMemTableAdrH+1,x
inx
cpx #maxMemory-1
bne initLoop1
ldx #maxBalls-1
txa
eXistenZstackFill
sta eXistenZstack+1,x
dex
txa
bne eXistenZstackFill
ldy #maxBalls
sty eXistenZstackPtr
;sty clearPtr
;OK, one ball starts!
;ldy eXistenZstackPtr
lda eXistenZstack,Y
dey
sty eXistenZstackPtr
tax
jsr randomStart ;just one random pixxxel
;previously the whole band of ballz
;VBI
vmain vint,7
mva #1 color
rts
.endp
;--------------------------------------------------
.proc drawBricks
;--------------------------------------------------
; solid maxBrickLines field
; for x=margin to screenWidth-margin:
; for y=margin to maxBrickLines+margin:
; fatplot(x,y)
mva #8 color
mva #margin*2 ypos
drawBricksLoopY
mva #margin*3 xpos
drawBricksLoop
jsr fatplot
inc xpos
lda xpos
cmp #screenWidth-margin*3
bne drawBricksLoop
inc ypos
lda ypos
cmp #maxBrickLines+margin*2
bne drawBricksLoopY
; set number of bricks in this level
mwa #952 BricksInLevel
rts
.endp
;--------------------------------------------------
.proc randomStart
; X - ball number
;--------------------------------------------------
lda #1
sta balleXistenZ,x
;randomize 10 70
lda racquetPos
adc #2 ; do not care about curry, just move the baby to the right
sta xposTableH,x
;randomize margin*2+maxBrickLines maxLines-margin*4
lda #54
sta yposTableH,x
; random initial speed and direction
;randomize 0 maxSpeed-1
lda random
and #%1
beq xneg
lda #1 ;easy start
bne @+ ; jmp
xneg
lda #-1
@
sta dxTableH,x
lda random
sta dxTableL,x
;randomize 1 maxSpeed-1 ;dy can not be too small or the game would take forever
lda #-1 ;easy start
sta dyTableH,x
lda #1
sta dyTableL,x
rts
.endp
;--------------------------------------------------
.proc FileUp
;--------------------------------------------------
inc LevelNumber+2
lda LevelNumber+2
cmp #'9'+1 ; 9+1 character code
bne NumberReady
lda #'0' ; 0 character code
sta LevelNumber+2
inc LevelNumber+1
lda LevelNumber+1
cmp #'9'+1 ; 9+1 character code
bne NumberReady
lda #'0' ; 0 character code
sta LevelNumber+1
inc LevelNumber
NumberReady
rts
.endp
;--------------------------------------------------
.proc LoadLevelData
;--------------------------------------------------
lda LevelType
beq level000
bmi levelTitle
; load level from disk
; prepare number in filename
ldx #2
@ lda LevelNumber,x
sta fname+7,x
dex
bpl @-
; clear buffer
mwa #LevelFileBuff temp
ldy #0
@ tya
sta (temp),y
inw temp
cpw temp #LevelFileBuffEnd
bne @-
; try to load file
jsr close
jsr open
bmi open_error
jsr bget
bmi bget_error
go_close jsr close
rts
bget_error
cpy #136 ; EOF
beq go_close
open_error
mva #0 LevelType ; set level to internal 000
level000
; reset file number to 000
ldx #2
@ lda StartLevelNumber,x
sta LevelNumber,x
dex
bpl @-
levelTitle
rts
.endp
;--------------------------------------------------
.proc BuildLevelFromBuffer
;--------------------------------------------------
lda LevelType
beq level000
bmi levelTitle
mwa #LevelFileBuff inlevel
jmp PrepareLevel
levelTitle
mwa #Menu_data inlevel
jmp PrepareLevel
level000
mwa #Level000_data inlevel
PrepareLevel
ldy #0
sty BricksInLevel
sty BricksInLevel+1
nextnumber
lda (inlevel),y
inw inlevel
cmp #CR_PC ; skip PC CR
beq nextnumber
cmp #EOL ; Atari LF
beq nextnumber2
cmp #LF_PC ; PC LF
beq nextnumber2
; check valid characters
ldx #9
@ cmp Numbers,x
beq valid1
dex
bpl @-
jmp LevelDataError
valid1 ; value in X register
; now we must multiply BricksInLevel by 10
asl BricksInLevel
rol BricksInLevel+1
mwa BricksInLevel temp
asl BricksInLevel
rol BricksInLevel+1
asl BricksInLevel
rol BricksInLevel+1
adw temp BricksInLevel BricksInLevel
; and add value
clc
txa
adc BricksInLevel
sta BricksInLevel
bcc @+
inc BricksInLevel+1
@ jmp nextnumber
nextnumber2
sty BigBrickFlag ; #0
lda (inlevel),y
inw inlevel
cmp #'1'
beq singlepixel
cmp #'2'
jne LevelDataError
doublepixel
dec BigBrickFlag ; #$ff
singlepixel
lda (inlevel),y
inw inlevel
cmp #CR_PC ; skip PC CR
beq singlepixel
cmp #EOL ; Atari LF
beq makeBricks
cmp #LF_PC ; PC LF
bne singlepixel
; make bricks
makeBricks
mwa #0 temp2
mva #margin*2 ypos
drawBricksLoopY
mva #0 xpos
drawBricksLoop
; get data
ldy #0
lda (inlevel),y
beq LevelDataEnd ; if end of data
inw inlevel
cmp #CR_PC ; skip PC CR
beq drawBricksLoop
cmp #EOL ; Atari LF
beq EndOfLine
cmp #LF_PC ; PC LF
beq EndOfLine ; next line
cmp #' '
beq NoBrick ; if no brick
ldy #8
inw temp2 ; real number of bricks
bit BigBrickFlag
bpl OnePixel
inw temp2 ; real number of bricks
OnePixel
NoBrick
sty color
jsr fatplot
inc xpos
bit BigBrickFlag
bpl SmallBrick
jsr fatplot ; second bixel of big brick
inc xpos
SmallBrick
lda xpos
cmp #screenWidth
bne drawBricksLoop
; if screenwidth is reached we skip all buffer characters up to EOL.
jsr skipToEOL
EndOfLine
inc ypos
lda ypos
cmp #maxlines
bne drawBricksLoopY
LevelDataEnd
cpw BricksInLevel temp2
bcc BricksOK ; if defined bricks number is bigger tan real
mwa temp2 BricksInLevel ; set to real brick number
BricksOK
mwa #AllBlocks displayposition
mwa temp2 decimal
jsr displaydec5
mwa #BlocksToHit displayposition
mwa BricksInLevel decimal
jsr displaydec5
jsr cyclecolorsReset
rts
LevelDataError
; errer in data - set level to o (internal) and draw level
mva #0 LevelType
jmp level000
skipToEOL
ldy #0
lda (inlevel),y
beq skipped ; if end of data
inw inlevel
cmp #EOL ; Atari LF
beq skipped
cmp #LF_PC ; PC LF
bne skipToEOL ; next data character
skipped
rts
.endp
;--------------------------------------------------
.proc displaydec5 ;decimal (word), displayposition (word)
;--------------------------------------------------
; displays decimal number as in parameters (in text mode)
; leading zeroes are removed
; the range is (00000..65565 - two bytes)
ldy #4 ; there will be 5 digits
NextDigit
ldx #16 ; 16-bit dividee so Rotate 16 times
lda #$00
Rotate000
aslw decimal
rol ; scroll dividee
; (as highest byte - additional - byte is A)
cmp #10 ; divider
bcc TooLittle000 ; if A is smaller than divider
; there is nothing to substract
sbc #10 ; divider
inc decimal ; lowest bit set to 1
; because it is 0 and this is the fastest way
TooLittle000 dex
bne Rotate000 ; and Rotate 16 times, Result will be in decimal
tax ; and the rest in A
; (and it goes to X because
; it is our decimal digit)
lda digits,x
sta decimalresult,y
dey
bpl NextDigit ; Result again /10 and we have next digit
;rightnumber
; displaying without leading zeroes (if zeroes exist then display space at this position)
ldy #0
ldx #0 ; digit flag (cut leading zeroes)
displayloop
lda decimalresult,y
cpx #0
bne noleading0
cpy #4
beq noleading0 ; if 00000 - last 0 must stay
cmp zero
bne noleading0
lda #space
beq displaychar ; space = 0 !
noleading0
inx ; set flag (no leading zeroes to cut)
displaychar
sta (displayposition),y
nexdigit
iny
cpy #5
bne displayloop
rts
.endp
;--------------------------------------------------
.proc RmtSongSelect
; starting song line 0-255 to A reg
;--------------------------------------------------
/*
cmp #song_main_menu
beq noingame ; noMusic blocks only ingame songs
bit noMusic
spl:lda #song_silencio
noingame
*/
mvx #$ff RMT_blocked
ldx #<MODUL ; low byte of RMT module to X reg
ldy #>MODUL ; hi byte of RMT module to Y reg
jsr RASTERMUSICTRACKER ; Init
mva #0 RMT_blocked
rts
.endp
;--------------------------------------------------
.proc wait_for_depress ; ion
;--------------------------------------------------
lda CONSOL
and:cmp #%00000111
bne wait_for_depress
lda TRIG0
beq wait_for_depress
rts
.endp
;--------------------------------------------------
Menu_data
.byte '200',EOL ; number of bricks in ATASCII before success
.byte '1',EOL ; brick size in pixels
; 0 1 2 3 4 5 6 7
; 01234567890123456789012345678901234567890123456789012345678901234567890123456789
.byte EOL
.byte ' #### ## ## ####### ###### ## ##',EOL
.byte ' ###### ## ## ## ## ## ## ##',EOL
.byte ' ## ## ## ## ##### ###### ####',EOL
.byte ' ######## #### ## ## ## ##',EOL
.byte ' ## ## ## ####### ## ## ##',EOL
.byte EOL
.byte ' ##### ###### ####### #### ## ## ###### ## ## ########',EOL
.byte ' ## ## ## ## ## ###### ## ## ## ## ## ## ##',EOL
.byte ' ##### ###### ##### ## ## #### ## ## ## ## ##',EOL
.byte ' ## ## ## ## ## ######## ## ## ## ## ## ## ##',EOL
.byte ' ###### ## ## ####### ## ## ## ## ###### ###### ##',EOL
.byte EOL
.byte 0
Level000_data
.byte '486',EOL ; '952',EOL ; number of bricks (pixes) in ATASCII to be taken out before success
.byte '2',EOL ; brick size in pixels
; 0 1 2 3
; 0123456789012345678901234567890123456789
.byte EOL,EOL,EOL
:14 .byte ' ##################################',EOL
.byte 0
LevelFileBuff
LevelFileBuffLen=(screenWidth*maxLines)+20
.ds LevelFileBuffLen ; Buffer for data from the level file
LevelFileBuffEnd
LevelNumber
.byte '000'
StartLevelNumber
.byte '000'
fname
.byte 'D:LEVEL000.DAT',EOL
;--------------------------------------------------
EndLevelFlag
.byte 0 ; $ff - level over, $00 - level ended
BigBrickFlag
.byte 0
BricksInLevel
.word 0
temp2
.word 0
LevelType
.byte 0 ; level type $00 - first level, $01 - level from buffer, $ff - title screen
Numbers
.byte '0123456789'
digits
zero
.byte "0123456789"
space = 0
.byte " "
decimal
.word 0
decimalresult
.byte " "
lineAdrL
:margin .byte <marginLine ;8 lines of margin space
:maxLines .byte <(display+screenBytes*#)
:256-maxLines-1*margin .by <marginLine ; (display+40*#) ;just to let the plot smear on full .byte ypos
lineAdrH
:margin .byte >marginLine
:maxLines .byte >(display+screenBytes*#)
:256-maxLines-1*margin .by >marginLine ; (display+40*#) ;just to let the plot smear on full .byte ypos
bittable
.byte %11110000
debittable
.byte %00001111
.byte %11110000
RNColtable ; Right Nibble color Table
.byte %00000000
.byte %00000001
.byte %00000010
.byte %00000011
.byte %00000100
.byte %00000101
.byte %00000110
.byte %00000111
.byte %00001000
LNColtable ; Left Nibble color Table
.byte %00000000
.byte %00010000
.byte %00100000
.byte %00110000
.byte %01000000
.byte %01010000
.byte %01100000
.byte %01110000
.byte %10000000
;--------------------------------
clear_vars_start
dxTableL :maxBalls .byte 0
dxTableH :maxBalls .byte 0
dyTableL :maxBalls .byte 0
dyTableH :maxBalls .byte 0
; xpos and ydaw are "decimal" parts of static point precision .word
xposTableL :maxBalls .byte 0 ; "fractional" part
xposTableH :maxBalls .byte 0 ; "fractional" part
yposTableL :maxBalls .byte 0 ;
yposTableH :maxBalls .byte 0 ;
; ball position memory tables - the ball trace works like a "snake"
; (one set, one erased)
; there are "maxMemory" number of tables, "maxballs" length each
; too bad their addressess are not known in advance,
; so a short subrourine must calculate them and place to XposMemTableAdrL, etc.
balleXistenZ :maxBalls .byte 0 ; 0-dead, 1-alive!
balleXistenZcatch
.byte 0 ; catch last ball byte
eXistenZstack ; goes from index [1..maxBalls]. maxBalls[0] is unused
; keeps the list of free slots for balls
; goes from down to top. ptr==0 means stack is empty (all balls playing)
:maxBalls+1 .byte 0
xposMemTable
:maxBalls*maxMemory .byte 0
yposMemTable
:maxBalls*maxMemory .byte 0
;addressess of the tables with snake pixels
xposMemTableAdrL
:maxMemory .byte 0
xposMemTableAdrH
:maxMemory .byte 0
yposMemTableAdrL
:maxMemory .byte 0
yposMemTableAdrH
:maxMemory .byte 0
;table for keeping the count on the last position to be deleted from the "snake"
memCycleTable
:maxBalls .byte 0
clear_vars_end
;--------------------------------
statusBar
dta d"rc$"
hexDump
dta d" dx$"
dxDisp
dta d" dy$"
dyDisp
dta d" balls$"
ballDisp
dta d" "
marginLine .ds screenBytes
;--------------------------------
; names of RMT instruments (sfx)
;--------------------------------
sfx_ping = $07
sfx_pong = $08
;--------------------------------
; RMT songs (lines)
;--------------------------------
song_main_menu = $00
song_ingame = $07
song_game_over = $12
RUN main