Breakout

TUTORIAL · BREAKOUT · STAGE 8 OF 8BETA

Stage 8 · Sound

Sound effects from the EAudio ROM, one request per frame at most. This stage is the finished game: STAGE8 on the disc is called GAME.

BRICKFALL in play: HUD, brick field, ball and bat
The finished game.

Type these 11 lines into the previous stage. Lines 20, 50, 185, 320, 325, 330, 340, 350, 392 and 394 replace lines you already have: type them again in full. Or load the whole stage from the disc with CHAIN "STAGE8".

Text after :REM and indented continuation lines are Beeb-dev notes and layout: the build strips them, so they cost the BBC nothing. Typing into a real BBC, leave the notes out and join each continuation line onto the line above.

20 PROCeginit                                    :REM start EGFX: checks TOP<=&2200 and HIMEM=&2400,
    REM fills the 64-byte request block and makes sure EGFX is fitted
    :PROCegcompanion(7,&81,255)                  :REM register the 16x16 sprite pack at &8100 in
    REM slot 7 for the brick batches (255: no sideways RAM history bank)
    :DIM E% 15                                   :REM 16 bytes for EAudio's OSWORD &C0 block
    REM (E% is a resident integer: one letter, cheap to tokenise)
    :!E%=&1040                                   :REM bytes 0 and 1 in one store: 64 out, 16 back
    :$(E%+2)="EAUD"                              :REM bytes 2-5: the signature EAudio answers to
    :E%?6=0:PROCe                                :REM command 0: INIT (overwrites the string's CR)
    :E%?6=7:E%?8=3:PROCe                         :REM command 7: VOLUME 3. A freshly switched-on
    REM machine holds 0 here, which is mute, and EAudio still says yes
50 p%=152                                        :REM bat X, left edge: always a multiple of 4
    :b%=160:y%=192                               :REM ball top-left, sitting on the bat
    :sv%=1                                       :REM 1 = ball waits on the bat for SPACE
    :d%=4                                        :REM 3 or more = full redraw of both pages next frame
    :u%=b%:v%=y%:w%=p%                           :REM ball and bat last frame
    :aa%=b%:bb%=y%:cc%=p%                        :REM and the frame before: what the hidden page shows
    :S%=0:G%=0                                   :REM no sound queued, no GAME OVER banner. Resident
    REM integers survive RUN and CHAIN, so clear them on every start
185 aa%=u%:bb%=v%:cc%=w%                         :REM last frame's positions move back one: the
    REM page drawn next frame last showed them
    :u%=b%:v%=y%:w%=p%                           :REM and this frame's become last frame's
    :IF S% E%?6=3:E%?8=S%-1                      :REM a sound was asked for: command 3, effect S%-1
    :PROCe:S%=0                                  :REM send it, so never more than one request a frame
320 IF nx%<=8 nx%=8:e%=-e%                       :REM left wall: clamp and bounce
    :S%=9                                        :REM effect 8, tick
325 IF nx%>=288 nx%=288:e%=-e%                   :REM right wall
    :S%=9
330 IF ny%<24 ny%=24:f%=-f%                      :REM ceiling, just below the HUD
    :S%=9
340 IF ny%>240 sv%=1:f%=-4                       :REM off the bottom: missed. Serve again...
    :nx%=p%+8:ny%=192                            :REM ...from the bat
    :lv%=lv%-1:hd%=2                             :REM lose a life, HUD on both pages
    :S%=3                                        :REM effect 2
    :IF lv%=0 S%=1:G%=2:sv%=2                    :REM last life: effect 0, GAME OVER banner,
    REM and sv%=2 hides the ball until SPACE restarts (line 127)
350 IF ny%<=205 AND ny%+7>=202                   :REM overlapping the bat's rows 202-205...
     AND f%>0                                    :REM ...moving down...
     AND nx%+3>=p% AND nx%+3<p%+16               :REM ...and the ball's centre is over the bat
     ny%=194:f%=-f%                              :REM sit it on top and bounce
    :e%=e%+SGN(nx%+3-p%-8)                       :REM steer: one pixel a frame towards the side
    REM of the bat it struck
    :S%=5                                        :REM effect 4, bell
392 IF a%(rr%,ct%)>1 a%(rr%,ct%)=a%(rr%,ct%)-1   :REM a strong brick: one hit
    :S%=9                                        :REM off, tick...
    :ENDPROC                                     :REM ...and it stays
394 a%(rr%,ct%)=0:n%=n%-1                        :REM broken: one fewer brick
    :sc%=sc%+10                                  :REM ten points
    :hd%=4                                       :REM HUD in two frames' time (line 170)
    :S%=7                                        :REM effect 6, crunch
    :IF n%=0 S%=4                                :REM that was the last brick: effect 3, fanfare...
    :PROCnextlevel                               :REM ...and the next level
520 DEF PROCe                                    :REM send the EAudio block at E%
    :A%=&C0                                      :REM OSWORD &C0, the call EGFX and EAudio both answer
    :X%=E%:Y%=E%DIV256                           :REM block address: low byte in X, high in Y
    :CALL&FFF1                                   :REM OSWORD. EAudio claims it by the EAUD signature
    :ENDPROC

The EAudio block

20 PROCeginit                                    :REM start EGFX: checks TOP<=&2200 and HIMEM=&2400,
    REM fills the 64-byte request block and makes sure EGFX is fitted
    :PROCegcompanion(7,&81,255)                  :REM register the 16x16 sprite pack at &8100 in
    REM slot 7 for the brick batches (255: no sideways RAM history bank)
    :DIM E% 15                                   :REM 16 bytes for EAudio's OSWORD &C0 block
    REM (E% is a resident integer: one letter, cheap to tokenise)
    :!E%=&1040                                   :REM bytes 0 and 1 in one store: 64 out, 16 back
    :$(E%+2)="EAUD"                              :REM bytes 2-5: the signature EAudio answers to
    :E%?6=0:PROCe                                :REM command 0: INIT (overwrites the string's CR)
    :E%?6=7:E%?8=3:PROCe                         :REM command 7: VOLUME 3. A freshly switched-on
    REM machine holds 0 here, which is mute, and EAudio still says yes
520 DEF PROCe                                    :REM send the EAudio block at E%
    :A%=&C0                                      :REM OSWORD &C0, the call EGFX and EAudio both answer
    :X%=E%:Y%=E%DIV256                           :REM block address: low byte in X, high in Y
    :CALL&FFF1                                   :REM OSWORD. EAudio claims it by the EAUD signature
    :ENDPROC

EAudio answers the same OSWORD &C0 as EGFX, with its own signature. DIM E% 15 makes a 16-byte block. !E%=&1040 writes the lengths in one go (64 bytes out, 16 back), $(E%+2)="EAUD" stamps the signature, and two calls follow: command 0 (INIT) and command 7 (VOLUME) with level 3. Do not skip the volume: EAudio 0.3 keeps it in host memory, a freshly switched-on machine has 0 there, and 0 means mute (the ROM still says yes to every effect, so nothing looks wrong).

Asking for a sound

Collisions never call EAudio directly. They set S% to the effect number plus one, and line 185 sends at most one request a frame (command 3, effect S%-1), so two bounces in one frame cannot queue up two calls.

EventLineEffect
Wall, ceiling, dented brick320–330, 3928 tick
Bat3504 bell
Brick broken3946 crunch
Ball lost3402
Game over3400
Level cleared3943 fanfare

EAudio has 50 effects (0–49) and three-voice music as well: the command reference lists them. Mount EAUDIO.rom in slot 5. In B-em, make sure sound is switched on.

The whole of stage 8, ready to type in: the EGFX helpers merged and the notes stripped (93 BASIC lines)
5 MODE 1:HIMEM=&2400
20 PROCeginit:PROCegcompanion(7,&81,255):DIM E% 15:!E%=&1040:$(E%+2)="EAUD":E%?6=0:PROCe:E%?6=7:E%?8=3:PROCe
30 DIM a%(5,7):l%=1:e%=4:f%=-4:sc%=0:lv%=3:hd%=0:PROCresetlevel
40 PROCstartscreen
50 p%=152:b%=160:y%=192:sv%=1:d%=4:u%=b%:v%=y%:w%=p%:aa%=b%:bb%=y%:cc%=p%:S%=0:G%=0
60 REPEAT:IF d%>=3 PROCegbegin:PROCdrawbricks:PROCdrawhud:PROCbatdraw(p%):PROCegpresent:PROCegbegin:PROCdrawbricks:PROCdrawhud:PROCbatdraw(p%):PROCegpresent:d%=0:aa%=p%:u%=p%:bb%=192:v%=192
65 PROCegdirtybegin
75 z%=INKEY(-98):x%=INKEY(-67):sp%=INKEY(-99)
80 IF z% p%=p%-12
90 IF x% p%=p%+12
100 IF p%<8 p%=8
110 IF p%>288 p%=288
120 IF sv%=1 b%=p%+8:y%=192:IF sp% sv%=0
127 IF lv%=0 AND sp% G%=0:l%=0:sc%=0:lv%=3:PROCnextlevel:e%=4:f%=-4
130 IF sv%=0 PROCmoveball
150 IF aa%<>b% OR bb%<>y% PROCegcompact(aa%,bb%,1,0,7,&87)
155 IF cc%<>p% PROCbatclear(cc%)
160 IF d%>0 AND d%<3 PROCbe
161 IF d%>0 AND d%<3 d%=d%-1
170 IF hd% hd%=hd%-1:IF hd%<2 PROCdrawhud
175 IF G% PROCegtext("GAME OVER",120,140):PROCegtext("PRESS SPACE",112,156):G%=G%-1
180 IF cc%<>p% OR (bb%<=205 AND bb%+7>=202 AND aa%<=p%+15 AND aa%+7>=p%) PROCbatdraw(p%)
181 IF sv%<>2 PROCegcompact(b%,y%,0,0,7,&87)
185 aa%=u%:bb%=v%:cc%=w%:u%=b%:v%=y%:w%=p%:IF S% E%?6=3:E%?8=S%-1:PROCe:S%=0
190 PROCegpresent
200 UNTIL INKEY(-17)
210 MODE 7:END
220 DEF PROCstartscreen
230 PROCegbegin:FOR y%=24 TO 88 STEP 8:PROCegline(40,y%,280,y%,(y% DIV 8) MOD 4):NEXT:PROCegtext("BRICKFALL",120,48):PROCegline(64,80,256,80,3)
240 PROCegtext("PRESS SPACE",112,176):PROCegpresent
250 REPEAT:UNTIL INKEY(0)=32:*FX 15
260 ENDPROC
300 DEF PROCmoveball
310 nx%=b%+e%:ny%=y%+f%
320 IF nx%<=8 nx%=8:e%=-e%:S%=9
325 IF nx%>=288 nx%=288:e%=-e%:S%=9
330 IF ny%<24 ny%=24:f%=-f%:S%=9
340 IF ny%>240 sv%=1:f%=-4:nx%=p%+8:ny%=192:lv%=lv%-1:hd%=2:S%=3:IF lv%=0 S%=1:G%=2:sv%=2
350 IF ny%<=205 AND ny%+7>=202 AND f%>0 AND nx%+3>=p% AND nx%+3<p%+16 ny%=194:f%=-f%:e%=e%+SGN(nx%+3-p%-8):S%=5
355 IF ny%>107 OR ny%+7<40 OR nx%+7<64 OR nx%>247 GOTO 380
360 r0%=(ny%-40)DIV12:IF r0%<0 r0%=0
362 r1%=(ny%+7-40)DIV12:IF r1%>5 r1%=5
364 c0%=(nx%-64)DIV24:IF c0%<0 c0%=0
366 c1%=(nx%+7-64)DIV24:IF c1%>7 c1%=7
368 h%=0:FOR rr%=r0% TO r1%:FOR ct%=c0% TO c1%
370 IF h%=0 AND a%(rr%,ct%)>0 PROCbrickhit
372 NEXT:NEXT
380 b%=nx%:y%=ny%:ENDPROC
382 DEF PROCbrickhit
384 IF nx%>79+ct%*24 OR nx%+7<64+ct%*24 OR ny%>47+rr%*12 OR ny%+7<40+rr%*12 ENDPROC
385 IF d%=1 PROCbe
386 h%=1:hr%=rr%:hc%=ct%:d%=2
388 IF y%+7<40+rr%*12 OR y%>47+rr%*12 f%=-f% ELSE e%=-e%
390 nx%=b%+e%:ny%=y%+f%
392 IF a%(rr%,ct%)>1 a%(rr%,ct%)=a%(rr%,ct%)-1:S%=9:ENDPROC
394 a%(rr%,ct%)=0:n%=n%-1:sc%=sc%+10:hd%=4:S%=7:IF n%=0 S%=4:PROCnextlevel
396 ENDPROC
400 DEF PROCresetlevel:n%=0:FOR r%=0 TO 5:FOR c%=0 TO 7
402 s%=1:IF l%>1 s%=1+((r%+c%+l%)MOD 3)
404 a%(r%,c%)=s%:n%=n%+1:NEXT:NEXT:ENDPROC
410 DEF PROCnextlevel:l%=l%+1:e%=4+l%:f%=-e%:PROCresetlevel:sv%=1:d%=4:hd%=2:ENDPROC
420 DEF PROCdrawbricks:FOR r%=0 TO 5:egb%?8=8:FOR c%=0 TO 7:egb%?(16+c%*5)=2+r%:egb%?(17+c%*5)=(64+c%*24) AND 255:egb%?(18+c%*5)=0:egb%?(19+c%*5)=40+r%*12:egb%?(20+c%*5)=0:NEXT:PROCegcall(34):NEXT:ENDPROC
440 DEF PROCdrawhud:PROCegtext("SCORE "+RIGHT$("00000"+STR$(sc%),5)+"  LEVEL "+RIGHT$("00"+STR$(l%),2)+"  LIVES "+STR$(lv%),8,8):ENDPROC
500 DEF PROCbatclear(x%):bt%=&6E82+2*x%:FOR q%=0 TO 3:!bt%=0:bt%=bt%+8:NEXT:ENDPROC
510 DEF PROCbatdraw(x%):bt%=&6E82+2*x%:FOR q%=0 TO 3:!bt%=&FF0F0FFF:bt%=bt%+8:NEXT:ENDPROC
520 DEF PROCe:A%=&C0:X%=E%:Y%=E%DIV256:CALL&FFF1:ENDPROC
530 DEF PROCbe:PROCegcompact(64+hc%*24,40+hr%*12,9+SGN(a%(hr%,hc%))*(hr%-7),0,7,&81):ENDPROC
1000 DEF PROCeginit
1010 IF HIMEM>&2400 OR TOP>&2200 PRINT "EGFX MEM":END
1020 DIM egb% 63:LOCAL egi%
1025 FOR egi%=0 TO 63:egb%?egi%=0:NEXT
1040 egb%?0=64:egb%?1=8:egb%?2=69:egb%?3=71:egb%?4=1
1045 PROCegcall(0):ENDPROC
1050 DEF PROCegcall(egf%)
1070 egb%?5=egf%:egb%?7=0
1080 A%=&C0:X%=egb% MOD 256:Y%=egb% DIV 256:CALL &FFF1
1090 IF egb%?7<>&A5 PRINT "NO EGFX ROM":END
1095 IF egf%>=11 AND egf%<>22 AND egf%<>23 AND egb%?6<>0 PRINT "EG ERROR":END
1100 ENDPROC
1110 DEF PROCegxy(egx%,egy%)
1120 egb%?8=egx%:egb%?9=egx% DIV 256
1130 egb%?10=egy%:egb%?11=egy% DIV 256:ENDPROC
1140 DEF PROCegbegin:PROCegcall(1):ENDPROC
1144 DEF PROCegdirtybegin:egb%?8=255:PROCegcall(38):ENDPROC
1150 DEF PROCegpresent:PROCegcall(6):ENDPROC
1180 DEF PROCegtext(egs$,egx%,egy%)
1190 PROCegxy(egx%,egy%):$(egb%+22)=egs$+CHR$0:PROCegcall(7):ENDPROC
1260 DEF PROCegline(egx%,egy%,egex%,egey%,egc%)
1270 PROCegxy(egx%,egy%):egb%?13=egc%:egb%?16=egex% AND 255:egb%?17=(egex% AND &FFFF) DIV 256
1280 egb%?20=egey% AND 255:egb%?21=(egey% AND &FFFF) DIV 256:PROCegcall(10):ENDPROC
1820 DEF PROCegcompanion(b%,p%,h%):egb%?8=b%:egb%?9=p%:egb%?10=h%:PROCegcall(33):ENDPROC
2110 DEF PROCegcompact(x%,y%,i%,f%,b%,p%)
2120 PROCegxy(x%,y%):egb%?12=i%:egb%?14=f%:egb%?16=b%:egb%?17=p%:PROCegcall(37):ENDPROC

Try it: a different effect per brick row, or a short fanfare at game over.

Where next

The under the hood pages explain the sprite ROM, the two-page renderer, the bat blit and how the frame time was measured. The EGFX assembly guide is the next step if you outgrow BASIC.

← Stage 7: Lives, game over and restart Under the hood →